33#include <QtCore/QCoreApplication>
34#include <QtCore/QFile>
35#include <QtCore/QRegExp>
36#include <QTextDocument>
40using namespace KPIMUtils;
47class KPIMUtils::LinkLocator::Private
56K_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();
102QString LinkLocator::getUrlAndCheckValidHref(
bool *badurl)
117 QChar beforeUrl, afterUrl;
125if ( 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 );
195bool 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:" ) );
226bool 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>");
LinkLocator assists in identifying sections of text that can usefully be converted in hyperlinks in H...
QString mText
The plaintext string being scanned for URLs and email addresses.
int maxAddressLen() const
Returns the current limit on the maximum length of an email address.
void setMaxUrlLen(int length)
Sets the maximum length of URLs that will be matched by getUrl().
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.
~LinkLocator()
Destructor.
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.
QString getEmailAddress()
Attempts to grab an email address.
int mPos
The current scan position.
static QString pngToDataUrl(const QString &iconPath)
Embeds the given PNG image into a data URL.
This file is part of the KDEPIM Utilities library and provides the LinkLocator class.