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;
111 QChar beforeUrl, afterUrl;
119 if ( beforeUrl ==
'[' ) {
121 }
else if ( beforeUrl ==
'<' ) {
123 }
else if ( beforeUrl ==
'>' ) {
125 }
else if ( beforeUrl ==
'"' ) {
132 while ( (
mPos < (
int)
mText.length() ) &&
134 ( ( afterUrl.isNull() && !
mText[
mPos].isSpace() ) ||
135 ( !afterUrl.isNull() &&
mText[
mPos] != afterUrl ) ) ) {
146 if ( isEmptyUrl( url ) || ( url.length() >
maxUrlLen() ) ) {
159 QList<QChar> wordBoundaries;
160 wordBoundaries <<
'.' <<
',' <<
':' <<
'!' <<
'?' <<
')' <<
'>';
161 if ( url.length() > 1 ) {
163 if ( wordBoundaries.contains( url.at( url.length() - 1 ) ) ) {
169 }
while( url.length() > 1 );
176 bool LinkLocator::atUrl()
const
180 const QString allowedSpecialChars = QString(
".!#$%&'*+-/=?^_`{|}~" );
186 ( allowedSpecialChars.indexOf(
mText[
mPos-1] ) != -1 ) ) ) {
192 ( ch ==
'h' && (
mText.mid(
mPos, 7 ) == QLatin1String(
"http://" ) ||
193 mText.mid(
mPos, 8 ) == QLatin1String(
"https://" ) ) ) ||
194 ( ch ==
'v' &&
mText.mid(
mPos, 6 ) == QLatin1String(
"vnc://" ) ) ||
195 ( ch ==
'f' && (
mText.mid(
mPos, 7 ) == QLatin1String(
"fish://" ) ||
196 mText.mid(
mPos, 6 ) == QLatin1String(
"ftp://" ) ||
197 mText.mid(
mPos, 7 ) == QLatin1String(
"ftps://" ) ) ) ||
198 ( ch ==
's' && (
mText.mid(
mPos, 7 ) == QLatin1String(
"sftp://" ) ||
199 mText.mid(
mPos, 6 ) == QLatin1String(
"smb://" ) ) ) ||
200 ( ch ==
'm' &&
mText.mid(
mPos, 7 ) == QLatin1String(
"mailto:" ) ) ||
201 ( ch ==
'w' &&
mText.mid(
mPos, 4 ) == QLatin1String(
"www." ) ) ||
202 ( ch ==
'f' && (
mText.mid(
mPos, 4 ) == QLatin1String(
"ftp." ) ||
203 mText.mid(
mPos, 7 ) == QLatin1String(
"file://" ) ) )||
204 ( ch ==
'n' &&
mText.mid(
mPos, 5 ) == QLatin1String(
"news:" ) );
207 bool LinkLocator::isEmptyUrl(
const QString &url )
const
209 return url.isEmpty() ||
210 url == QLatin1String(
"http://" ) ||
211 url == QLatin1String(
"https://" ) ||
212 url == QLatin1String(
"fish://" ) ||
213 url == QLatin1String(
"ftp://" ) ||
214 url == QLatin1String(
"ftps://" ) ||
215 url == QLatin1String(
"sftp://" ) ||
216 url == QLatin1String(
"smb://" ) ||
217 url == QLatin1String(
"vnc://" ) ||
218 url == QLatin1String(
"mailto" ) ||
219 url == QLatin1String(
"www" ) ||
220 url == QLatin1String(
"ftp" ) ||
221 url == QLatin1String(
"news" ) ||
222 url == QLatin1String(
"news://" );
232 const QString allowedSpecialChars = QString(
".!#$%&'*+-/=?^_`{|}~" );
235 int start =
mPos - 1;
236 while ( start >= 0 &&
mText[start].unicode() < 128 &&
237 (
mText[start].isLetterOrNumber() ||
238 mText[start] ==
'@' ||
239 allowedSpecialChars.indexOf(
mText[start] ) != -1 ) ) {
240 if (
mText[start] ==
'@' ) {
247 while ( ( start <
mPos ) && !
mText[start].isLetterOrNumber() ) {
250 if ( start ==
mPos ) {
255 int dotPos = INT_MAX;
257 while ( end < (
int)
mText.length() &&
258 (
mText[end].isLetterOrNumber() ||
261 mText[end] ==
'-' ) ) {
262 if (
mText[end] ==
'@' ) {
265 if (
mText[end] ==
'.' ) {
266 dotPos = qMin( dotPos, end );
271 while ( ( end >
mPos ) && !
mText[end - 1].isLetterOrNumber() ) {
277 if ( dotPos >= end ) {
284 address =
mText.mid( start, end - start );
292 int maxUrlLen,
int maxAddressLen )
299 QString result( (QChar*)0, (
int)locator.
mText.length() * 2 );
302 bool startOfLine =
true;
305 for ( locator.
mPos = 0, x = 0; locator.
mPos < (
int)locator.
mText.length();
306 locator.
mPos++, x++ ) {
308 if ( flags & PreserveSpaces ) {
310 if ( locator.
mPos + 1 < locator.
mText.length() ) {
311 if ( locator.
mText[locator.
mPos + 1] !=
' ' ) {
314 const bool endOfLine = locator.
mText[locator.
mPos + 1] ==
'\n';
315 if ( !startOfLine && !endOfLine ) {
323 while ( locator.
mPos < locator.
mText.length() && locator.
mText[locator.
mPos] ==
' ' ) {
342 }
else if ( ch ==
'\t' ) {
346 }
while ( ( x & 7 ) != 0 );
353 result +=
"<br />\n";
362 }
else if ( ch ==
'"' ) {
364 }
else if ( ch ==
'<' ) {
366 }
else if ( ch ==
'>' ) {
369 const int start = locator.
mPos;
370 if ( !( flags & IgnoreUrls ) ) {
372 if ( !str.isEmpty() ) {
374 if ( str.left( 4 ) ==
"www." ) {
375 hyperlink =
"http://" + str;
376 }
else if ( str.left( 4 ) ==
"ftp." ) {
377 hyperlink =
"ftp://" + str;
382 result +=
"<a href=\"" + hyperlink +
"\">" + Qt::escape( str ) +
"</a>";
383 x += locator.
mPos - start;
387 if ( !str.isEmpty() ) {
389 int len = str.indexOf(
'@' );
390 QString localPart = str.left( len );
394 result.truncate( result.length() -
395 len - ( localPart.count(
'&' ) * 4 ) );
398 result +=
"<a href=\"mailto:" + str +
"\">" + str +
"</a>";
399 x += str.length() - 1;
403 if ( flags & HighlightText ) {
405 if ( !str.isEmpty() ) {
407 x += locator.
mPos - start;
415 if ( flags & ReplaceSmileys ) {
417 exclude <<
"(c)" <<
"(C)" <<
">:-(" <<
">:(" <<
"(B)" <<
"(b)" <<
"(P)" <<
"(p)";
418 exclude <<
"(O)" <<
"(o)" <<
"(D)" <<
"(d)" <<
"(E)" <<
"(e)" <<
"(K)" <<
"(k)";
419 exclude <<
"(I)" <<
"(i)" <<
"(L)" <<
"(l)" <<
"(8)" <<
"(T)" <<
"(t)" <<
"(G)";
420 exclude <<
"(g)" <<
"(F)" <<
"(f)" <<
"(H)";
421 exclude <<
"8)" <<
"(N)" <<
"(n)" <<
"(Y)" <<
"(y)" <<
"(U)" <<
"(u)" <<
"(W)" <<
"(w)";
422 static QString cachedEmoticonsThemeName;
423 if ( cachedEmoticonsThemeName.isEmpty() ) {
424 cachedEmoticonsThemeName = KEmoticons::currentThemeName();
427 sEmoticons->theme( cachedEmoticonsThemeName ).parseEmoticons(
428 result, KEmoticonsTheme::StrictParse | KEmoticonsTheme::SkipHTML, exclude );
436 if ( iconPath.isEmpty() ) {
440 QFile pngFile( iconPath );
441 if ( !pngFile.open( QIODevice::ReadOnly | QIODevice::Unbuffered ) ) {
445 QByteArray ba = pngFile.readAll();
447 return QString::fromLatin1(
"data:image/png;base64,%1" ).arg( ba.toBase64().constData() );
458 if ( ch !=
'/' && ch !=
'*' && ch !=
'_' && ch !=
'-' ) {
463 QRegExp( QString(
"\\%1((\\w+)([\\s-']\\w+)*( ?[,.:\\?!;])?)\\%2" ).arg( ch ).arg( ch ) );
464 re.setMinimal(
true );
466 int length = re.matchedLength();
472 switch ( ch.toLatin1() ) {
474 return "<b>*" + re.cap( 1 ) +
"*</b>";
476 return "<u>_" + re.cap( 1 ) +
"_</u>";
478 return "<i>/" + re.cap( 1 ) +
"/</i>";
480 return "<strike>-" + re.cap( 1 ) +
"-</strike>";