-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi
At the moment there are some problems with the smily system. 1. bug 1224036 - > does not work in emoticons.xml http://sourceforge.net/tracker/index.php?func=detail&aid=1224036&group_id=254&atid=100254 2. at the moment there is simply a replacement of the smily symbols with the image even in urls 3. if I have 100 smilies, there will be 100 times "search and replace" for one message To solve these problems I wrote this patch. It arranges the smilies internally in a tree. Urls are handled as protected content where no replacement of smily should be done. For example if there are the smilies :) and :-) the tree would look like : / \ ) - | ) So I search in the message only the ':' symbol and look whether is is part of the smily. If it is a smily and not part of an url it is replaces by an image. Additionally the bug mentioned above is fixed here. regars, rsLeo -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (GNU/Linux) Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org iD8DBQFESTwmuFcJvHsEu9gRAsFIAJsEqn6jWES4NP1dzh8xGXoYMshihgCfUbRV 2MXGuRsvAnTOJH4wYxyARnU= =crJ0 -----END PGP SIGNATURE-----
--- licq/plugins/qt-gui/src/emoticon.h 2006-03-10 12:24:35.000000000 +0100 +++ licq_patched/plugins/qt-gui/src/emoticon.h 2006-04-09 17:17:44.000000000 +0200 @@ -4,6 +4,7 @@ #include <qmap.h> #include <qstring.h> #include <qstringlist.h> +#include "emotnode.h" class CEmoticons { --- licq/plugins/qt-gui/src/emoticon.cpp 2006-03-10 12:24:35.000000000 +0100 +++ licq_patched/plugins/qt-gui/src/emoticon.cpp 2006-04-09 17:19:27.000000000 +0200 @@ -23,7 +23,6 @@ { QStringList emoticon; QString file; - QRegExp reg; }; typedef std::list<struct node> node_list_t; @@ -36,6 +35,7 @@ QString theme; /* current theme */ node_list_t emoticons; + EmotNode *emoticonsTree; }; CEmoticons::CEmoticons(const char *basedir, const char *altbasedir, @@ -44,10 +44,12 @@ this->data = new struct Emoticons; data->basedir = basedir; data->altbasedir = altbasedir; + data->emoticonsTree = NULL; } CEmoticons::~CEmoticons() { + delete this->data->emoticonsTree; delete this->data; } @@ -140,52 +142,20 @@ return ret; } -static void create_regexp(QStringList &list, QRegExp ®) -{ - unsigned n = 0; - QString s = "("; - - for (QStringList::Iterator it = list.begin(); it!=list.end(); ++it) - { - if (n != 0) - s += "|"; -#if QT_VERSION < 0x030100 - // we have to implement the functionality of QRegExp::escape() - // ourself since qt 3.0.x is missing it. - // Our goal is to escape all special characters with a backslash: - // The special characters are $, (, ), *, +, ., ?, [, \, ], ^, {, | and }. - // The implementation is heavily inspired by QT QRegExp sources ;-) - - static const char *c = "\\$()*+.?[]^{}|"; - int i = 0; - QString tmp = (*it).latin1(); - while (i < (int)tmp.length()) - { - if (strstr(c, tmp.mid(i,1).latin1()) != 0) - tmp.insert( i++, "\\"); - i++; - } - s += tmp; -#else - s += QRegExp::escape(*it); -#endif - n++; - } - s += ")"; - reg = QRegExp(s); -} /*! * * \param data CDT for the CEmoticon class * \param themedir path to the theme directory * \param list list where the results are stored + * \param emoticonsTree tree where the smilies are stored * * \returns the number of loaded emoticons */ static unsigned loadTheme(const struct Emoticons *data, const QString &themedir, - node_list_t &list) + node_list_t &list, + EmotNode *emoticonsTree = NULL) { QDomDocument doc("doc"); QFile file(themedir + "/emoticons.xml"); @@ -210,13 +180,14 @@ unsigned size; node.emoticon = loadStrings(data, n.firstChild(), &size); - if (size) - { - node.file = f; - create_regexp(node.emoticon, node.reg); - list.push_back(node); - ret += size; - } + if (size) + { + node.file = f; + list.push_back(node); + if (emoticonsTree != NULL) + emoticonsTree->insert(node.emoticon, f, true); + ret += size; + } } } } @@ -240,13 +211,14 @@ QDir d1(szdir1); QDir d2(szdir2); node_list_t list; + EmotNode *emoTree = new EmotNode(); int ret = -1; unsigned n = 0; if (d1.exists()) - n = loadTheme(data, szdir1.ascii(), list); + n = loadTheme(data, szdir1.ascii(), list, emoTree); else if(d2.exists()) - n = loadTheme(data, szdir2.ascii(), list); + n = loadTheme(data, szdir2.ascii(), list, emoTree); if (n) @@ -254,7 +226,10 @@ ret = n; data->theme = theme; data->emoticons = list; + data->emoticonsTree = emoTree; } + else + delete emoTree; return ret; } @@ -326,26 +301,9 @@ void CEmoticons::ParseMessage(QString &msg) { - /** - * \todo this sucks: solution create a finite state machine to parse - * the message - */ - node_list_t::iterator iter; - struct node n; - if (data->theme != QString::null && data->theme != "None") { - QString r; - for( iter = data->emoticons.begin(); - iter != data->emoticons.end() ; iter++ ) - { - n = *iter; - for ( QStringList::Iterator it = n.emoticon.begin(); - it != n.emoticon.end(); ++it) - { - msg.replace(n.reg," <img src=\""+n.file+"\"/> "); - } - } + msg = data->emoticonsTree->replaceSmilies(msg); } } --- licq/plugins/qt-gui/src/Makefile.am 2006-03-10 12:24:35.000000000 +0100 +++ licq_patched/plugins/qt-gui/src/Makefile.am 2006-04-09 20:33:12.578165592 +0200 @@ -21,7 +21,7 @@ mmlistview.h mmsenddlg.h userinfodlg.h usereventdlg.h keyrequestdlg.h \ jfcstyle.h usercodec.h reqauthdlg.h licqdialog.h userselectdlg.h \ editfilelistdlg.h emoticon.h catdlg.h phonedlg.h ownermanagerdlg.h \ - gpgkeyselect.h gpgkeymanager.h licqkimiface.h kimiface.h + gpgkeyselect.h gpgkeymanager.h licqkimiface.h kimiface.h emotnode.h licq_gui = adduserdlg.cpp authuserdlg.cpp awaymsgdlg.cpp \ refusedlg.cpp chatdlg.cpp editgrp.cpp editfile.cpp eventdesc.cpp \ @@ -34,7 +34,7 @@ mmsenddlg.cpp userinfodlg.cpp usereventdlg.cpp keyrequestdlg.cpp \ jfcstyle.cpp usercodec.cpp reqauthdlg.cpp licqdialog.cpp userselectdlg.cpp \ editfilelistdlg.cpp emoticon.cpp catdlg.cpp phonedlg.cpp \ - ownermanagerdlg.cpp gpgkeyselect.cpp gpgkeymanager.cpp + ownermanagerdlg.cpp gpgkeyselect.cpp gpgkeymanager.cpp emotnode.cpp licq_qt_gui_la_SOURCES = $(licq_gui) licq_kde_gui_la_SOURCES = $(licq_gui) wrap_kde_malloc.cpp \ --- /dev/null 2005-08-09 09:06:25.000000000 +0200 +++ licq_patched/plugins/qt-gui/src/emotnode.h 2006-04-06 00:23:05.000000000 +0200 @@ -0,0 +1,71 @@ +#ifndef EMOTNODE_H +#define EMOTNODE_H + +#include <qstring.h> +#include <qstringlist.h> + +#include <string> +#include <map> + + +class EmotNode{ +private: + struct file_child_t { + QString m_file; //every node can have a filename (image) + EmotNode* mp_child; //every node may link to other nodes + + }; + + typedef std::map<QChar,file_child_t> node_t; + typedef std::map<QChar,file_child_t>::const_iterator node_iter_t; + + node_t m_node; //m_node[symbol_x], m_node[symbol_y] are all nodes + //example with smily :-) + //m_node[:].mp_child[-].mp_child[)].m_file = smily.jpg + + /** + * Check whether at a given position in a message a smily can be found. + * @param msg message with smilies + * @param spos position of possible smily + * @param endPos returns the end position of a smily if one is found + * @return If a smily was found then the filename is returned. Else NULL ist returned. + */ + QString searchSmilyInTree(QString msg, int spos, int &endPos); + + /** + * Parse the msg for contents which should not be replaced with smilies + * @param msg + */ + QString createSpecialContentSafe(QString msg); + +public: + EmotNode(); + EmotNode(QString icons, QString filename); + + ~EmotNode(); + + /** + * Insert one smily with all his representatives. + * @param iconList list of representatives of the smily + * @param filename name of image file + * @param doEncodeSmily (optional, default=false) encode the smilies e.g. >:) will be >:) + */ + void insert(QStringList iconList, QString filename, bool doEncode); + + /** + * Insert a new smily in the tree. + * @param icons the test of the smily + * @param filename the image name of the smily + * @param depth used internally because of recursive behaviour + */ + void insert(QString icons, QString filename, unsigned int depth = 0); + + /** + * Replace all smilies in a message and return the resulting message. + * @param msg message with smilies to replace + * @return message with replaced smilies + */ + QString replaceSmilies(QString msg); +}; + +#endif --- /dev/null 2005-08-09 09:06:25.000000000 +0200 +++ licq_patched/plugins/qt-gui/src/emotnode.cpp 2006-04-09 19:06:43.549017928 +0200 @@ -0,0 +1,221 @@ +/* Licq - A ICQ Client for Unix + + Copyright (C) 2006 Martin Garbe <[EMAIL PROTECTED]> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + * \file Build a tree with smily symbols. Every node has one symbol of + * a smily and maybe a filename to an image. There is a filename + * if the symbols on the path from the root node to this node + * builds a smily. + */ + +#include <qregexp.h> +#include <qstylesheet.h> + +#include "licq_log.h" + +#include "emotnode.h" + + +EmotNode::EmotNode() +{ + +} + + +/* + * Insert one smily with all his representatives. + */ +void EmotNode::insert(QStringList iconList, QString filename, bool doEncodeSmily = false) +{ + for (QStringList::Iterator iter = iconList.begin(); iter != iconList.end(); iter++) + { + if (doEncodeSmily) + { + insert(QStyleSheet::escape(*iter), filename); + } + else + { + insert(*iter, filename); + } + } +} + + +/* + * Insert a new smily in the tree. + */ +void EmotNode::insert(QString icons, QString filename, unsigned int depth ) +{ + QChar sym = icons.at(depth); + node_iter_t iter = m_node.find(sym); + if (iter != m_node.end()) + { + //symbol already exists here + if ((depth+1) == icons.length()) + { + //if end of symbol is reached then insert file + m_node[sym].m_file = filename; + } + else + { + //end of symbol not reached -> go deeper + m_node[sym].mp_child->insert(icons, filename, depth+1); + } + } + else + { + //add symbol here + m_node[sym].mp_child = new EmotNode(); + if ((depth+1) == icons.length()) + { + //end of smily reached, insert image here + m_node[sym].m_file = filename; + } + else + { + m_node[sym].m_file = ""; + m_node[sym].mp_child->insert(icons, filename, depth+1); + } + } +} + + +// +//Check whether at a given position in a message a smily can be found. +// +QString EmotNode::searchSmilyInTree(QString msg, int spos, int &endPos) +{ + QChar curSym = msg.at(spos); + node_iter_t iter = m_node.find(curSym); + if (iter != m_node.end()) + { + //found symbol in node, so go deeper + QString s = m_node[curSym].mp_child->searchSmilyInTree(msg, spos+1, endPos); + if (s == NULL) + { + //is here any file for the smily? + if (m_node[curSym].m_file.length() != 0) + { + endPos = spos; + return m_node[curSym].m_file; + } + } + else + { + return s; + } + } + return NULL; +} + + +/* + * Replace all smilies in a message and return the resulting message. + */ +QString EmotNode::replaceSmilies(QString msg) +{ + int pos = 0; + int tmppos; + int max = msg.length()+1; + int spos = -1; //smallest position + int nextToCopyPos = 0; //saves the next position to copy from msg to newMsg + QString contentSafe; + QString newMsg = ""; + + contentSafe = createSpecialContentSafe(msg); + + while (spos != max) + { + spos = max; + //search for the first possible smily symbol + for ( node_iter_t i = m_node.begin(); i != m_node.end(); ++i) + { + tmppos = contentSafe.find(i->first, pos); + if (tmppos != -1 && tmppos < spos) + { + //found a position, remember it and try to find one more left + spos = tmppos; + } + } + if (spos != max) + { + //check if at this position is a smily in msg + int endPos = -1; + QString f = searchSmilyInTree(contentSafe, spos, endPos); + if (f != NULL) + { + //replace + newMsg.append(msg.mid(nextToCopyPos, spos-nextToCopyPos) + " <img src=\"" + f + "\"/> "); + nextToCopyPos = endPos + 1; + pos = endPos + 1; + } + else + { + //it's beginning with a smily symbol but it's no smily + pos = spos + 1; + } + } + } + + if (nextToCopyPos != contentSafe.length()) + { + newMsg.append(msg.right(contentSafe.length() - nextToCopyPos)); + } + + return newMsg; +} + + +/* + * Parse the msg for content which should not be replaced with smilies + */ +QString EmotNode::createSpecialContentSafe(QString msg) +{ + QString contentSafe = msg; + QString fill; + int bpos = 0; + int epos = 0; + QRegExp reURL("(\\b|^)((https?|ftp)://([-a-z0-9]+(:[-a-z0-9]+)?@)?[-a-z0-9.]+[-a-z0-9](:[0-9]+)?(/([-a-z0-9%{}|\\\\^~`;/?:@=&$_.+!*'(),]|\\[|\\])*)?)"); + reURL.setMinimal(false); + reURL.setCaseSensitive(false); + do + { + bpos = contentSafe.find(reURL); + if (bpos != -1) + { + //find end position + epos = bpos + reURL.cap(2).length(); + //replace content area with spaces (mark as content area) + contentSafe = contentSafe.replace(bpos, epos - bpos, fill.fill(' ', epos - bpos)); + } + } + while (bpos != -1); + + return contentSafe; +} + + +EmotNode::~EmotNode() +{ + for ( node_iter_t i = m_node.begin(); i != m_node.end(); ++i) + { + delete(i->second.mp_child); + } +} + +