33 #include <QtCore/QCoreApplication>
34 #include <QtCore/QFile>
35 #include <QtCore/QRegExp>
36 #include <QTextDocument>
40 using namespace KPIMUtils;
47 class KPIMUtils::LinkLocator::Private
56 K_GLOBAL_STATIC( KEmoticons, sEmoticons )
59 : mText( text ), mPos( pos ), d( new KPIMUtils::
LinkLocator::Private )
62 d->mMaxAddressLen = 255;
78 d->mMaxUrlLen = length;
88 d->mMaxAddressLen = length;
93 return d->mMaxAddressLen;
98 return getUrlAndCheckValidHref();
102 QString LinkLocator::getUrlAndCheckValidHref(
bool *badurl)
117 QChar beforeUrl, afterUrl;
125 if ( beforeUrl == QLatin1Char(
'[') ) {
126 afterUrl = QLatin1Char(
']');
127 }
else if ( beforeUrl == QLatin1Char(
'<') ) {
128 afterUrl = QLatin1Char(
'>');
129 }
else if ( beforeUrl == QLatin1Char(
'>') ) {
130 afterUrl = QLatin1Char(
'<');
131 }
else if ( beforeUrl == QLatin1Char(
'"') ) {
132 afterUrl = QLatin1Char(
'"');
138 bool previousCharIsADoubleQuote =
false;
139 while ( (
mPos < (
int)
mText.length() ) &&
141 ( ( afterUrl.isNull() && !
mText[
mPos].isSpace() ) ||
142 ( !afterUrl.isNull() &&
mText[
mPos] != afterUrl ) ) ) {
144 if (
mText[
mPos] == QLatin1Char(
'>') && previousCharIsADoubleQuote) {
152 previousCharIsADoubleQuote =
true;
154 previousCharIsADoubleQuote =
false;
165 if ( isEmptyUrl( url ) || ( url.length() >
maxUrlLen() ) ) {
178 QList<QChar> wordBoundaries;
179 wordBoundaries << QLatin1Char(
'.') << QLatin1Char(
',') << QLatin1Char(
':') << QLatin1Char(
'!') << QLatin1Char(
'?') << QLatin1Char(
')') << QLatin1Char(
'>');
180 if ( url.length() > 1 ) {
182 if ( wordBoundaries.contains( url.at( url.length() - 1 ) ) ) {
188 }
while( url.length() > 1 );
195 bool LinkLocator::atUrl()
const
199 static const QString allowedSpecialChars = QLatin1String(
".!#$%&'*+-/=?^_`{|}~" );
205 ( allowedSpecialChars.indexOf(
mText[
mPos-1] ) != -1 ) ) ) {
211 ( ch == QLatin1Char(
'h') && (
mText.mid(
mPos, 7 ) == QLatin1String(
"http://" ) ||
212 mText.mid(
mPos, 8 ) == QLatin1String(
"https://" ) ) ) ||
213 ( ch == QLatin1Char(
'v') &&
mText.mid(
mPos, 6 ) == QLatin1String(
"vnc://" ) ) ||
214 ( ch == QLatin1Char(
'f') && (
mText.mid(
mPos, 7 ) == QLatin1String(
"fish://" ) ||
215 mText.mid(
mPos, 6 ) == QLatin1String(
"ftp://" ) ||
216 mText.mid(
mPos, 7 ) == QLatin1String(
"ftps://" ) ) ) ||
217 ( ch == QLatin1Char(
's') && (
mText.mid(
mPos, 7 ) == QLatin1String(
"sftp://" ) ||
218 mText.mid(
mPos, 6 ) == QLatin1String(
"smb://" ) ) ) ||
219 ( ch == QLatin1Char(
'm') &&
mText.mid(
mPos, 7 ) == QLatin1String(
"mailto:" ) ) ||
220 ( ch == QLatin1Char(
'w') &&
mText.mid(
mPos, 4 ) == QLatin1String(
"www." ) ) ||
221 ( ch == QLatin1Char(
'f') && (
mText.mid(
mPos, 4 ) == QLatin1String(
"ftp." ) ||
222 mText.mid(
mPos, 7 ) == QLatin1String(
"file://" ) ) )||
223 ( ch == QLatin1Char(
'n') &&
mText.mid(
mPos, 5 ) == QLatin1String(
"news:" ) );
226 bool LinkLocator::isEmptyUrl(
const QString &url )
const
228 return url.isEmpty() ||
229 url == QLatin1String(
"http://" ) ||
230 url == QLatin1String(
"https://" ) ||
231 url == QLatin1String(
"fish://" ) ||
232 url == QLatin1String(
"ftp://" ) ||
233 url == QLatin1String(
"ftps://" ) ||
234 url == QLatin1String(
"sftp://" ) ||
235 url == QLatin1String(
"smb://" ) ||
236 url == QLatin1String(
"vnc://" ) ||
237 url == QLatin1String(
"mailto" ) ||
238 url == QLatin1String(
"www" ) ||
239 url == QLatin1String(
"ftp" ) ||
240 url == QLatin1String(
"news" ) ||
241 url == QLatin1String(
"news://" );
251 static const QString allowedSpecialChars = QLatin1String(
".!#$%&'*+-/=?^_`{|}~" );
254 int start =
mPos - 1;
255 while ( start >= 0 &&
mText[start].unicode() < 128 &&
256 (
mText[start].isLetterOrNumber() ||
257 mText[start] == QLatin1Char(
'@') ||
258 allowedSpecialChars.indexOf(
mText[start] ) != -1 ) ) {
259 if (
mText[start] == QLatin1Char(
'@') ) {
266 while ( ( start <
mPos ) && !
mText[start].isLetterOrNumber() ) {
269 if ( start ==
mPos ) {
274 int dotPos = INT_MAX;
276 while ( end < (
int)
mText.length() &&
277 (
mText[end].isLetterOrNumber() ||
278 mText[end] == QLatin1Char(
'@') ||
279 mText[end] == QLatin1Char(
'.') ||
280 mText[end] == QLatin1Char(
'-') ) ) {
281 if (
mText[end] == QLatin1Char(
'@') ) {
284 if (
mText[end] == QLatin1Char(
'.') ) {
285 dotPos = qMin( dotPos, end );
290 while ( ( end >
mPos ) && !
mText[end - 1].isLetterOrNumber() ) {
296 if ( dotPos >= end ) {
303 address =
mText.mid( start, end - start );
311 int maxUrlLen,
int maxAddressLen )
318 QString result( (QChar*)0, (
int)locator.
mText.length() * 2 );
321 bool startOfLine =
true;
323 for ( locator.
mPos = 0, x = 0; locator.
mPos < (
int)locator.
mText.length();
324 locator.
mPos++, x++ ) {
326 if ( flags & PreserveSpaces ) {
327 if ( ch == QLatin1Char(
' ') ) {
328 if ( locator.
mPos + 1 < locator.
mText.length() ) {
329 if ( locator.
mText[locator.
mPos + 1] != QLatin1Char(
' ') ) {
332 const bool endOfLine = locator.
mText[locator.
mPos + 1] == QLatin1Char(
'\n');
333 if ( !startOfLine && !endOfLine ) {
334 result += QLatin1Char(
' ');
336 result += QLatin1String(
" ");
341 while ( locator.
mPos < locator.
mText.length() && locator.
mText[locator.
mPos] == QLatin1Char(
' ') ) {
342 result += QLatin1String(
" ");
353 result += QLatin1String(
" ");
360 }
else if ( ch == QLatin1Char(
'\t') ) {
362 result += QLatin1String(
" ");
364 }
while ( ( x & 7 ) != 0 );
370 if ( ch == QLatin1Char(
'\n') ) {
371 result += QLatin1String(
"<br />\n");
378 if ( ch == QLatin1Char(
'&') ) {
379 result += QLatin1String(
"&");
380 }
else if ( ch == QLatin1Char(
'"') ) {
381 result += QLatin1String(
""");
382 }
else if ( ch == QLatin1Char(
'<') ) {
383 result += QLatin1String(
"<");
384 }
else if ( ch == QLatin1Char(
'>') ) {
385 result += QLatin1String(
">");
387 const int start = locator.
mPos;
388 if ( !( flags & IgnoreUrls ) ) {
390 str = locator.getUrlAndCheckValidHref(&badUrl);
392 return locator.
mText;
395 if ( !str.isEmpty() ) {
397 if ( str.left( 4 ) == QLatin1String(
"www.") ) {
398 hyperlink = QLatin1String(
"http://") + str;
399 }
else if ( str.left( 4 ) == QLatin1String(
"ftp.") ) {
400 hyperlink = QLatin1String(
"ftp://") + str;
405 result += QLatin1String(
"<a href=\"") + hyperlink + QLatin1String(
"\">") + Qt::escape( str ) + QLatin1String(
"</a>");
406 x += locator.
mPos - start;
410 if ( !str.isEmpty() ) {
412 int len = str.indexOf( QLatin1Char(
'@') );
413 QString localPart = str.left( len );
417 result.truncate( result.length() -
418 len - ( localPart.count( QLatin1Char(
'&') ) * 4 ) );
421 result += QLatin1String(
"<a href=\"mailto:") + str + QLatin1String(
"\">") + str + QLatin1String(
"</a>");
422 x += str.length() - 1;
426 if ( flags & HighlightText ) {
428 if ( !str.isEmpty() ) {
430 x += locator.
mPos - start;
438 if ( flags & ReplaceSmileys ) {
440 exclude << QLatin1String(
"(c)") << QLatin1String(
"(C)") << QLatin1String(
">:-(") << QLatin1String(
">:(") << QLatin1String(
"(B)") << QLatin1String(
"(b)") << QLatin1String(
"(P)") << QLatin1String(
"(p)");
441 exclude << QLatin1String(
"(O)") << QLatin1String(
"(o)") << QLatin1String(
"(D)") << QLatin1String(
"(d)") << QLatin1String(
"(E)") << QLatin1String(
"(e)") << QLatin1String(
"(K)")<< QLatin1String(
"(k)");
442 exclude << QLatin1String(
"(I)") << QLatin1String(
"(i)") << QLatin1String(
"(L)") << QLatin1String(
"(l)") << QLatin1String(
"(8)") << QLatin1String(
"(T)") << QLatin1String(
"(t)") << QLatin1String(
"(G)");
443 exclude << QLatin1String(
"(g)") << QLatin1String(
"(F)") << QLatin1String(
"(f)") << QLatin1String(
"(H)");
444 exclude << QLatin1String(
"8)") << QLatin1String(
"(N)") << QLatin1String(
"(n)") << QLatin1String(
"(Y)") << QLatin1String(
"(y)" )<< QLatin1String(
"(U)") << QLatin1String(
"(u)") << QLatin1String(
"(W)") << QLatin1String(
"(w)");
445 static QString cachedEmoticonsThemeName;
446 if ( cachedEmoticonsThemeName.isEmpty() ) {
447 cachedEmoticonsThemeName = KEmoticons::currentThemeName();
450 sEmoticons->theme( cachedEmoticonsThemeName ).parseEmoticons(
451 result, KEmoticonsTheme::StrictParse | KEmoticonsTheme::SkipHTML, exclude );
459 if ( iconPath.isEmpty() ) {
463 QFile pngFile( iconPath );
464 if ( !pngFile.open( QIODevice::ReadOnly | QIODevice::Unbuffered ) ) {
468 QByteArray ba = pngFile.readAll();
470 return QString::fromLatin1(
"data:image/png;base64,%1" ).arg( QLatin1String(ba.toBase64().constData()) );
481 if ( ch != QLatin1Char(
'/') && ch != QLatin1Char(
'*') && ch != QLatin1Char(
'_') && ch != QLatin1Char(
'-') ) {
486 QRegExp( QString::fromLatin1(
"\\%1((\\w+)([\\s-']\\w+)*( ?[,.:\\?!;])?)\\%2" ).arg( ch ).arg( ch ) );
487 re.setMinimal(
true );
489 int length = re.matchedLength();
495 switch ( ch.toLatin1() ) {
497 return QLatin1String(
"<b>*") + re.cap( 1 ) + QLatin1String(
"*</b>");
499 return QLatin1String(
"<u>_") + re.cap( 1 ) + QLatin1String(
"_</u>");
501 return QLatin1String(
"<i>/") + re.cap( 1 ) + QLatin1String(
"/</i>");
503 return QLatin1String(
"<strike>-") + re.cap( 1 ) + QLatin1String(
"-</strike>");