diff options
Diffstat (limited to 'kdecore/kstringhandler.cpp')
-rw-r--r-- | kdecore/kstringhandler.cpp | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/kdecore/kstringhandler.cpp b/kdecore/kstringhandler.cpp new file mode 100644 index 000000000..185a0316a --- /dev/null +++ b/kdecore/kstringhandler.cpp @@ -0,0 +1,663 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Ian Zepp (icszepp@islc.net) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kstringhandler.h" +#include "kglobal.h" + +static void parsePythonRange( const QCString &range, uint &start, uint &end ) +{ + const int colon = range.find( ':' ); + if ( colon == -1 ) { + start = range.toUInt(); + end = start; + } else if ( colon == int( range.length() - 1 ) ) { + start = range.left( colon ).toUInt(); + } else if ( colon == 0 ) { + end = range.mid( 1 ).toUInt(); + } else { + start = range.left( colon ).toInt(); + end = range.mid( colon + 1 ).toInt(); + } +} + +QString KStringHandler::word( const QString &text , uint pos ) +{ + return text.section( ' ', pos, pos ); +} + +QString KStringHandler::word( const QString &text , const char *range ) +{ + // Format in: START:END + // Note index starts a 0 (zero) + // + // 0: first word to end + // 1:3 second to fourth words + QStringList list = QStringList::split( " ", text , true ); + QString tmp = ""; + QString r = range; + + if ( text.isEmpty() ) + return tmp; + + uint pos = 0, cnt = list.count(); + parsePythonRange( range, pos, cnt ); + + // + // Extract words + // + int wordsToExtract = cnt-pos+1; + QStringList::Iterator it = list.at( pos); + + while ( (it != list.end()) && (wordsToExtract-- > 0)) + { + tmp += *it; + tmp += " "; + it++; + } + + return tmp.stripWhiteSpace(); +} + +// +// Insertion and removal routines +// +QString KStringHandler::insword( const QString &text , const QString &word , uint pos ) +{ + if ( text.isEmpty() ) + return word; + + if ( word.isEmpty() ) + return text; + + // Split words and add into list + QStringList list = QStringList::split( " ", text, true ); + + if ( pos >= list.count() ) + list.append( word ); + else + list.insert( list.at(pos) , word ); + + // Rejoin + return list.join( " " ); +} + +QString KStringHandler::setword( const QString &text , const QString &word , uint pos ) +{ + if ( text.isEmpty() ) + return word; + + if ( word.isEmpty() ) + return text; + + // Split words and add into list + QStringList list = QStringList::split( " ", text, true ); + + if ( pos >= list.count() ) + list.append( word ); + else + { + list.insert( list.remove( list.at(pos) ) , word ); + } + + // Rejoin + return list.join( " " ); +} + +QString KStringHandler::remrange( const QString &text , const char *range ) +{ + // Format in: START:END + // Note index starts a 0 (zero) + // + // 0: first word to end + // 1:3 second to fourth words + QStringList list = QStringList::split( " ", text , true ); + QString tmp = ""; + QString r = range; + + if ( text.isEmpty() ) + return tmp; + + uint pos = 0, cnt = list.count(); + parsePythonRange( range, pos, cnt ); + + // + // Remove that range of words + // + int wordsToDelete = cnt-pos+1; + QStringList::Iterator it = list.at( pos); + + while ( (it != list.end()) && (wordsToDelete-- > 0)) + it = list.remove( it ); + + return list.join( " " ); +} + +QString KStringHandler::remword( const QString &text , uint pos ) +{ + QString tmp = ""; + + if ( text.isEmpty() ) + return tmp; + + // Split words and add into list + QStringList list = QStringList::split( " ", text, true ); + + if ( pos < list.count() ) + list.remove( list.at( pos ) ); + + // Rejoin + return list.join( " " ); +} + +QString KStringHandler::remword( const QString &text , const QString &word ) +{ + QString tmp = ""; + + if ( text.isEmpty() ) + return tmp; + + if ( word.isEmpty() ) + return text; + + // Split words and add into list + QStringList list = QStringList::split( " ", text, true ); + + QStringList::Iterator it = list.find(word); + + if (it != list.end()) + list.remove( it ); + + // Rejoin + return list.join( " " ); +} + +// +// Capitalization routines +// +QString KStringHandler::capwords( const QString &text ) +{ + if ( text.isEmpty() ) { + return text; + } + + const QString strippedText = text.stripWhiteSpace(); + const QStringList words = capwords( QStringList::split( ' ', strippedText ) ); + + QString result = text; + result.replace( strippedText, words.join( " " ) ); + return result; +} + +QStringList KStringHandler::capwords( const QStringList &list ) +{ + QStringList tmp = list; + for ( QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it ) { + *it = ( *it )[ 0 ].upper() + ( *it ).mid( 1 ); + } + return tmp; +} + +// +// Reverse routines +// +QString KStringHandler::reverse( const QString &text ) +{ + QString tmp; + + if ( text.isEmpty() ) + return tmp; + + QStringList list; + list = QStringList::split( " ", text, true ); + list = reverse( list ); + + return list.join( " " ); +} + +QStringList KStringHandler::reverse( const QStringList &list ) +{ + QStringList tmp; + + if ( list.count() == 0 ) + return tmp; + + for ( QStringList::ConstIterator it= list.begin(); + it != list.end(); + it++) + tmp.prepend( *it ); + + return tmp; +} + +// +// Left, Right, Center justification +// +QString KStringHandler::ljust( const QString &text , uint width ) +{ + return text.stripWhiteSpace().leftJustify( width ); +} + +QString KStringHandler::rjust( const QString &text , uint width ) +{ + return text.stripWhiteSpace().rightJustify( width ); +} + +QString KStringHandler::center( const QString &text , uint width ) +{ + const QString s = text.stripWhiteSpace(); + const unsigned int length = s.length(); + if ( width <= length ) { + return s; + } + + QString result; + result.fill( ' ', ( width - length ) / 2 ); + result += s; + + return result.leftJustify( width ); +} + +QString KStringHandler::lsqueeze( const QString & str, uint maxlen ) +{ + if (str.length() > maxlen) { + int part = maxlen-3; + return QString("..." + str.right(part)); + } + else return str; +} + +QString KStringHandler::csqueeze( const QString & str, uint maxlen ) +{ + if (str.length() > maxlen && maxlen > 3) { + int part = (maxlen-3)/2; + return QString(str.left(part) + "..." + str.right(part)); + } + else return str; +} + +QString KStringHandler::rsqueeze( const QString & str, uint maxlen ) +{ + if (str.length() > maxlen) { + int part = maxlen-3; + return QString(str.left(part) + "..."); + } + else return str; +} + +QString KStringHandler::lEmSqueeze(const QString &name, const QFontMetrics& fontMetrics, uint maxlen) +{ + return lPixelSqueeze(name, fontMetrics, fontMetrics.maxWidth() * maxlen); +} + +QString KStringHandler::lPixelSqueeze(const QString& name, const QFontMetrics& fontMetrics, uint maxPixels) +{ + uint nameWidth = fontMetrics.width(name); + + if (maxPixels < nameWidth) + { + QString tmp = name; + const uint em = fontMetrics.maxWidth(); + maxPixels -= fontMetrics.width("..."); + + while (maxPixels < nameWidth && !tmp.isEmpty()) + { + int delta = (nameWidth - maxPixels) / em; + delta = kClamp(delta, 1, delta); // no max + + tmp.remove(0, delta); + nameWidth = fontMetrics.width(tmp); + } + + return ("..." + tmp); + } + + return name; +} + +QString KStringHandler::cEmSqueeze(const QString& name, const QFontMetrics& fontMetrics, uint maxlen) +{ + return cPixelSqueeze(name, fontMetrics, fontMetrics.maxWidth() * maxlen); +} + +QString KStringHandler::cPixelSqueeze(const QString& s, const QFontMetrics& fm, uint width) +{ + if ( s.isEmpty() || uint( fm.width( s ) ) <= width ) { + return s; + } + + const unsigned int length = s.length(); + if ( length == 2 ) { + return s; + } + + const int maxWidth = width - fm.width( '.' ) * 3; + if ( maxWidth <= 0 ) { + return "..."; + } + + unsigned int leftIdx = 0, rightIdx = length; + unsigned int leftWidth = fm.charWidth( s, leftIdx++ ); + unsigned int rightWidth = fm.charWidth( s, --rightIdx ); + while ( leftWidth + rightWidth < uint( maxWidth ) ) { + while ( leftWidth <= rightWidth && leftWidth + rightWidth < uint( maxWidth ) ) { + leftWidth += fm.charWidth( s, leftIdx++ ); + } + while ( rightWidth <= leftWidth && leftWidth + rightWidth < uint( maxWidth ) ) { + rightWidth += fm.charWidth( s, --rightIdx ); + } + } + + if ( leftWidth > rightWidth ) { + --leftIdx; + } else { + ++rightIdx; + } + + rightIdx = length - rightIdx; + if ( leftIdx == 0 && rightIdx == 1 || leftIdx == 1 && rightIdx == 0 ) { + return "..."; + } + + return s.left( leftIdx ) + "..." + s.right( rightIdx ); +} + +QString KStringHandler::rEmSqueeze(const QString& name, const QFontMetrics& fontMetrics, uint maxlen) +{ + return rPixelSqueeze(name, fontMetrics, fontMetrics.maxWidth() * maxlen); +} + +QString KStringHandler::rPixelSqueeze(const QString& name, const QFontMetrics& fontMetrics, uint maxPixels) +{ + uint nameWidth = fontMetrics.width(name); + + if (maxPixels < nameWidth) + { + QString tmp = name; + const uint em = fontMetrics.maxWidth(); + maxPixels -= fontMetrics.width("..."); + + while (maxPixels < nameWidth && !tmp.isEmpty()) + { + int length = tmp.length(); + int delta = em ? (nameWidth - maxPixels) / em : length; + delta = kClamp(delta, 1, length) ; + + tmp.remove(length - delta, delta); + nameWidth = fontMetrics.width(tmp); + } + + return (tmp + "..."); + } + + return name; +} + +///// File name patterns (like *.txt) + +bool KStringHandler::matchFileName( const QString& filename, const QString& pattern ) +{ + int len = filename.length(); + int pattern_len = pattern.length(); + + if (!pattern_len) + return false; + + // Patterns like "Makefile*" + if ( pattern[ pattern_len - 1 ] == '*' && len + 1 >= pattern_len ) { + if ( pattern[ 0 ] == '*' ) + { + return filename.find(pattern.mid(1, pattern_len - 2)) != -1; + } + + const QChar *c1 = pattern.unicode(); + const QChar *c2 = filename.unicode(); + int cnt = 1; + while ( cnt < pattern_len && *c1++ == *c2++ ) + ++cnt; + return cnt == pattern_len; + } + + // Patterns like "*~", "*.extension" + if ( pattern[ 0 ] == '*' && len + 1 >= pattern_len ) + { + const QChar *c1 = pattern.unicode() + pattern_len - 1; + const QChar *c2 = filename.unicode() + len - 1; + int cnt = 1; + while ( cnt < pattern_len && *c1-- == *c2-- ) + ++cnt; + return cnt == pattern_len; + } + + // Patterns like "Makefile" + return ( filename == pattern ); +} + + QStringList +KStringHandler::perlSplit(const QString & sep, const QString & s, uint max) +{ + bool ignoreMax = 0 == max; + + QStringList l; + + int searchStart = 0; + + int tokenStart = s.find(sep, searchStart); + + while (-1 != tokenStart && (ignoreMax || l.count() < max - 1)) + { + if (!s.mid(searchStart, tokenStart - searchStart).isEmpty()) + l << s.mid(searchStart, tokenStart - searchStart); + + searchStart = tokenStart + sep.length(); + tokenStart = s.find(sep, searchStart); + } + + if (!s.mid(searchStart, s.length() - searchStart).isEmpty()) + l << s.mid(searchStart, s.length() - searchStart); + + return l; +} + + QStringList +KStringHandler::perlSplit(const QChar & sep, const QString & s, uint max) +{ + bool ignoreMax = 0 == max; + + QStringList l; + + int searchStart = 0; + + int tokenStart = s.find(sep, searchStart); + + while (-1 != tokenStart && (ignoreMax || l.count() < max - 1)) + { + if (!s.mid(searchStart, tokenStart - searchStart).isEmpty()) + l << s.mid(searchStart, tokenStart - searchStart); + + searchStart = tokenStart + 1; + tokenStart = s.find(sep, searchStart); + } + + if (!s.mid(searchStart, s.length() - searchStart).isEmpty()) + l << s.mid(searchStart, s.length() - searchStart); + + return l; +} + + QStringList +KStringHandler::perlSplit(const QRegExp & sep, const QString & s, uint max) +{ + bool ignoreMax = 0 == max; + + QStringList l; + + int searchStart = 0; + int tokenStart = sep.search(s, searchStart); + int len = sep.matchedLength(); + + while (-1 != tokenStart && (ignoreMax || l.count() < max - 1)) + { + if (!s.mid(searchStart, tokenStart - searchStart).isEmpty()) + l << s.mid(searchStart, tokenStart - searchStart); + + searchStart = tokenStart + len; + tokenStart = sep.search(s, searchStart); + len = sep.matchedLength(); + } + + if (!s.mid(searchStart, s.length() - searchStart).isEmpty()) + l << s.mid(searchStart, s.length() - searchStart); + + return l; +} + + QString +KStringHandler::tagURLs( const QString& text ) +{ + /*static*/ QRegExp urlEx("(www\\.(?!\\.)|(fish|(f|ht)tp(|s))://)[\\d\\w\\./,:_~\\?=&;#@\\-\\+\\%\\$]+[\\d\\w/]"); + + QString richText( text ); + int urlPos = 0, urlLen; + while ((urlPos = urlEx.search(richText, urlPos)) >= 0) + { + urlLen = urlEx.matchedLength(); + QString href = richText.mid( urlPos, urlLen ); + // Qt doesn't support (?<=pattern) so we do it here + if((urlPos > 0) && richText[urlPos-1].isLetterOrNumber()){ + urlPos++; + continue; + } + // Don't use QString::arg since %01, %20, etc could be in the string + QString anchor = "<a href=\"" + href + "\">" + href + "</a>"; + richText.replace( urlPos, urlLen, anchor ); + + + urlPos += anchor.length(); + } + return richText; +} + +QString KStringHandler::obscure( const QString &str ) +{ + QString result; + const QChar *unicode = str.unicode(); + for ( uint i = 0; i < str.length(); ++i ) + result += ( unicode[ i ].unicode() <= 0x21 ) ? unicode[ i ] : + QChar( 0x1001F - unicode[ i ].unicode() ); + + return result; +} + +bool KStringHandler::isUtf8(const char *buf) +{ + int i, n; + register unsigned char c; + bool gotone = false; + + if (!buf) + return true; // whatever, just don't crash + +#define F 0 /* character never appears in text */ +#define T 1 /* character appears in plain ASCII text */ +#define I 2 /* character appears in ISO-8859 text */ +#define X 3 /* character appears in non-ISO extended ASCII (Mac, IBM PC) */ + + static const unsigned char text_chars[256] = { + /* BEL BS HT LF FF CR */ + F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F, /* 0x0X */ + /* ESC */ + F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x3X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x4X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x5X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x6X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F, /* 0x7X */ + /* NEL */ + X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, /* 0x8X */ + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x9X */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xaX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xbX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xcX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xdX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xeX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I /* 0xfX */ + }; + + /* *ulen = 0; */ + for (i = 0; (c = buf[i]); i++) { + if ((c & 0x80) == 0) { /* 0xxxxxxx is plain ASCII */ + /* + * Even if the whole file is valid UTF-8 sequences, + * still reject it if it uses weird control characters. + */ + + if (text_chars[c] != T) + return false; + + } else if ((c & 0x40) == 0) { /* 10xxxxxx never 1st byte */ + return false; + } else { /* 11xxxxxx begins UTF-8 */ + int following; + + if ((c & 0x20) == 0) { /* 110xxxxx */ + following = 1; + } else if ((c & 0x10) == 0) { /* 1110xxxx */ + following = 2; + } else if ((c & 0x08) == 0) { /* 11110xxx */ + following = 3; + } else if ((c & 0x04) == 0) { /* 111110xx */ + following = 4; + } else if ((c & 0x02) == 0) { /* 1111110x */ + following = 5; + } else + return false; + + for (n = 0; n < following; n++) { + i++; + if (!(c = buf[i])) + goto done; + + if ((c & 0x80) == 0 || (c & 0x40)) + return false; + } + gotone = true; + } + } +done: + return gotone; /* don't claim it's UTF-8 if it's all 7-bit */ +} + +#undef F +#undef T +#undef I +#undef X + +QString KStringHandler::from8Bit( const char *str ) +{ + if (!str) + return QString::null; + if (!*str) { + static const QString &emptyString = KGlobal::staticQString(""); + return emptyString; + } + return KStringHandler::isUtf8( str ) ? + QString::fromUtf8( str ) : + QString::fromLocal8Bit( str ); +} |