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>";
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.