kpimutils
linklocator.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00030 #include "linklocator.h"
00031
00032 #include <kglobal.h>
00033 #include <kstandarddirs.h>
00034 #include <kcodecs.h>
00035 #include <kdebug.h>
00036 #include <kdeversion.h>
00037 #if KDE_IS_VERSION( 4, 0, 95 )
00038 #include <kemoticons.h>
00039 #endif
00040
00041 #include <QtCore/QCoreApplication>
00042 #include <QtCore/QFile>
00043 #include <QtCore/QRegExp>
00044 #include <QtGui/QTextDocument>
00045
00046 #include <limits.h>
00047
00048 using namespace KPIMUtils;
00049
00054
00055 class KPIMUtils::LinkLocator::Private
00056 {
00057 public:
00058 int mMaxUrlLen;
00059 int mMaxAddressLen;
00060 };
00061
00062
00063 #if KDE_IS_VERSION( 4, 0, 95 )
00064
00065 K_GLOBAL_STATIC( KEmoticons, sEmoticons )
00066 #endif
00067
00068 LinkLocator::LinkLocator( const QString &text, int pos )
00069 : mText( text ), mPos( pos ), d( new KPIMUtils::LinkLocator::Private )
00070 {
00071 d->mMaxUrlLen = 4096;
00072 d->mMaxAddressLen = 255;
00073
00074
00075
00076
00077
00078
00079 }
00080
00081 LinkLocator::~LinkLocator()
00082 {
00083 delete d;
00084 }
00085
00086 void LinkLocator::setMaxUrlLen( int length )
00087 {
00088 d->mMaxUrlLen = length;
00089 }
00090
00091 int LinkLocator::maxUrlLen() const
00092 {
00093 return d->mMaxUrlLen;
00094 }
00095
00096 void LinkLocator::setMaxAddressLen( int length )
00097 {
00098 d->mMaxAddressLen = length;
00099 }
00100
00101 int LinkLocator::maxAddressLen() const
00102 {
00103 return d->mMaxAddressLen;
00104 }
00105
00106 QString LinkLocator::getUrl()
00107 {
00108 QString url;
00109 if ( atUrl() ) {
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121 QChar beforeUrl, afterUrl;
00122
00123
00124 if ( mPos > 0 ) {
00125 beforeUrl = mText[mPos - 1];
00126
00127 if ( beforeUrl == '(' ) {
00128 afterUrl = ')';
00129 } else if ( beforeUrl == '[' ) {
00130 afterUrl = ']';
00131 } else if ( beforeUrl == '<' ) {
00132 afterUrl = '>';
00133 } else if ( beforeUrl == '>' ) {
00134 afterUrl = '<';
00135 } else if ( beforeUrl == '"' ) {
00136 afterUrl = '"';
00137 }
00138 }
00139
00140 url.reserve( maxUrlLen() );
00141 int start = mPos;
00142 while ( ( mPos < (int)mText.length() ) &&
00143 ( mText[mPos].isPrint() || mText[mPos].isSpace() ) &&
00144 ( ( afterUrl.isNull() && !mText[mPos].isSpace() ) ||
00145 ( !afterUrl.isNull() && mText[mPos] != afterUrl ) ) ) {
00146 if ( !mText[mPos].isSpace() ) {
00147 url.append( mText[mPos] );
00148 if ( url.length() > maxUrlLen() ) {
00149 break;
00150 }
00151 }
00152
00153 mPos++;
00154 }
00155
00156 if ( isEmptyUrl(url) || ( url.length() > maxUrlLen() ) ) {
00157 mPos = start;
00158 url = "";
00159 } else {
00160 --mPos;
00161 }
00162 }
00163 return url;
00164 }
00165
00166
00167 bool LinkLocator::atUrl() const
00168 {
00169
00170
00171 const QString allowedSpecialChars = QString( ".!#$%&'*+-/=?^_`{|}~" );
00172
00173
00174
00175 if ( ( mPos > 0 ) &&
00176 ( mText[mPos-1].isLetterOrNumber() ||
00177 ( allowedSpecialChars.indexOf( mText[mPos-1] ) != -1 ) ) ) {
00178 return false;
00179 }
00180
00181 QChar ch = mText[mPos];
00182 return
00183 ( ch == 'h' && ( mText.mid( mPos, 7 ) == "http://" ||
00184 mText.mid( mPos, 8 ) == "https://" ) ) ||
00185 ( ch == 'v' && mText.mid( mPos, 6 ) == "vnc://" ) ||
00186 ( ch == 'f' && ( mText.mid( mPos, 7 ) == "fish://" ||
00187 mText.mid( mPos, 6 ) == "ftp://" ||
00188 mText.mid( mPos, 7 ) == "ftps://" ) ) ||
00189 ( ch == 's' && ( mText.mid( mPos, 7 ) == "sftp://" ||
00190 mText.mid( mPos, 6 ) == "smb://" ) ) ||
00191 ( ch == 'm' && mText.mid( mPos, 7 ) == "mailto:" ) ||
00192 ( ch == 'w' && mText.mid( mPos, 4 ) == "www." ) ||
00193 ( ch == 'f' && ( mText.mid( mPos, 4 ) == "ftp." ||
00194 mText.mid( mPos, 7 ) == "file://" ) ) ||
00195 ( ch == 'n' && mText.mid( mPos, 5 ) == "news:" );
00196 }
00197
00198 bool LinkLocator::isEmptyUrl( const QString &url ) const
00199 {
00200 return url.isEmpty() ||
00201 url == "http://" ||
00202 url == "https://" ||
00203 url == "fish://" ||
00204 url == "ftp://" ||
00205 url == "ftps://" ||
00206 url == "sftp://" ||
00207 url == "smb://" ||
00208 url == "vnc://" ||
00209 url == "mailto" ||
00210 url == "www" ||
00211 url == "ftp" ||
00212 url == "news" ||
00213 url == "news://";
00214 }
00215
00216 QString LinkLocator::getEmailAddress()
00217 {
00218 QString address;
00219
00220 if ( mText[mPos] == '@' ) {
00221
00222
00223 const QString allowedSpecialChars = QString( ".!#$%&'*+-/=?^_`{|}~" );
00224
00225
00226 int start = mPos - 1;
00227 while ( start >= 0 && mText[start].unicode() < 128 &&
00228 ( mText[start].isLetterOrNumber() ||
00229 mText[start] == '@' ||
00230 allowedSpecialChars.indexOf( mText[start] ) != -1 ) ) {
00231 if ( mText[start] == '@' ) {
00232 return QString();
00233 }
00234 --start;
00235 }
00236 ++start;
00237
00238 while ( ( start < mPos ) && !mText[start].isLetterOrNumber() ) {
00239 ++start;
00240 }
00241 if ( start == mPos ) {
00242 return QString();
00243 }
00244
00245
00246 int dotPos = INT_MAX;
00247 int end = mPos + 1;
00248 while ( end < (int)mText.length() &&
00249 ( mText[end].isLetterOrNumber() ||
00250 mText[end] == '@' ||
00251 mText[end] == '.' ||
00252 mText[end] == '-' ) ) {
00253 if ( mText[end] == '@' ) {
00254 return QString();
00255 }
00256 if ( mText[end] == '.' ) {
00257 dotPos = qMin( dotPos, end );
00258 }
00259 ++end;
00260 }
00261
00262 while ( ( end > mPos ) && !mText[end - 1].isLetterOrNumber() ) {
00263 --end;
00264 }
00265 if ( end == mPos ) {
00266 return QString();
00267 }
00268 if ( dotPos >= end ) {
00269 return QString();
00270 }
00271
00272 if ( end - start > maxAddressLen() ) {
00273 return QString();
00274 }
00275 address = mText.mid( start, end - start );
00276
00277 mPos = end - 1;
00278 }
00279 return address;
00280 }
00281
00282 QString LinkLocator::convertToHtml( const QString &plainText, int flags,
00283 int maxUrlLen, int maxAddressLen )
00284 {
00285 LinkLocator locator( plainText );
00286 locator.setMaxUrlLen( maxUrlLen );
00287 locator.setMaxAddressLen( maxAddressLen );
00288
00289 QString str;
00290 QString result( (QChar*)0, (int)locator.mText.length() * 2 );
00291 QChar ch;
00292 int x;
00293 bool startOfLine = true;
00294 QString emoticon;
00295
00296 for ( locator.mPos = 0, x = 0; locator.mPos < (int)locator.mText.length();
00297 locator.mPos++, x++ ) {
00298 ch = locator.mText[locator.mPos];
00299 if ( flags & PreserveSpaces ) {
00300 if ( ch == ' ' ) {
00301 if ( locator.mPos + 1 < locator.mText.length() ) {
00302 if ( locator.mText[locator.mPos + 1] != ' ' ) {
00303
00304
00305 const bool endOfLine = locator.mText[locator.mPos + 1] == '\n';
00306 if ( !startOfLine && !endOfLine ) {
00307 result += ' ';
00308 } else {
00309 result += " ";
00310 }
00311 } else {
00312
00313
00314 while ( locator.mPos < locator.mText.length() && locator.mText[locator.mPos] == ' ' ) {
00315 result += " ";
00316 locator.mPos++;
00317 x++;
00318 }
00319
00320
00321 locator.mPos--;
00322 x--;
00323 }
00324 } else {
00325
00326 result += " ";
00327 }
00328
00329 if ( startOfLine ) {
00330 startOfLine = false;
00331 }
00332 continue;
00333 } else if ( ch == '\t' ) {
00334 do
00335 {
00336 result += " ";
00337 x++;
00338 }
00339 while ( ( x & 7 ) != 0 );
00340 x--;
00341 startOfLine = false;
00342 continue;
00343 }
00344 }
00345 if ( ch == '\n' ) {
00346 result += "<br />\n";
00347 startOfLine = true;
00348 x = -1;
00349 continue;
00350 }
00351
00352 startOfLine = false;
00353 if ( ch == '&' ) {
00354 result += "&";
00355 } else if ( ch == '"' ) {
00356 result += """;
00357 } else if ( ch == '<' ) {
00358 result += "<";
00359 } else if ( ch == '>' ) {
00360 result += ">";
00361 } else {
00362 const int start = locator.mPos;
00363 if ( !( flags & IgnoreUrls ) ) {
00364 str = locator.getUrl();
00365 if ( !str.isEmpty() ) {
00366 QString hyperlink;
00367 if ( str.left( 4 ) == "www." ) {
00368 hyperlink = "http://" + str;
00369 } else if ( str.left( 4 ) == "ftp." ) {
00370 hyperlink = "ftp://" + str;
00371 } else {
00372 hyperlink = str;
00373 }
00374
00375 result += "<a href=\"" + hyperlink + "\">" + Qt::escape( str ) + "</a>";
00376 x += locator.mPos - start;
00377 continue;
00378 }
00379 str = locator.getEmailAddress();
00380 if ( !str.isEmpty() ) {
00381
00382 int len = str.indexOf( '@' );
00383 QString localPart = str.left( len );
00384
00385
00386
00387 result.truncate( result.length() -
00388 len - ( localPart.count( '&' ) * 4 ) );
00389 x -= len;
00390
00391 result += "<a href=\"mailto:" + str + "\">" + str + "</a>";
00392 x += str.length() - 1;
00393 continue;
00394 }
00395 }
00396 if ( flags & HighlightText ) {
00397 str = locator.highlightedText();
00398 if ( !str.isEmpty() ) {
00399 result += str;
00400 x += locator.mPos - start;
00401 continue;
00402 }
00403 }
00404 result += ch;
00405 }
00406 }
00407
00408 #if KDE_IS_VERSION( 4, 0, 95 )
00409 if ( flags & ReplaceSmileys ) {
00410 QStringList exclude;
00411 exclude << "(c)" << "(C)" << ">:-(" << ">:(" << "(B)" << "(b)" << "(P)" << "(p)";
00412 exclude << "(O)" << "(o)" << "(D)" << "(d)" << "(E)" << "(e)" << "(K)" << "(k)";
00413 exclude << "(I)" << "(i)" << "(L)" << "(l)" << "(8)" << "(T)" << "(t)" << "(G)";
00414 exclude << "(g)" << "(F)" << "(f)" << "(H)";
00415 exclude << "8)" << "(N)" << "(n)" << "(Y)" << "(y)" << "(U)" << "(u)" << "(W)" << "(w)";
00416 static QString cachedEmoticonsThemeName;
00417 if ( cachedEmoticonsThemeName.isEmpty() ) {
00418 cachedEmoticonsThemeName = KEmoticons::currentThemeName();
00419 }
00420 result =
00421 sEmoticons->theme( cachedEmoticonsThemeName ).parseEmoticons(
00422 result, KEmoticonsTheme::StrictParse | KEmoticonsTheme::SkipHTML, exclude );
00423 }
00424 #endif
00425
00426 return result;
00427 }
00428
00429 QString LinkLocator::pngToDataUrl( const QString &iconPath )
00430 {
00431 if ( iconPath.isEmpty() ) {
00432 return QString();
00433 }
00434
00435 QFile pngFile( iconPath );
00436 if ( !pngFile.open( QIODevice::ReadOnly | QIODevice::Unbuffered ) ) {
00437 return QString();
00438 }
00439
00440 QByteArray ba = pngFile.readAll();
00441 pngFile.close();
00442 return QString::fromLatin1( "data:image/png;base64,%1" ).arg( ba.toBase64().constData() );
00443 }
00444
00445 QString LinkLocator::highlightedText()
00446 {
00447
00448 if ( ( mPos > 0 ) && !mText[mPos-1].isSpace() ) {
00449 return QString();
00450 }
00451
00452 const QChar ch = mText[mPos];
00453 if ( ch != '/' && ch != '*' && ch != '_' ) {
00454 return QString();
00455 }
00456
00457 QRegExp re =
00458 QRegExp( QString( "\\%1((\\w+)([\\s-']\\w+)*( ?[,.:\\?!;])?)\\%2" ).arg( ch ).arg( ch ) );
00459 re.setMinimal(true);
00460 if ( re.indexIn( mText, mPos ) == mPos ) {
00461 int length = re.matchedLength();
00462
00463 if ( mPos + length < mText.length() && !mText[mPos + length].isSpace() ) {
00464 return QString();
00465 }
00466 mPos += length - 1;
00467 switch ( ch.toLatin1() ) {
00468 case '*':
00469 return "<b>" + re.cap( 1 ) + "</b>";
00470 case '_':
00471 return "<u>" + re.cap( 1 ) + "</u>";
00472 case '/':
00473 return "<i>" + re.cap( 1 ) + "</i>";
00474 }
00475 }
00476 return QString();
00477 }