Changeset 4543

Show
Ignore:
Timestamp:
07/27/06 05:40:15 (2 years ago)
Author:
erijo
Message:

Rewrite of the emoticon system. Haven't done everything I'm planing on
yet, but I wanted to commit this to get some feedback.

First, this commit requires the full cycle:
make -f Makefile.cvs && ./configure && make

Things that's done:

  • Made it possible to use CEmoticons as a singleton by adding CEmoticons::self() and updating all users to use that instead of a "global" variable.
  • Added KDE's emoticons (Debian: in package kdeartwork-emoticons) to the list of available themes (if they exist).
  • Changed CEmoticons::parseMessage to not parse the message more than once and not replace text inside tags or urls. Started adding a "strict mode" (as in kopete). See #646.

Things to be done:

  • Make the "strict mode" better and perhaps configurable (on/off).
  • When text is copied, the smiley should be copied as well (#646).

Fixes #740 and parts of #646.

Location:
trunk/qt-gui/src
Files:
7 modified

Legend:

Unmodified
Added
Removed
  • trunk/qt-gui/src/emoticon.cpp

    r4526 r4543  
    11/* 
    2  * Licq - A ICQ Client for Unix 
    3  * 
    4  * Copyright (C) 2003 Licq developers <licq-devel@lists.sourceforge.net> 
    5  * 
    6  * This program is licensed under the terms found in the LICENSE file. 
    7  * 
    8  * \file support for emoticons themes. Compatible with kopete 0.6 format. 
    9  * \todo lot of improvents (memory vs time) 
    10  */ 
    11  
    12 #include <list> 
     2 * This file is part of Licq, an instant messaging client for UNIX. 
     3 * Copyright (C) 2003-2006 Licq developers 
     4 * 
     5 * Licq is free software; you can redistribute it and/or modify 
     6 * it under the terms of the GNU General Public License as published by 
     7 * the Free Software Foundation; either version 2 of the License, or 
     8 * (at your option) any later version. 
     9 * 
     10 * Licq is distributed in the hope that it will be useful, 
     11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     13 * GNU General Public License for more details. 
     14 * 
     15 * You should have received a copy of the GNU General Public License 
     16 * along with Licq; if not, write to the Free Software 
     17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
     18 */ 
     19 
     20#ifdef HAVE_CONFIG_H 
     21#include "config.h" 
     22#endif 
     23 
     24#ifdef USE_KDE 
     25# include <kapplication.h> 
     26#else 
     27# include <qapplication.h> 
     28#endif 
     29 
    1330#include <qmap.h> 
    1431#include <qdir.h> 
    1532#include <qdom.h> 
    16 #include <qregexp.h> 
     33#include <qstylesheet.h> 
    1734 
    1835#include "licq_log.h" 
     
    2037#include "emoticon.h" 
    2138 
    22 struct node 
    23 { 
    24   QStringList  emoticon; 
    25   QString      file; 
    26   QRegExp      reg; 
     39static const QString DEFAULT_THEME("Default"); 
     40static const QString NO_THEME("None"); 
     41 
     42struct Emoticon 
     43{ 
     44  QString file; 
     45  QString smiley; 
     46  QString escapedSmiley; 
    2747}; 
    2848 
    29 typedef std::list<struct node> node_list_t; 
    30  
    31 /*! private definition of CEmotions */ 
    32 struct Emoticons 
    33 { 
    34   QString basedir;     /* base directory for resourses */ 
    35   QString altbasedir;  /* alternative base directory for resourses */ 
    36   QString theme;       /* current theme */ 
    37  
    38   node_list_t emoticons; 
     49/// Private data and functions for CEmotions 
     50class CEmoticons::Impl 
     51{ 
     52public: 
     53  QStringList basedirs; 
     54  QString currentTheme; 
     55 
     56  // Maps first char in smiley to its Emoticon instance. 
     57  QMap<QChar, QValueList<Emoticon> > emoticons; 
     58 
     59  // Maps an emoticon's filename to a smiley. 
     60  QMap<QString, QString> fileSmiley; 
     61 
     62  QString themeDir(const QString &theme) const; 
    3963}; 
    4064 
    41 CEmoticons::CEmoticons(const char *basedir, const char *altbasedir,  
    42                        const char *theme  ) 
    43 { 
    44   this->data = new struct Emoticons; 
    45   data->basedir = basedir; 
    46   data->altbasedir = altbasedir; 
     65/** 
     66 * @returns the full path to @a theme, or QString::null if no such theme was found. 
     67 */ 
     68QString CEmoticons::Impl::themeDir(const QString &theme) const 
     69{ 
     70  QStringList::ConstIterator basedir = basedirs.begin(); 
     71  for (; basedir != basedirs.end(); basedir++) 
     72  { 
     73    const QString dir = QString("%1/%2").arg(*basedir).arg(theme); 
     74    if (QFile::exists(QString("%1/emoticons.xml").arg(dir))) 
     75      return dir; 
     76  } 
     77 
     78  return QString::null; 
     79} 
     80 
     81 
     82// By making the application object parent, this instance will be 
     83// deleted when the application is closed. 
     84CEmoticons::CEmoticons() 
     85#ifdef USE_KDE 
     86  : QObject(kapp) 
     87#else 
     88  : QObject(qApp) 
     89#endif 
     90{ 
     91  pimpl = new Impl; 
     92  pimpl->currentTheme = NO_THEME; 
    4793} 
    4894 
    4995CEmoticons::~CEmoticons() 
    5096{ 
    51   delete this->data; 
    52 } 
    53  
    54 QStringList CEmoticons::Themes() 
    55 { 
    56   QDir    dir(data->basedir,    "*", 0, QDir::Dirs); 
    57   QDir altdir(data->altbasedir, "*", 0, QDir::Dirs); 
    58   QStringList list = dir.entryList().grep(QRegExp("^[^.].*")) +  
    59                      altdir.entryList().grep(QRegExp("^[^.].*")); 
    60  
    61   // unique  
    62   QString last = ""; 
    63   list.sort(); 
    64   for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) 
    65   { 
    66      // std::cout << "\t" << *it << "\n"; 
    67      if( *it == last ) 
    68         it = list.remove(it); 
    69      else 
    70         last = *it; 
    71   } 
    72    
    73   return list; 
    74 } 
    75  
    76 /*!  
    77  * \returns the real path for the image file in the themedir if exists 
    78  * 
    79  *  \param data     CDT for CEmoticons 
    80  *  \param themedir path to the theme's directory 
    81  *  \param file     file (without extension) to check 
    82  */ 
    83 static QString realFile(const struct Emoticons *data, const QString &themedir, 
    84                      const QString &file) 
    85 { 
    86   QString base; 
    87  
    88   if (file != QString::null) 
    89   { 
    90  
    91     base = themedir + "/" + file; 
    92     if (QFile(base + ".png").exists()) 
    93       base += ".png"; 
    94     else if (QFile(base + ".jpg").exists()) 
    95       base += ".jpg"; 
    96     else if (QFile(base + ".gif").exists()) 
    97       base += ".gif"; 
    98     else 
     97  delete pimpl; 
     98} 
     99 
     100CEmoticons *CEmoticons::m_self = 0L; 
     101CEmoticons* CEmoticons::self() 
     102{ 
     103  if (!m_self) 
     104    m_self = new CEmoticons; 
     105  return m_self; 
     106} 
     107 
     108void CEmoticons::setBasedirs(const QStringList &basedirs) 
     109{ 
     110  pimpl->basedirs = basedirs; 
     111} 
     112 
     113/** 
     114 * In every subdir in every basedir, we check for a file 
     115 * named emoticons.xml, and if we find one, subdir is added 
     116 * to the list of themes. 
     117 */ 
     118QStringList CEmoticons::themes() const 
     119{ 
     120  QStringList themes; 
     121  bool defaultExists = false; 
     122 
     123  QStringList::ConstIterator basedir = pimpl->basedirs.begin(); 
     124  for (; basedir != pimpl->basedirs.end(); basedir++) 
     125  { 
     126    QDir dir(*basedir, QString::null, QDir::Unsorted, QDir::Dirs); 
     127    const QStringList subdirs = dir.entryList(); 
     128 
     129    QStringList::ConstIterator subdir = subdirs.begin(); 
     130    for (; subdir != subdirs.end(); subdir++) 
    99131    { 
    100       gLog.Warn("%sWarning unknown file `%s'\n", L_WARNxSTR, base.ascii()); 
    101       base = QString::null; 
    102     } 
    103   } 
    104  
    105   return base; 
    106 } 
    107  
    108 /*! 
    109  * helper function for #loadTheme 
    110  * 
    111  * \return a list of ascii emoticons 
    112  * 
    113  * <string>:^)</string> 
    114  * <string>:)</string> 
    115  * <string>:-)</string> 
    116  * 
    117  */ 
    118 static QStringList loadStrings(const struct Emoticons *data, QDomNode node, 
    119                                unsigned *n) 
    120 { 
    121   QStringList ret; 
    122  
    123   *n = 0U; 
    124  
    125   for (; !node.isNull() ; node=node.nextSibling()) 
    126   { 
    127     QDomElement emo = node.toElement(); 
    128     if (!emo.isNull() && emo.tagName() == "string") 
    129     { 
    130       if (!emo.text().isEmpty()) 
     132      if (*subdir == NO_THEME) 
     133        continue; // Add this later 
     134 
     135      if (QFile::exists(QString("%1/%2/emoticons.xml").arg(*basedir).arg(*subdir))) 
    131136      { 
    132         (*n) +=1; 
    133         ret << emo.text(); 
     137        if (*subdir == DEFAULT_THEME) 
     138        { 
     139          defaultExists = true; 
     140          continue; // Add this later 
     141        } 
     142 
     143        // Only add unique entires 
     144        if (themes.find(*subdir) == themes.end()) 
     145          themes += *subdir; 
    134146      } 
    135147    } 
    136     else 
    137       gLog.Warn("%sWarning element `%s'", L_WARNxSTR, emo.tagName().ascii()); 
    138   } 
    139  
    140   return ret; 
    141 } 
    142  
    143 static void create_regexp(QStringList &list, QRegExp &reg) 
    144 { 
    145   unsigned n = 0; 
    146   QString s = "("; 
    147  
    148   for (QStringList::Iterator it = list.begin(); it!=list.end(); ++it) 
    149   { 
    150     if (n != 0) 
    151       s += "|"; 
    152 #if QT_VERSION < 0x030100 
    153     // we have to implement the functionality of QRegExp::escape()  
    154     // ourself since qt 3.0.x is missing it. 
    155     // Our goal is to escape all special characters with a backslash: 
    156     // The special characters are $, (, ), *, +, ., ?, [, \, ], ^, {, | and }. 
    157     // The implementation is heavily inspired by QT QRegExp sources ;-) 
    158      
    159     static const char *c = "\\$()*+.?[]^{}|"; 
    160     int i = 0; 
    161     QString tmp = (*it).latin1(); 
    162     while (i < (int)tmp.length()) 
     148  } 
     149 
     150  themes.sort(); 
     151 
     152  if (defaultExists) 
     153    themes.push_front(DEFAULT_THEME); 
     154  themes.push_front(NO_THEME); 
     155 
     156  return themes; 
     157} 
     158 
     159/** 
     160 * @param dir directory to search in 
     161 * @param file filename (without extension) to search for 
     162 * @returns the full filename or QString::null if no such file exists. 
     163 */ 
     164QString fullFilename(const QString &dir, const QString &file) 
     165{ 
     166  const QString base = QString("%1/%2").arg(dir).arg(file); 
     167 
     168  if (QFile::exists(base)) // First try without extension 
     169    return base; 
     170  else if (QFile::exists(base + ".png")) 
     171    return base + ".png"; 
     172  else if (QFile::exists(base + ".jpg")) 
     173    return base + ".jpg"; 
     174  else if (QFile::exists(base + ".gif")) 
     175    return base + ".gif"; 
     176 
     177  gLog.Warn("%sUnknown file '%s'.\n", L_WARNxSTR, base.latin1()); 
     178  return QString::null; 
     179} 
     180 
     181/** 
     182 * Parses the emoticons.xml file in @a dir. 
     183 * @param emoticons  For every smiley, the first character is added as a key 
     184 *                   and its Emoticon instance is appened to the list. 
     185 * @param fileSmiley Maps the filename of an emoticon to a smiley. 
     186 * @returns true on success; otherwise false. 
     187 * 
     188 * A short emoticons.xml file could look like this: 
     189 * <?xml version="1.0"?> 
     190 * <messaging-emoticon-map > 
     191 * 
     192 * <emoticon file="biggrin"> 
     193 * <string>:-&lt;</string> 
     194 * <string>:D</string> 
     195 * </emoticon> 
     196 * 
     197 * <emoticon file="confused"> 
     198 * <string>:-S</string> 
     199 * </emoticon> 
     200 * 
     201 * </messaging-emoticon-map> 
     202 */ 
     203bool parseXml(const QString &dir, QMap<QChar, QValueList<Emoticon> > *emoticons, QMap<QString, QString> *fileSmiley) 
     204{ 
     205  QFile xmlfile(dir + QString::fromLatin1("/emoticons.xml")); 
     206  if (!xmlfile.open(IO_ReadOnly)) 
     207    return false; 
     208 
     209  QDomDocument doc("emoticons"); 
     210  if (!doc.setContent(&xmlfile)) 
     211  { 
     212    xmlfile.close(); 
     213    return false; 
     214  } 
     215  xmlfile.close(); 
     216 
     217  QDomElement docElem = doc.documentElement(); 
     218 
     219  // Walk through all <emoticon> elements 
     220  QDomNode n = docElem.firstChild(); 
     221  for (; !n.isNull(); n = n.nextSibling()) 
     222  { 
     223    QDomElement e = n.toElement(); 
     224    if (!e.isNull() && e.tagName() == QString::fromLatin1("emoticon")) 
    163225    { 
    164       if (strstr(c, tmp.mid(i,1).latin1()) != 0) 
    165         tmp.insert( i++, "\\"); 
    166       i++; 
    167     } 
    168     s += tmp; 
    169 #else 
    170     s += QRegExp::escape(*it); 
    171 #endif 
    172     n++; 
    173   } 
    174   s += ")"; 
    175   reg = QRegExp(s); 
    176 } 
    177  
    178 /*! 
    179  * 
    180  * \param data       CDT for the CEmoticon class 
    181  * \param themedir   path to the theme directory 
    182  * \param list       list where the results are stored 
    183  * 
    184  * \returns the number of loaded emoticons 
    185  */ 
    186 static unsigned loadTheme(const struct Emoticons *data, 
    187                           const QString &themedir, 
    188                           node_list_t &list) 
    189 { 
    190   QDomDocument doc("doc"); 
    191   QFile file(themedir + "/emoticons.xml"); 
    192   unsigned ret = 0; 
    193  
    194   if (file.open(IO_ReadOnly) && doc.setContent(&file)) 
    195   { 
    196     QDomElement elem = doc.documentElement(); 
    197     QDomNode n = elem.firstChild(); 
    198     for (; !n.isNull(); n = n.nextSibling()) 
    199     { 
    200       if (n.isElement()) 
     226      const QString file = fullFilename(dir, e.attribute("file")); 
     227      if (file.isNull()) 
     228        continue; 
     229 
     230      bool first = true; 
     231      QDomNode stringNode = n.firstChild(); 
     232      for (; !stringNode.isNull(); stringNode = stringNode.nextSibling()) 
    201233      { 
    202         elem = n.toElement(); 
    203         if (!elem.isNull() && elem.tagName() == QString::fromLatin1("emoticon")) 
     234        // We extract all smileys from <string> elements (<string>smiley</string>). 
     235        // The first one is added to fileSmiley, so that when the user clicks 
     236        // on the icon, this is the smiley that is inserted into the document. 
     237        // 
     238        // All smileys are then indexed on the first character in the emoticons 
     239        // map. Both the original and the escaped (< replaced with &lt; etc) 
     240        // versions are inserted (if the differ). 
     241        QDomElement string = stringNode.toElement(); 
     242        if (!string.isNull() && string.tagName() == QString::fromLatin1("string")) 
    204243        { 
    205           QString file = elem.attribute("file"); 
    206           QString f=realFile(data,themedir,file); 
    207           if (f != QString::null) 
     244          Emoticon emo; 
     245          emo.smiley = string.text(); 
     246          emo.escapedSmiley = QStyleSheet::escape(emo.smiley); 
     247          emo.file = file; 
     248 
     249          if (first) 
    208250          { 
    209             struct node node; 
    210             unsigned size; 
    211  
    212             node.emoticon = loadStrings(data, n.firstChild(), &size); 
    213         if (size) 
    214         { 
    215           node.file = f; 
    216           create_regexp(node.emoticon, node.reg); 
    217           list.push_back(node); 
    218           ret += size; 
    219         } 
     251            (*fileSmiley)[emo.file] = emo.smiley; 
     252            first = false; 
    220253          } 
     254 
     255          //emoticons[emo.smiley[0]].append(emo); 
     256          //if (emo.smiley != emo.escapedSmiley) 
     257          (*emoticons)[emo.escapedSmiley[0]].append(emo); 
     258        } 
     259        else 
     260        { 
     261          gLog.Warn("%sElement '%s' in '%s' unknown.\n", L_WARNxSTR, 
     262                    string.tagName().latin1(), xmlfile.name().latin1()); 
    221263        } 
    222264      } 
     
    224266  } 
    225267 
    226   file.close(); 
    227   return ret; 
    228 }; 
    229  
    230 int CEmoticons::SetTheme(const char *theme) 
    231 { 
    232   if (strcmp(theme, "None") == 0) 
    233   { 
    234     data->theme = QString("None"); 
    235     return 1; 
    236   } 
    237  
    238   QString szdir1 = data->altbasedir + "/" + theme + "/"; 
    239   QString szdir2 = data->basedir    + "/" + theme + "/"; 
    240   QDir d1(szdir1); 
    241   QDir d2(szdir2); 
    242   node_list_t list; 
    243   int ret = -1; 
    244   unsigned n = 0; 
    245  
    246   if (d1.exists()) 
    247     n = loadTheme(data, szdir1.ascii(), list); 
    248   else if(d2.exists()) 
    249     n = loadTheme(data, szdir2.ascii(), list); 
    250  
    251  
    252   if (n) 
    253   { 
    254     ret = n; 
    255     data->theme = theme; 
    256     data->emoticons = list; 
    257   } 
    258  
    259   return ret; 
    260 } 
    261    
    262 const char *CEmoticons::Theme(void) 
    263 { 
    264   return data->theme == QString::null ? 0 : data->theme.ascii() ; 
    265 } 
    266    
    267 QStringList CEmoticons::fileList() 
    268 { 
    269   node_list_t::iterator iter; 
    270   QStringList ret; 
    271   struct node n; 
    272    
    273   for( iter  = data->emoticons.begin(); 
    274        iter != data->emoticons.end() ; 
    275        iter++ ) 
    276   { 
    277     n = *iter; 
    278     ret << n.file; 
    279   } 
    280  
    281   return ret; 
    282 } 
    283  
    284 QStringList CEmoticons::fileList(const char *theme) 
    285 { 
    286   QString szdir = data->basedir + "/" + theme + "/"; 
    287   QString szaltdir = data->altbasedir + "/" + theme + "/"; 
    288   QStringList ret; 
    289   QDir d(szdir); 
    290   QDir altd(szaltdir); 
    291   node_list_t list; 
    292   node_list_t::iterator iter; 
    293   struct node n; 
    294  
    295   if (d.exists()) 
    296     loadTheme(data, szdir.ascii(), list); 
    297   else if (altd.exists()) 
    298     loadTheme(data, szaltdir.ascii(), list); 
    299  
    300   if (d.exists() || altd.exists()) 
    301   { 
    302     for (iter  = list.begin(); 
    303          iter != list.end(); iter++) 
     268  return true; 
     269} 
     270 
     271QStringList CEmoticons::fileList() const 
     272{ 
     273  return pimpl->fileSmiley.keys(); 
     274} 
     275 
     276// Similar to setTheme(const QString_&) but with the difference that 
     277// here we don't update currentTheme. We're just interested in getting 
     278// the filelist. 
     279QStringList CEmoticons::fileList(const QString &theme) const 
     280{ 
     281  if (theme.isEmpty() || theme == NO_THEME) 
     282    return QStringList(); 
     283 
     284  if (theme == pimpl->currentTheme) 
     285    return fileList(); 
     286 
     287  const QString dir = pimpl->themeDir(theme); 
     288  if (dir.isNull()) 
     289    return QStringList(); 
     290 
     291  QMap<QChar, QValueList<Emoticon> > emoticons; 
     292  QMap<QString, QString> fileSmiley; 
     293 
     294  const bool parsed = parseXml(dir, &emoticons, &fileSmiley); 
     295  if (parsed) 
     296    return fileSmiley.keys(); 
     297 
     298  return QStringList(); 
     299} 
     300 
     301bool CEmoticons::setTheme(const QString &theme) 
     302{ 
     303  if (theme.isEmpty() || theme == NO_THEME) 
     304  { 
     305    pimpl->currentTheme = NO_THEME; 
     306    pimpl->emoticons.clear(); 
     307    pimpl->fileSmiley.clear(); 
     308    return true; 
     309  } 
     310 
     311  if (theme == pimpl->currentTheme) 
     312    return true; 
     313 
     314  const QString dir = pimpl->themeDir(theme); 
     315  if (dir.isNull()) 
     316    return false; 
     317 
     318  QMap<QChar, QValueList<Emoticon> > emoticons; 
     319  QMap<QString, QString> fileSmiley; 
     320 
     321  const bool parsed = parseXml(dir, &emoticons, &fileSmiley); 
     322  if (parsed) 
     323  { 
     324    pimpl->currentTheme = theme; 
     325    pimpl->emoticons = emoticons; 
     326    pimpl->fileSmiley = fileSmiley; 
     327    emit themeChanged(); 
     328  } 
     329 
     330  return parsed; 
     331} 
     332 
     333QString CEmoticons::theme() const 
     334{ 
     335  return pimpl->currentTheme; 
     336} 
     337 
     338QMap<QString, QString> CEmoticons::emoticonsKeys() const 
     339{ 
     340  return pimpl->fileSmiley; 
     341} 
     342 
     343/** 
     344 * @returns true if s1[start:start+s2.length] == s2 
     345 */ 
     346static bool containsAt(const QString &s1, const QString &s2, const uint start) 
     347{ 
     348  const uint end = start + s2.length(); 
     349  if (s1.length() < end) 
     350    return false; 
     351 
     352  for (uint pos = start; pos < end; pos++) 
     353  { 
     354    if (s1[pos] != s2[pos - start]) 
     355      return false; 
     356  } 
     357  return true; 
     358} 
     359 
     360void CEmoticons::parseMessage(QString &message) const 
     361{ 
     362  // Short-circuit if we don't have any emoticons 
     363  if (pimpl->emoticons.isEmpty()) 
     364    return; 
     365 
     366//   qDebug("message b: '%s'", message.latin1()); 
     367 
     368  QChar p(' '), c; // previous and current char 
     369  for (uint pos = 0; pos < message.length(); pos++) 
     370  { 
     371    c = message[pos]; 
     372    if (c == '<') 
    304373    { 
    305       n = *iter; 
    306       ret << n.file; 
     374      // If this is an a tag, skip it completly 
     375      if (message[pos + 1] == 'a') 
     376      { 
     377        const int index = message.find("</a>", pos); 
     378        if (index == -1) 
     379          return; // We're done 
     380//         qDebug(" Skipping '%s', point at '%c'", message.mid(pos, index + 3 - pos).latin1(), message[index + 3].latin1()); 
     381        pos = index + 3; // Fast-forward pos to point at '>' 
     382      } 
     383      else // Skip just the tag 
     384      { 
     385        const int index = message.find('>', pos); 
     386        if (index == -1) 
     387          return; // We're done 
     388//         qDebug(" Skipping '%s', point at '%c'", message.mid(pos, index - pos).latin1(), message[index].latin1()); 
     389        pos = index; // Fast-forward pos to point at '>' 
     390      } 
     391      p = '>'; 
     392      continue; 
    307393    } 
    308   } 
    309  
    310   return ret; 
    311 } 
    312  
    313 QMap<QString, QString> CEmoticons::EmoticonsKeys() 
    314 { 
    315   QMap<QString, QString> map; 
    316    
    317   node_list_t list = data->emoticons; 
    318   node_list_t::iterator iter; 
    319   for (iter = list.begin(); iter != list.end(); ++iter) 
    320   { 
    321     map[(*iter).file] = (*iter).emoticon.first(); 
    322   } 
    323    
    324   return map; 
    325 } 
    326  
    327 void CEmoticons::ParseMessage(QString &msg) 
    328 { 
    329   /** 
    330    * \todo this sucks: solution create a finite state machine to parse 
    331    * the message 
    332    */ 
    333   node_list_t::iterator iter; 
    334   struct node n; 
    335  
    336   if (data->theme != QString::null && data->theme != "None") 
    337   { 
    338     QString r; 
    339     for( iter  = data->emoticons.begin(); 
    340          iter != data->emoticons.end() ; iter++ ) 
     394 
     395    // Only insert smileys after a space, tag or html entitiy 
     396    if (!p.isSpace() && p != '>' && p != ';') 
    341397    { 
    342       n = *iter; 
    343       for ( QStringList::Iterator it = n.emoticon.begin(); 
    344              it != n.emoticon.end(); ++it) 
     398      p = c; 
     399      continue; 
     400    } 
     401 
     402    if (pimpl->emoticons.contains(c)) 
     403    { 
     404      const QValueList<Emoticon> emolist = pimpl->emoticons[c]; 
     405      QValueList<Emoticon>::ConstIterator it = emolist.begin(); 
     406      for (; it != emolist.end(); it++) 
    345407      { 
    346         msg.replace(n.reg," <img src=\""+n.file+"\"/>&nbsp;"); 
     408        const Emoticon &emo = *it; 
     409        if (containsAt(message, emo.escapedSmiley, pos)) 
     410        { 
     411          const QString img = QString::fromLatin1("<img src=\"%1\" />&nbsp;").arg(emo.file); 
     412//           qDebug(" Replacing '%s' with '%s'", message.mid(pos, emo.escapedSmiley.length()).latin1(), img.latin1()); 
     413          message.replace(pos, emo.escapedSmiley.length(), img); 
     414          pos += img.length() - 1; // Point pos at ';' 
     415//           qDebug(" Pointing at '%c'", message[pos].latin1()); 
     416          c = ';'; 
     417          break; 
     418        } 
    347419      } 
    348420    } 
    349   } 
    350 } 
     421 
     422    p = c; 
     423  } 
     424//   qDebug("message a: '%s'", message.latin1()); 
     425} 
     426 
     427#include "emoticon.moc" 
    351428 
    352429#ifdef EMOTICON_TEST_DRIVER 
  • trunk/qt-gui/src/emoticon.h

    <
    r4526 r4543  
    1 #ifndef zE1D68C95080DE073514FA90C07628F92 
    2 #define zE1D68C95080DE073514FA90C07628F92 
     1/* 
     2 * This file is part of Licq, an instant messaging client for UNIX. 
     3 * Copyright (C) 2003-2006 Licq developers 
     4 * 
     5 * Licq is free software; you can redistribute it and/or modify 
     6 * it under the terms of the GNU General Public License as published by 
     7 * the Free Software Foundation; either version 2 of the License, or 
     8 * (at your option) any later version. 
     9 * 
     10 * Licq is distributed in the hope that it will be useful, 
     11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     13 * GNU General Public License for more details. 
     14 * 
     15 * You should have received a copy of the GNU General Public License 
     16 * along with Licq; if not, write to the Free Software 
     17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
     18 */ 
     19 
     20#ifndef EMOTICON_H 
     21#define EMOTICON_H 
    322 
    423#include <qmap.h> 
    5 #include <qstring.h> 
     24#include <qobject.h> 
    625#include <qstringlist.h> 
    726