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 == QLatin1Char(
'[') ) {
120 afterUrl = QLatin1Char(
']');
121 }
else if ( beforeUrl == QLatin1Char(
'<') ) {
122 afterUrl = QLatin1Char(
'>');
123 }
else if ( beforeUrl == QLatin1Char(
'>') ) {
124 afterUrl = QLatin1Char(
'<');
125 }
else if ( beforeUrl == QLatin1Char(
'"') ) {
126 afterUrl = QLatin1Char(
'"');
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 << QLatin1Char(
'.') << QLatin1Char(
',') << QLatin1Char(
':') << QLatin1Char(
'!') << QLatin1Char(
'?') << QLatin1Char(
')') << QLatin1Char(
'>');
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 static const QString allowedSpecialChars = QLatin1String(
".!#$%&'*+-/=?^_`{|}~" );
186 ( allowedSpecialChars.indexOf(
mText[
mPos-1] ) != -1 ) ) ) {
192 ( ch == QLatin1Char(
'h') && (
mText.mid(
mPos, 7 ) == QLatin1String(
"http://" ) ||
193 mText.mid(
mPos, 8 ) == QLatin1String(
"https://" ) ) ) ||
194 ( ch == QLatin1Char(
'v') &&
mText.mid(
mPos, 6 ) == QLatin1String(
"vnc://" ) ) ||
195 ( ch == QLatin1Char(
'f') && (
mText.mid(
mPos, 7 ) == QLatin1String(
"fish://" ) ||
196 mText.mid(
mPos, 6 ) == QLatin1String(
"ftp://" ) ||
197 mText.mid(
mPos, 7 ) == QLatin1String(
"ftps://" ) ) ) ||
198 ( ch == QLatin1Char(
's') && (
mText.mid(
mPos, 7 ) == QLatin1String(
"sftp://" ) ||
199 mText.mid(
mPos, 6 ) == QLatin1String(
"smb://" ) ) ) ||
200 ( ch == QLatin1Char(
'm') &&
mText.mid(
mPos, 7 ) == QLatin1String(
"mailto:" ) ) ||
201 ( ch == QLatin1Char(
'w') &&
mText.mid(
mPos, 4 ) == QLatin1String(
"www." ) ) ||
202 ( ch == QLatin1Char(
'f') && (
mText.mid(
mPos, 4 ) == QLatin1String(
"ftp." ) ||
203 mText.mid(
mPos, 7 ) == QLatin1String(
"file://" ) ) )||
204 ( ch == QLatin1Char(
'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 static const QString allowedSpecialChars = QLatin1String(
".!#$%&'*+-/=?^_`{|}~" );
235 int start =
mPos - 1;
236 while ( start >= 0 &&
mText[start].unicode() < 128 &&
237 (
mText[start].isLetterOrNumber() ||
238 mText[start] == QLatin1Char(
'@') ||
239 allowedSpecialChars.indexOf(
mText[start] ) != -1 ) ) {
240 if (
mText[start] == QLatin1Char(
'@') ) {
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() ||
259 mText[end] == QLatin1Char(
'@') ||
260 mText[end] == QLatin1Char(
'.') ||
261 mText[end] == QLatin1Char(
'-') ) ) {
262 if (
mText[end] == QLatin1Char(
'@') ) {
265 if (
mText[end] == QLatin1Char(
'.') ) {
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;
304 for ( locator.
mPos = 0, x = 0; locator.
mPos < (
int)locator.
mText.length();
305 locator.
mPos++, x++ ) {
307 if ( flags & PreserveSpaces ) {
308 if ( ch == QLatin1Char(
' ') ) {
309 if ( locator.
mPos + 1 < locator.
mText.length() ) {
310 if ( locator.
mText[locator.
mPos + 1] != QLatin1Char(
' ') ) {
313 const bool endOfLine = locator.
mText[locator.
mPos + 1] == QLatin1Char(
'\n');
314 if ( !startOfLine && !endOfLine ) {
315 result += QLatin1Char(
' ');
317 result += QLatin1String(
" ");
322 while ( locator.
mPos < locator.
mText.length() && locator.
mText[locator.
mPos] == QLatin1Char(
' ') ) {
323 result += QLatin1String(
" ");
334 result += QLatin1String(
" ");
341 }
else if ( ch == QLatin1Char(
'\t') ) {
343 result += QLatin1String(
" ");
345 }
while ( ( x & 7 ) != 0 );
351 if ( ch == QLatin1Char(
'\n') ) {
352 result += QLatin1String(
"<br />\n");
359 if ( ch == QLatin1Char(
'&') ) {
360 result += QLatin1String(
"&");
361 }
else if ( ch == QLatin1Char(
'"') ) {
362 result += QLatin1String(
""");
363 }
else if ( ch == QLatin1Char(
'<') ) {
364 result += QLatin1String(
"<");
365 }
else if ( ch == QLatin1Char(
'>') ) {
366 result += QLatin1String(
">");
368 const int start = locator.
mPos;
369 if ( !( flags & IgnoreUrls ) ) {
371 if ( !str.isEmpty() ) {
373 if ( str.left( 4 ) == QLatin1String(
"www.") ) {
374 hyperlink = QLatin1String(
"http://") + str;
375 }
else if ( str.left( 4 ) == QLatin1String(
"ftp.") ) {
376 hyperlink = QLatin1String(
"ftp://") + str;
381 result += QLatin1String(
"<a href=\"") + hyperlink + QLatin1String(
"\">") + Qt::escape( str ) + QLatin1String(
"</a>");
382 x += locator.
mPos - start;
386 if ( !str.isEmpty() ) {
388 int len = str.indexOf( QLatin1Char(
'@') );
389 QString localPart = str.left( len );
393 result.truncate( result.length() -
394 len - ( localPart.count( QLatin1Char(
'&') ) * 4 ) );
397 result += QLatin1String(
"<a href=\"mailto:") + str + QLatin1String(
"\">") + str + QLatin1String(
"</a>");
398 x += str.length() - 1;
402 if ( flags & HighlightText ) {
404 if ( !str.isEmpty() ) {
406 x += locator.
mPos - start;
414 if ( flags & ReplaceSmileys ) {
416 exclude << QLatin1String(
"(c)") << QLatin1String(
"(C)") << QLatin1String(
">:-(") << QLatin1String(
">:(") << QLatin1String(
"(B)") << QLatin1String(
"(b)") << QLatin1String(
"(P)") << QLatin1String(
"(p)");
417 exclude << QLatin1String(
"(O)") << QLatin1String(
"(o)") << QLatin1String(
"(D)") << QLatin1String(
"(d)") << QLatin1String(
"(E)") << QLatin1String(
"(e)") << QLatin1String(
"(K)")<< QLatin1String(
"(k)");
418 exclude << QLatin1String(
"(I)") << QLatin1String(
"(i)") << QLatin1String(
"(L)") << QLatin1String(
"(l)") << QLatin1String(
"(8)") << QLatin1String(
"(T)") << QLatin1String(
"(t)") << QLatin1String(
"(G)");
419 exclude << QLatin1String(
"(g)") << QLatin1String(
"(F)") << QLatin1String(
"(f)") << QLatin1String(
"(H)");
420 exclude << QLatin1String(
"8)") << QLatin1String(
"(N)") << QLatin1String(
"(n)") << QLatin1String(
"(Y)") << QLatin1String(
"(y)" )<< QLatin1String(
"(U)") << QLatin1String(
"(u)") << QLatin1String(
"(W)") << QLatin1String(
"(w)");
421 static QString cachedEmoticonsThemeName;
422 if ( cachedEmoticonsThemeName.isEmpty() ) {
423 cachedEmoticonsThemeName = KEmoticons::currentThemeName();
426 sEmoticons->theme( cachedEmoticonsThemeName ).parseEmoticons(
427 result, KEmoticonsTheme::StrictParse | KEmoticonsTheme::SkipHTML, exclude );
435 if ( iconPath.isEmpty() ) {
439 QFile pngFile( iconPath );
440 if ( !pngFile.open( QIODevice::ReadOnly | QIODevice::Unbuffered ) ) {
444 QByteArray ba = pngFile.readAll();
446 return QString::fromLatin1(
"data:image/png;base64,%1" ).arg( QLatin1String(ba.toBase64().constData()) );
457 if ( ch != QLatin1Char(
'/') && ch != QLatin1Char(
'*') && ch != QLatin1Char(
'_') && ch != QLatin1Char(
'-') ) {
462 QRegExp( QString::fromLatin1(
"\\%1((\\w+)([\\s-']\\w+)*( ?[,.:\\?!;])?)\\%2" ).arg( ch ).arg( ch ) );
463 re.setMinimal(
true );
465 int length = re.matchedLength();
471 switch ( ch.toLatin1() ) {
473 return QLatin1String(
"<b>*") + re.cap( 1 ) + QLatin1String(
"*</b>");
475 return QLatin1String(
"<u>_") + re.cap( 1 ) + QLatin1String(
"_</u>");
477 return QLatin1String(
"<i>/") + re.cap( 1 ) + QLatin1String(
"/</i>");
479 return QLatin1String(
"<strike>-") + re.cap( 1 ) + QLatin1String(
"-</strike>");
static QString pngToDataUrl(const QString &iconPath)
Embeds the given PNG image into a data URL.
int mPos
The current scan position.
~LinkLocator()
Destructor.
QString mText
The plaintext string being scanned for URLs and email addresses.
QString getUrl()
Attempts to grab a URL starting at the current scan position.
void setMaxAddressLen(int length)
Sets the maximum length of email addresses that will be matched by getEmailAddress().
QString highlightedText()
Highlight text according to bold, /italic/ and underlined markup.
QString getEmailAddress()
Attempts to grab an email address.
LinkLocator assists in identifying sections of text that can usefully be converted in hyperlinks in H...
void setMaxUrlLen(int length)
Sets the maximum length of URLs that will be matched by getUrl().
This file is part of the KDEPIM Utilities library and provides the LinkLocator class.
int maxUrlLen() const
Returns the current limit on the maximum length of a URL.
static QString convertToHtml(const QString &plainText, int flags=0, int maxUrlLen=4096, int maxAddressLen=255)
Converts plaintext into html.
int maxAddressLen() const
Returns the current limit on the maximum length of an email address.