KUtils
kemoticonstheme.cpp
Go to the documentation of this file.
00001 /********************************************************************************** 00002 * Copyright (C) 2008 by Carlo Segato <brandon.ml@gmail.com> * 00003 * Copyright (c) 2002-2003 by Stefan Gehn <metz@gehn.net> * 00004 * Kopete (c) 2002-2008 by the Kopete developers <kopete-devel@kde.org> * 00005 * Copyright (c) 2005 by Engin AYDOGAN <engin@bzzzt.biz> * 00006 * * 00007 * This library is free software; you can redistribute it and/or * 00008 * modify it under the terms of the GNU Lesser General Public * 00009 * License as published by the Free Software Foundation; either * 00010 * version 2.1 of the License, or (at your option) any later version. * 00011 * * 00012 * This library is distributed in the hope that it will be useful, * 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 00015 * Lesser General Public License for more details. * 00016 * * 00017 * You should have received a copy of the GNU Lesser General Public * 00018 * License along with this library. If not, see <http://www.gnu.org/licenses/>.* 00019 * * 00020 **********************************************************************************/ 00021 00022 #include "kemoticonstheme.h" 00023 #include "kemoticons.h" 00024 00025 #include <QtCore/QFileInfo> 00026 #include <QtCore/QDir> 00027 #include <QtGui/QTextDocument> 00028 #include <QtCore/QtAlgorithms> 00029 00030 #include <kio/netaccess.h> 00031 #include <kstandarddirs.h> 00032 #include <kdebug.h> 00033 00034 class KEmoticonsTheme::KEmoticonsThemeData : public QSharedData 00035 { 00036 public: 00037 KEmoticonsThemeData(); 00038 ~KEmoticonsThemeData(); 00039 KEmoticonsProvider *provider; 00040 }; 00041 00042 00043 KEmoticonsTheme::KEmoticonsThemeData::KEmoticonsThemeData() 00044 { 00045 provider = 0; 00046 } 00047 00048 KEmoticonsTheme::KEmoticonsThemeData::~KEmoticonsThemeData() 00049 { 00050 // delete provider; 00051 } 00052 00053 KEmoticonsTheme::KEmoticonsTheme() 00054 { 00055 d = new KEmoticonsThemeData; 00056 } 00057 00058 KEmoticonsTheme::KEmoticonsTheme(const KEmoticonsTheme &ket) 00059 { 00060 d = ket.d; 00061 } 00062 00063 KEmoticonsTheme::KEmoticonsTheme(KEmoticonsProvider *p) 00064 { 00065 d = new KEmoticonsThemeData; 00066 d->provider = p; 00067 } 00068 00069 KEmoticonsTheme::~KEmoticonsTheme() 00070 { 00071 } 00072 00073 bool KEmoticonsTheme::loadTheme(const QString &path) 00074 { 00075 if (!d->provider) { 00076 return false; 00077 } 00078 00079 return d->provider->loadTheme(path); 00080 } 00081 00082 bool KEmoticonsTheme::removeEmoticon(const QString &emo) 00083 { 00084 if (!d->provider) { 00085 return false; 00086 } 00087 00088 return d->provider->removeEmoticon(emo); 00089 } 00090 00091 bool KEmoticonsTheme::addEmoticon(const QString &emo, const QString &text, KEmoticonsProvider::AddEmoticonOption option) 00092 { 00093 if (!d->provider) { 00094 return false; 00095 } 00096 00097 return d->provider->addEmoticon(emo, text, option); 00098 } 00099 00100 void KEmoticonsTheme::save() 00101 { 00102 if (!d->provider) { 00103 return; 00104 } 00105 00106 d->provider->save(); 00107 } 00108 00109 QString KEmoticonsTheme::themeName() const 00110 { 00111 if (!d->provider) { 00112 return QString(); 00113 } 00114 00115 return d->provider->themeName(); 00116 } 00117 00118 void KEmoticonsTheme::setThemeName(const QString &name) 00119 { 00120 if (!d->provider) { 00121 return; 00122 } 00123 00124 d->provider->setThemeName(name); 00125 } 00126 00127 QString KEmoticonsTheme::themePath() const 00128 { 00129 if (!d->provider) { 00130 return QString(); 00131 } 00132 00133 return d->provider->themePath(); 00134 } 00135 00136 QString KEmoticonsTheme::fileName() const 00137 { 00138 if (!d->provider) { 00139 return QString(); 00140 } 00141 00142 return d->provider->fileName(); 00143 } 00144 00145 QHash<QString, QStringList> KEmoticonsTheme::emoticonsMap() const 00146 { 00147 if (!d->provider) { 00148 return QHash<QString, QStringList>(); 00149 } 00150 00151 return d->provider->emoticonsMap(); 00152 } 00153 00154 void KEmoticonsTheme::createNew() 00155 { 00156 if (!d->provider) { 00157 return; 00158 } 00159 00160 d->provider->createNew(); 00161 } 00162 00163 QString KEmoticonsTheme::parseEmoticons(const QString &text, ParseMode mode, const QStringList &exclude) const 00164 { 00165 QList<Token> tokens = tokenize(text, mode | SkipHTML); 00166 if (tokens.isEmpty() && !text.isEmpty()) 00167 return text; 00168 00169 QString result; 00170 00171 foreach(const Token &token , tokens) { 00172 switch (token.type) { 00173 case Text: 00174 result += token.text; 00175 break; 00176 case Image: 00177 if (!exclude.contains(token.text)) { 00178 result += token.picHTMLCode; 00179 } else { 00180 result += token.text; 00181 } 00182 break; 00183 default: 00184 kWarning() << "Unknown token type. Something's broken."; 00185 break; 00186 } 00187 } 00188 return result; 00189 } 00190 00191 bool EmoticonCompareEscaped( const KEmoticonsProvider::Emoticon &s1, const KEmoticonsProvider::Emoticon &s2) 00192 { 00193 return s1.matchTextEscaped.length()>s2.matchTextEscaped.length(); 00194 } 00195 bool EmoticonCompare( const KEmoticonsProvider::Emoticon &s1, const KEmoticonsProvider::Emoticon &s2) 00196 { 00197 return s1.matchText.length()>s2.matchText.length(); 00198 } 00199 00200 00201 QList<KEmoticonsTheme::Token> KEmoticonsTheme::tokenize(const QString &message, ParseMode mode) const 00202 { 00203 if (!d->provider) { 00204 return QList<KEmoticonsTheme::Token>(); 00205 } 00206 00207 if (!(mode & (StrictParse | RelaxedParse))) { 00208 //if none of theses two mode are selected, use the mode from the config 00209 mode |= KEmoticons::parseMode(); 00210 } 00211 00212 QList<Token> result; 00213 00214 /* previous char, in the firs iteration assume that it is space since we want 00215 * to let emoticons at the beginning, the very first previous QChar must be a space. */ 00216 QChar p = ' '; 00217 QChar c; /* current char */ 00218 QChar n; 00219 00220 /* This is the EmoticonNode container, it will represent each matched emoticon */ 00221 typedef QPair<KEmoticonsProvider::Emoticon, int> EmoticonNode; 00222 QList<EmoticonNode> foundEmoticons; 00223 /* First-pass, store the matched emoticon locations in foundEmoticons */ 00224 QList<KEmoticonsProvider::Emoticon> emoticonList; 00225 QList<KEmoticonsProvider::Emoticon>::const_iterator it; 00226 int pos; 00227 00228 bool inHTMLTag = false; 00229 bool inHTMLLink = false; 00230 bool inHTMLEntity = false; 00231 QString needle; // search for this 00232 00233 for (pos = 0; pos < message.length(); ++pos) { 00234 c = message[pos]; 00235 00236 if (mode & SkipHTML) { // Shall we skip HTML ? 00237 if (!inHTMLTag) { // Are we already in an HTML tag ? 00238 if (c == '<') { // If not check if are going into one 00239 inHTMLTag = true; // If we are, change the state to inHTML 00240 p = c; 00241 continue; 00242 } 00243 } else { // We are already in a HTML tag 00244 if (c == '>') { // Check if it ends 00245 inHTMLTag = false; // If so, change the state 00246 00247 if (p == 'a') { 00248 inHTMLLink = false; 00249 } 00250 } else if (c == 'a' && p == '<') { // check if we just entered an achor tag 00251 inHTMLLink = true; // don't put smileys in urls 00252 } 00253 p = c; 00254 continue; 00255 } 00256 00257 if (!inHTMLEntity) { // are we 00258 if (c == '&') { 00259 inHTMLEntity = true; 00260 } 00261 } 00262 } 00263 00264 if (inHTMLLink) { // i can't think of any situation where a link address might need emoticons 00265 p = c; 00266 continue; 00267 } 00268 00269 if ((mode & StrictParse) && !p.isSpace() && p != '>') { // '>' may mark the end of an html tag 00270 p = c; 00271 continue; 00272 } /* strict requires space before the emoticon */ 00273 00274 if (d->provider->emoticonsIndex().contains(c)) { 00275 emoticonList = d->provider->emoticonsIndex().value(c); 00276 if (mode & SkipHTML) 00277 qSort(emoticonList.begin(),emoticonList.end(),EmoticonCompareEscaped); 00278 else 00279 qSort(emoticonList.begin(),emoticonList.end(),EmoticonCompare); 00280 bool found = false; 00281 for (it = emoticonList.constBegin(); it != emoticonList.constEnd(); ++it) { 00282 // If this is an HTML, then search for the HTML form of the emoticon. 00283 // For instance <o) => >o) 00284 needle = (mode & SkipHTML) ? (*it).matchTextEscaped : (*it).matchText; 00285 if ((pos == message.indexOf(needle, pos))) { 00286 if (mode & StrictParse) { 00287 /* check if the character after this match is space or end of string*/ 00288 if (message.length() > pos + needle.length()) { 00289 n = message[pos + needle.length()]; 00290 //<br/> marks the end of a line 00291 if (n != '<' && !n.isSpace() && !n.isNull() && n != '&') { 00292 break; 00293 } 00294 } 00295 } 00296 /* Perfect match */ 00297 foundEmoticons.append(EmoticonNode((*it), pos)); 00298 found = true; 00299 /* Skip the matched emoticon's matchText */ 00300 pos += needle.length() - 1; 00301 break; 00302 } 00303 00304 if (found) { 00305 break; 00306 } 00307 } 00308 00309 if (!found) { 00310 if (inHTMLEntity) { 00311 // If we are in an HTML entitiy such as > 00312 int htmlEnd = message.indexOf(';', pos); 00313 // Search for where it ends 00314 if (htmlEnd == -1) { 00315 // Apparently this HTML entity isn't ended, something is wrong, try skip the '&' 00316 // and continue 00317 kDebug() << "Broken HTML entity, trying to recover."; 00318 inHTMLEntity = false; 00319 pos++; 00320 } else { 00321 pos = htmlEnd; 00322 inHTMLEntity = false; 00323 } 00324 } 00325 } 00326 } /* else no emoticons begin with this character, so don't do anything */ 00327 p = c; 00328 } 00329 00330 /* if no emoticons found just return the text */ 00331 if (foundEmoticons.isEmpty()) { 00332 result.append(Token(Text, message)); 00333 return result; 00334 } 00335 00336 /* Second-pass, generate tokens based on the matches */ 00337 00338 pos = 0; 00339 int length; 00340 00341 for (int i = 0; i < foundEmoticons.size(); ++i) { 00342 EmoticonNode itFound = foundEmoticons.at(i); 00343 needle = (mode & SkipHTML) ? itFound.first.matchTextEscaped : itFound.first.matchText; 00344 00345 if ((length = (itFound.second - pos))) { 00346 result.append(Token(Text, message.mid(pos, length))); 00347 result.append(Token(Image, itFound.first.matchTextEscaped, itFound.first.picPath, itFound.first.picHTMLCode)); 00348 pos += length + needle.length(); 00349 } else { 00350 result.append(Token(Image, itFound.first.matchTextEscaped, itFound.first.picPath, itFound.first.picHTMLCode)); 00351 pos += needle.length(); 00352 } 00353 } 00354 00355 if (message.length() - pos) { // if there is remaining regular text 00356 result.append(Token(Text, message.mid(pos))); 00357 } 00358 00359 return result; 00360 } 00361 00362 bool KEmoticonsTheme::isNull() const 00363 { 00364 return d->provider ? false : true; 00365 } 00366 00367 KEmoticonsTheme& KEmoticonsTheme::operator=(const KEmoticonsTheme &ket) 00368 { 00369 if (d == ket.d) { 00370 return *this; 00371 } 00372 00373 d = ket.d; 00374 return *this; 00375 } 00376 00377 // kate: space-indent on; indent-width 4; replace-tabs on;
KDE 4.6 API Reference