33 #include <QtCore/QCoreApplication>
34 #include <QtCore/QFile>
35 #include <QtCore/QRegExp>
36 #include <QtGui/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;
117 if ( beforeUrl ==
'(' ) {
119 }
else 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 if ( url.length() > 1 ) {
160 QList<QChar> wordBoundaries;
161 wordBoundaries <<
'.' <<
',' <<
':' <<
'!' <<
'?';
162 if ( wordBoundaries.contains( url.at( url.length() - 1 ) ) ) {
172 bool LinkLocator::atUrl()
const
176 const QString allowedSpecialChars = QString(
".!#$%&'*+-/=?^_`{|}~" );
182 ( allowedSpecialChars.indexOf(
mText[
mPos-1] ) != -1 ) ) ) {
188 ( ch ==
'h' && (
mText.mid(
mPos, 7 ) == QLatin1String(
"http://") ||
189 mText.mid(
mPos, 8 ) == QLatin1String(
"https://") ) ) ||
190 ( ch ==
'v' &&
mText.mid(
mPos, 6 ) == QLatin1String(
"vnc://") ) ||
191 ( ch ==
'f' && (
mText.mid(
mPos, 7 ) == QLatin1String(
"fish://") ||
192 mText.mid(
mPos, 6 ) == QLatin1String(
"ftp://") ||
193 mText.mid(
mPos, 7 ) == QLatin1String(
"ftps://") ) ) ||
194 ( ch ==
's' && (
mText.mid(
mPos, 7 ) == QLatin1String(
"sftp://") ||
195 mText.mid(
mPos, 6 ) == QLatin1String(
"smb://") ) ) ||
196 ( ch ==
'm' &&
mText.mid(
mPos, 7 ) == QLatin1String(
"mailto:") ) ||
197 ( ch ==
'w' &&
mText.mid(
mPos, 4 ) == QLatin1String(
"www.") ) ||
198 ( ch ==
'f' && (
mText.mid(
mPos, 4 ) == QLatin1String(
"ftp.") ||
199 mText.mid(
mPos, 7 ) == QLatin1String(
"file://" ) ) )||
200 ( ch ==
'n' &&
mText.mid(
mPos, 5 ) == QLatin1String(
"news:") );
203 bool LinkLocator::isEmptyUrl(
const QString &url )
const
205 return url.isEmpty() ||
206 url == QLatin1String(
"http://") ||
207 url == QLatin1String(
"https://") ||
208 url == QLatin1String(
"fish://") ||
209 url == QLatin1String(
"ftp://") ||
210 url == QLatin1String(
"ftps://") ||
211 url == QLatin1String(
"sftp://") ||
212 url == QLatin1String(
"smb://") ||
213 url == QLatin1String(
"vnc://") ||
214 url == QLatin1String(
"mailto") ||
215 url == QLatin1String(
"www") ||
216 url == QLatin1String(
"ftp") ||
217 url == QLatin1String(
"news") ||
218 url == QLatin1String(
"news://");
228 const QString allowedSpecialChars = QString(
".!#$%&'*+-/=?^_`{|}~" );
231 int start =
mPos - 1;
232 while ( start >= 0 &&
mText[start].unicode() < 128 &&
233 (
mText[start].isLetterOrNumber() ||
234 mText[start] ==
'@' ||
235 allowedSpecialChars.indexOf(
mText[start] ) != -1 ) ) {
236 if (
mText[start] ==
'@' ) {
243 while ( ( start <
mPos ) && !
mText[start].isLetterOrNumber() ) {
246 if ( start ==
mPos ) {
251 int dotPos = INT_MAX;
253 while ( end < (
int)
mText.length() &&
254 (
mText[end].isLetterOrNumber() ||
257 mText[end] ==
'-' ) ) {
258 if (
mText[end] ==
'@' ) {
261 if (
mText[end] ==
'.' ) {
262 dotPos = qMin( dotPos, end );
267 while ( ( end >
mPos ) && !
mText[end - 1].isLetterOrNumber() ) {
273 if ( dotPos >= end ) {
280 address =
mText.mid( start, end - start );
288 int maxUrlLen,
int maxAddressLen )
295 QString result( (QChar*)0, (
int)locator.
mText.length() * 2 );
298 bool startOfLine =
true;
301 for ( locator.
mPos = 0, x = 0; locator.
mPos < (
int)locator.
mText.length();
302 locator.
mPos++, x++ ) {
304 if ( flags & PreserveSpaces ) {
306 if ( locator.
mPos + 1 < locator.
mText.length() ) {
307 if ( locator.
mText[locator.
mPos + 1] !=
' ' ) {
310 const bool endOfLine = locator.
mText[locator.
mPos + 1] ==
'\n';
311 if ( !startOfLine && !endOfLine ) {
319 while ( locator.
mPos < locator.
mText.length() && locator.
mText[locator.
mPos] ==
' ' ) {
338 }
else if ( ch ==
'\t' ) {
344 while ( ( x & 7 ) != 0 );
351 result +=
"<br />\n";
360 }
else if ( ch ==
'"' ) {
362 }
else if ( ch ==
'<' ) {
364 }
else if ( ch ==
'>' ) {
367 const int start = locator.
mPos;
368 if ( !( flags & IgnoreUrls ) ) {
370 if ( !str.isEmpty() ) {
372 if ( str.left( 4 ) ==
"www." ) {
373 hyperlink =
"http://" + str;
374 }
else if ( str.left( 4 ) ==
"ftp." ) {
375 hyperlink =
"ftp://" + str;
380 result +=
"<a href=\"" + hyperlink +
"\">" + Qt::escape( str ) +
"</a>";
381 x += locator.
mPos - start;
385 if ( !str.isEmpty() ) {
387 int len = str.indexOf(
'@' );
388 QString localPart = str.left( len );
392 result.truncate( result.length() -
393 len - ( localPart.count(
'&' ) * 4 ) );
396 result +=
"<a href=\"mailto:" + str +
"\">" + str +
"</a>";
397 x += str.length() - 1;
401 if ( flags & HighlightText ) {
403 if ( !str.isEmpty() ) {
405 x += locator.
mPos - start;
413 if ( flags & ReplaceSmileys ) {
415 exclude <<
"(c)" <<
"(C)" <<
">:-(" <<
">:(" <<
"(B)" <<
"(b)" <<
"(P)" <<
"(p)";
416 exclude <<
"(O)" <<
"(o)" <<
"(D)" <<
"(d)" <<
"(E)" <<
"(e)" <<
"(K)" <<
"(k)";
417 exclude <<
"(I)" <<
"(i)" <<
"(L)" <<
"(l)" <<
"(8)" <<
"(T)" <<
"(t)" <<
"(G)";
418 exclude <<
"(g)" <<
"(F)" <<
"(f)" <<
"(H)";
419 exclude <<
"8)" <<
"(N)" <<
"(n)" <<
"(Y)" <<
"(y)" <<
"(U)" <<
"(u)" <<
"(W)" <<
"(w)";
420 static QString cachedEmoticonsThemeName;
421 if ( cachedEmoticonsThemeName.isEmpty() ) {
422 cachedEmoticonsThemeName = KEmoticons::currentThemeName();
425 sEmoticons->theme( cachedEmoticonsThemeName ).parseEmoticons(
426 result, KEmoticonsTheme::StrictParse | KEmoticonsTheme::SkipHTML, exclude );
434 if ( iconPath.isEmpty() ) {
438 QFile pngFile( iconPath );
439 if ( !pngFile.open( QIODevice::ReadOnly | QIODevice::Unbuffered ) ) {
443 QByteArray ba = pngFile.readAll();
445 return QString::fromLatin1(
"data:image/png;base64,%1" ).arg( ba.toBase64().constData() );
456 if ( ch !=
'/' && ch !=
'*' && ch !=
'_' ) {
461 QRegExp( QString(
"\\%1((\\w+)([\\s-']\\w+)*( ?[,.:\\?!;])?)\\%2" ).arg( ch ).arg( ch ) );
464 int length = re.matchedLength();
470 switch ( ch.toLatin1() ) {
472 return "<b>" + re.cap( 1 ) +
"</b>";
474 return "<u>" + re.cap( 1 ) +
"</u>";
476 return "<i>" + re.cap( 1 ) +
"</i>";