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 int start = mPos;
00112 while ( mPos < (int)mText.length() &&
00113 mText[mPos] > ' ' && mText[mPos] != '"' &&
00114 QString( "<>()[]" ).indexOf( mText[mPos] ) == -1 ) {
00115 ++mPos;
00116 }
00117
00118
00119 const QString allowedSpecialChars = QString( "#/&-_" );
00120 while ( mPos > start && mText[mPos-1].isPunct() &&
00121 allowedSpecialChars.indexOf( mText[mPos-1] ) == -1 ) {
00122 --mPos;
00123 }
00124
00125 url = mText.mid( start, mPos - start );
00126 if ( isEmptyUrl(url) || mPos - start > maxUrlLen() ) {
00127 mPos = start;
00128 url = "";
00129 } else {
00130 --mPos;
00131 }
00132 }
00133 return url;
00134 }
00135
00136
00137 bool LinkLocator::atUrl() const
00138 {
00139
00140
00141 const QString allowedSpecialChars = QString( ".!#$%&'*+-/=?^_`{|}~" );
00142
00143
00144
00145 if ( ( mPos > 0 ) &&
00146 ( mText[mPos-1].isLetterOrNumber() ||
00147 ( allowedSpecialChars.indexOf( mText[mPos-1] ) != -1 ) ) ) {
00148 return false;
00149 }
00150
00151 QChar ch = mText[mPos];
00152 return
00153 ( ch == 'h' && ( mText.mid( mPos, 7 ) == "http://" ||
00154 mText.mid( mPos, 8 ) == "https://" ) ) ||
00155 ( ch == 'v' && mText.mid( mPos, 6 ) == "vnc://" ) ||
00156 ( ch == 'f' && ( mText.mid( mPos, 7 ) == "fish://" ||
00157 mText.mid( mPos, 6 ) == "ftp://" ||
00158 mText.mid( mPos, 7 ) == "ftps://" ) ) ||
00159 ( ch == 's' && ( mText.mid( mPos, 7 ) == "sftp://" ||
00160 mText.mid( mPos, 6 ) == "smb://" ) ) ||
00161 ( ch == 'm' && mText.mid( mPos, 7 ) == "mailto:" ) ||
00162 ( ch == 'w' && mText.mid( mPos, 4 ) == "www." ) ||
00163 ( ch == 'f' && ( mText.mid( mPos, 4 ) == "ftp." ||
00164 mText.mid( mPos, 7 ) == "file://" ) ) ||
00165 ( ch == 'n' && mText.mid( mPos, 5 ) == "news:" );
00166 }
00167
00168 bool LinkLocator::isEmptyUrl( const QString &url ) const
00169 {
00170 return url.isEmpty() ||
00171 url == "http://" ||
00172 url == "https://" ||
00173 url == "fish://" ||
00174 url == "ftp://" ||
00175 url == "ftps://" ||
00176 url == "sftp://" ||
00177 url == "smb://" ||
00178 url == "vnc://" ||
00179 url == "mailto" ||
00180 url == "www" ||
00181 url == "ftp" ||
00182 url == "news" ||
00183 url == "news://";
00184 }
00185
00186 QString LinkLocator::getEmailAddress()
00187 {
00188 QString address;
00189
00190 if ( mText[mPos] == '@' ) {
00191
00192
00193 const QString allowedSpecialChars = QString( ".!#$%&'*+-/=?^_`{|}~" );
00194
00195
00196 int start = mPos - 1;
00197 while ( start >= 0 && mText[start].unicode() < 128 &&
00198 ( mText[start].isLetterOrNumber() ||
00199 mText[start] == '@' ||
00200 allowedSpecialChars.indexOf( mText[start] ) != -1 ) ) {
00201 if ( mText[start] == '@' ) {
00202 return QString();
00203 }
00204 --start;
00205 }
00206 ++start;
00207
00208 while ( ( start < mPos ) && !mText[start].isLetterOrNumber() ) {
00209 ++start;
00210 }
00211 if ( start == mPos ) {
00212 return QString();
00213 }
00214
00215
00216 int dotPos = INT_MAX;
00217 int end = mPos + 1;
00218 while ( end < (int)mText.length() &&
00219 ( mText[end].isLetterOrNumber() ||
00220 mText[end] == '@' ||
00221 mText[end] == '.' ||
00222 mText[end] == '-' ) ) {
00223 if ( mText[end] == '@' ) {
00224 return QString();
00225 }
00226 if ( mText[end] == '.' ) {
00227 dotPos = qMin( dotPos, end );
00228 }
00229 ++end;
00230 }
00231
00232 while ( ( end > mPos ) && !mText[end - 1].isLetterOrNumber() ) {
00233 --end;
00234 }
00235 if ( end == mPos ) {
00236 return QString();
00237 }
00238 if ( dotPos >= end ) {
00239 return QString();
00240 }
00241
00242 if ( end - start > maxAddressLen() ) {
00243 return QString();
00244 }
00245 address = mText.mid( start, end - start );
00246
00247 mPos = end - 1;
00248 }
00249 return address;
00250 }
00251
00252 QString LinkLocator::convertToHtml( const QString &plainText, int flags,
00253 int maxUrlLen, int maxAddressLen )
00254 {
00255 LinkLocator locator( plainText );
00256 locator.setMaxUrlLen( maxUrlLen );
00257 locator.setMaxAddressLen( maxAddressLen );
00258
00259 QString str;
00260 QString result( (QChar*)0, (int)locator.mText.length() * 2 );
00261 QChar ch;
00262 int x;
00263 bool startOfLine = true;
00264 QString emoticon;
00265
00266 for ( locator.mPos = 0, x = 0; locator.mPos < (int)locator.mText.length();
00267 locator.mPos++, x++ ) {
00268 ch = locator.mText[locator.mPos];
00269 if ( flags & PreserveSpaces ) {
00270 if ( ch == ' ' ) {
00271 if ( startOfLine ) {
00272 result += " ";
00273 locator.mPos++, x++;
00274 startOfLine = false;
00275 }
00276 while ( locator.mText[locator.mPos] == ' ' ) {
00277 result += ' ';
00278 locator.mPos++, x++;
00279 if ( locator.mText[locator.mPos] == ' ' ) {
00280 result += " ";
00281 locator.mPos++, x++;
00282 }
00283 }
00284 locator.mPos--, x--;
00285 continue;
00286 } else if ( ch == '\t' ) {
00287 do
00288 {
00289 result += " ";
00290 x++;
00291 }
00292 while ( ( x & 7 ) != 0 );
00293 x--;
00294 startOfLine = false;
00295 continue;
00296 }
00297 }
00298 if ( ch == '\n' ) {
00299 result += "<br />\n";
00300 startOfLine = true;
00301 x = -1;
00302 continue;
00303 }
00304
00305 startOfLine = false;
00306 if ( ch == '&' ) {
00307 result += "&";
00308 } else if ( ch == '"' ) {
00309 result += """;
00310 } else if ( ch == '<' ) {
00311 result += "<";
00312 } else if ( ch == '>' ) {
00313 result += ">";
00314 } else {
00315 const int start = locator.mPos;
00316 if ( !( flags & IgnoreUrls ) ) {
00317 str = locator.getUrl();
00318 if ( !str.isEmpty() ) {
00319 QString hyperlink;
00320 if ( str.left( 4 ) == "www." ) {
00321 hyperlink = "http://" + str;
00322 } else if ( str.left( 4 ) == "ftp." ) {
00323 hyperlink = "ftp://" + str;
00324 } else {
00325 hyperlink = str;
00326 }
00327
00328 str = str.replace( '&', "&" );
00329 result += "<a href=\"" + hyperlink + "\">" + str + "</a>";
00330 x += locator.mPos - start;
00331 continue;
00332 }
00333 str = locator.getEmailAddress();
00334 if ( !str.isEmpty() ) {
00335
00336 int len = str.indexOf( '@' );
00337 QString localPart = str.left( len );
00338
00339
00340
00341 result.truncate( result.length() -
00342 len - ( localPart.count( '&' ) * 4 ) );
00343 x -= len;
00344
00345 result += "<a href=\"mailto:" + str + "\">" + str + "</a>";
00346 x += str.length() - 1;
00347 continue;
00348 }
00349 }
00350 if ( flags & HighlightText ) {
00351 str = locator.highlightedText();
00352 if ( !str.isEmpty() ) {
00353 result += str;
00354 x += locator.mPos - start;
00355 continue;
00356 }
00357 }
00358 result += ch;
00359 }
00360 }
00361
00362 #if KDE_IS_VERSION( 4, 0, 95 )
00363 if ( flags & ReplaceSmileys ) {
00364 QStringList exclude;
00365 exclude << "(c)" << "(C)" << ">:-(" << ">:(" << "(B)" << "(b)" << "(P)" << "(p)";
00366 exclude << "(O)" << "(o)" << "(D)" << "(d)" << "(E)" << "(e)" << "(K)" << "(k)";
00367 exclude << "(I)" << "(i)" << "(L)" << "(l)" << "(8)" << "(T)" << "(t)" << "(G)";
00368 exclude << "(g)" << "(F)" << "(f)" << "(H)";
00369 exclude << "8)" << "(N)" << "(n)" << "(Y)" << "(y)" << "(U)" << "(u)" << "(W)" << "(w)";
00370 result = sEmoticons->theme().parseEmoticons( result, KEmoticonsTheme::StrictParse |
00371 KEmoticonsTheme::SkipHTML,
00372 exclude );
00373 }
00374 #endif
00375
00376 return result;
00377 }
00378
00379 QString LinkLocator::pngToDataUrl( const QString &iconPath )
00380 {
00381 if ( iconPath.isEmpty() ) {
00382 return QString();
00383 }
00384
00385 QFile pngFile( iconPath );
00386 if ( !pngFile.open( QIODevice::ReadOnly | QIODevice::Unbuffered ) ) {
00387 return QString();
00388 }
00389
00390 QByteArray ba = pngFile.readAll();
00391 pngFile.close();
00392 return QString::fromLatin1( "data:image/png;base64,%1" ).arg( ba.toBase64().constData() );
00393 }
00394
00395 QString LinkLocator::highlightedText()
00396 {
00397
00398 if ( ( mPos > 0 ) && !mText[mPos-1].isSpace() ) {
00399 return QString();
00400 }
00401
00402 const QChar ch = mText[mPos];
00403 if ( ch != '/' && ch != '*' && ch != '_' ) {
00404 return QString();
00405 }
00406
00407 QRegExp re =
00408 QRegExp( QString( "\\%1([0-9A-Za-z]+)\\%2" ).arg( ch ).arg( ch ) );
00409 if ( re.indexIn( mText, mPos ) == mPos ) {
00410 int length = re.matchedLength();
00411
00412 if ( mPos + length < mText.length() && !mText[mPos + length].isSpace() ) {
00413 return QString();
00414 }
00415 mPos += length - 1;
00416 switch ( ch.toLatin1() ) {
00417 case '*':
00418 return "<b>" + re.cap( 1 ) + "</b>";
00419 case '_':
00420 return "<u>" + re.cap( 1 ) + "</u>";
00421 case '/':
00422 return "<i>" + re.cap( 1 ) + "</i>";
00423 }
00424 }
00425 return QString();
00426 }