/*************************************************************************** emoticoncollection.cpp - description ------------------- begin : Mon Apr 15 2002 copyright : (C) 2003 by Michael Curtis email : mdcurtis@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * 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. * * * **************************************************************************/ #include "emoticoncollection.h" #include <qcolor.h> #include <qptrlist.h> #include <qregexp.h> #include <qfile.h> #include <qdom.h> #include <qstringlist.h> #include <kdebug.h> #include <klocale.h> #include <kstddirs.h> #include "emoticon.h" #include "kmessdebug.h" #include "currentaccount.h" #ifdef KMESSDEBUG_EMOTICONS #define KMESSDEBUG_EMOTICONS_GENERAL #endif // Initialize the instance to zero EmoticonCollection* EmoticonCollection::instance_(0); // The constructor EmoticonCollection::EmoticonCollection() : QObject(0, "EmoticonCollection") , QPtrList<Emoticon>() { #ifdef KMESSDEBUG_EMOTICONS_GENERAL kdDebug() << "EmoticonCollection: initializing MSN emoticon list." << endl; #endif Emoticon *emoticon; clear(); setAutoDelete( true ); // MSN6 Smiley definitions added by Mokoshi // put some emoticons in our collection emoticon = new Emoticon( "smile", i18n("Smile") ); emoticon->addText( ":)" ); emoticon->addText( ":-)" ); append( emoticon ); emoticon = new Emoticon( "wink", i18n("Wink") ); emoticon->addText( ";)" ); emoticon->addText( ";-)" ); append( emoticon ); emoticon = new Emoticon( "tongue", i18n("Tongue out") ); emoticon->addText( ":P" ); emoticon->addText( ":p" ); emoticon->addText( ":-P" ); emoticon->addText( ":-p" ); append( emoticon ); emoticon = new Emoticon( "teeth", i18n("Big smile") ); emoticon->addText( ":D" ); emoticon->addText( ":d" ); emoticon->addText( ":-D" ); emoticon->addText( ":-d" ); emoticon->addText( ":>" ); emoticon->addText( ":->" ); emoticon->addText( ":>" ); emoticon->addText( ":->" ); append( emoticon ); emoticon = new Emoticon( "sad", i18n("Sad") ); emoticon->addText( ":(" ); emoticon->addText( ":-(" ); emoticon->addText( ":<" ); emoticon->addText( ":-<" ); emoticon->addText( ":<" ); emoticon->addText( ":-<" ); append( emoticon ); emoticon = new Emoticon( "cry", i18n("Crying") ); emoticon->addText( ":'(" ); append( emoticon ); emoticon = new Emoticon( "angry", i18n("Angry") ); emoticon->addText( ":@" ); emoticon->addText( ":-@" ); append( emoticon ); emoticon = new Emoticon( "confused", i18n("Confused") ); emoticon->addText( ":S" ); emoticon->addText( ":s" ); emoticon->addText( ":-S" ); emoticon->addText( ":-s" ); append( emoticon ); emoticon = new Emoticon( "embarrassed", i18n("Embarrassed") ); emoticon->addText( ":$" ); emoticon->addText( ":-$" ); append( emoticon ); emoticon = new Emoticon( "ugly", i18n("Disappointed") ); emoticon->addText( ":|" ); emoticon->addText( ":-|" ); append( emoticon ); emoticon = new Emoticon( "shade", i18n("Hot") ); emoticon->addText( "(H)" ); emoticon->addText( "(h)" ); append( emoticon ); emoticon = new Emoticon( "baringteeth", i18n("Baring teeth") ); emoticon->addText( "8o|" ); append( emoticon ); emoticon = new Emoticon( "nerd", i18n("Nerd") ); emoticon->addText( "8-|" ); append( emoticon ); emoticon = new Emoticon( "sick", i18n("Sick") ); emoticon->addText( "+o(" ); append( emoticon ); emoticon = new Emoticon( "omg", i18n("Surprised") ); emoticon->addText( ":O" ); emoticon->addText( ":o" ); emoticon->addText( ":-O" ); emoticon->addText( ":-o" ); append( emoticon ); emoticon = new Emoticon( "party", i18n("Party") ); emoticon->addText( "<:o)" ); emoticon->addText( "<:o)" ); append( emoticon ); emoticon = new Emoticon( "sleepy", i18n("Sleepy") ); emoticon->addText( "|-)" ); append( emoticon ); emoticon = new Emoticon( "thinking", i18n("Thinking") ); emoticon->addText( "*-)" ); append( emoticon ); emoticon = new Emoticon( "sshh", i18n("Don't tell anyone") ); emoticon->addText( ":-#" ); append( emoticon ); emoticon = new Emoticon( "secret", i18n("Secret telling") ); emoticon->addText( ":-*" ); append( emoticon ); emoticon = new Emoticon( "eyeroll", i18n("Eye-rolling") ); emoticon->addText( "8-)" ); append( emoticon ); emoticon = new Emoticon( "sarcastic", i18n("Sarcastic") ); emoticon->addText( "^o)" ); append( emoticon ); emoticon = new Emoticon( "huh", i18n("I don't know") ); emoticon->addText( ":^)" ); append( emoticon ); emoticon = new Emoticon( "brb", i18n("Be right back") ); emoticon->addText( "(brb)" ); append( emoticon ); emoticon = new Emoticon( "angel", i18n("Angel") ); emoticon->addText( "(A)" ); emoticon->addText( "(a)" ); append( emoticon ); emoticon = new Emoticon( "dude_hug", i18n("Left hug") ); emoticon->addText( "({)" ); append( emoticon ); emoticon = new Emoticon( "boy", i18n("Boy") ); emoticon->addText( "(Z)" ); emoticon->addText( "(z)" ); append( emoticon ); emoticon = new Emoticon( "love", i18n("Red heart") ); emoticon->addText( "(L)" ); emoticon->addText( "(l)" ); append( emoticon ); emoticon = new Emoticon( "rose", i18n("Red rose") ); emoticon->addText( "(F)" ); emoticon->addText( "(f)" ); append( emoticon ); emoticon = new Emoticon( "thumbs_up", i18n("Thumby up") ); emoticon->addText( "(Y)" ); emoticon->addText( "(y)" ); append( emoticon ); emoticon = new Emoticon( "dog", i18n("Dog face") ); emoticon->addText( "(&)" ); emoticon->addText( "(&)" ); append( emoticon ); emoticon = new Emoticon( "sun", i18n("Sun") ); emoticon->addText( "(#)" ); append( emoticon ); emoticon = new Emoticon( "devil", i18n("Devil") ); emoticon->addText( "(6)" ); append( emoticon ); emoticon = new Emoticon( "girl_hug", i18n("Right hug") ); emoticon->addText( "(})" ); append( emoticon ); emoticon = new Emoticon( "girl", i18n("Girl") ); emoticon->addText( "(X)" ); emoticon->addText( "(x)" ); append( emoticon ); emoticon = new Emoticon( "unlove", i18n("Broken heart") ); emoticon->addText( "(U)" ); emoticon->addText( "(u)" ); append( emoticon ); emoticon = new Emoticon( "wilted_rose", i18n("Wilted rose") ); emoticon->addText( "(W)" ); emoticon->addText( "(w)" ); append( emoticon ); emoticon = new Emoticon( "thumbs_down", i18n("Thumbs down") ); emoticon->addText( "(N)" ); emoticon->addText( "(n)" ); append( emoticon ); emoticon = new Emoticon( "cat", i18n("Cat face") ); emoticon->addText( "(@)" ); append( emoticon ); emoticon = new Emoticon( "moon", i18n("Sleeping half-moon") ); emoticon->addText( "(S)" ); append( emoticon ); emoticon = new Emoticon( "kiss", i18n("Red lips") ); emoticon->addText( "(K)" ); emoticon->addText( "(k)" ); append( emoticon ); emoticon = new Emoticon( "highfive", i18n("Clapping") ); emoticon->addText( "(h5)" ); append( emoticon ); emoticon = new Emoticon( "fingerscrossed", i18n("Crossed fingers") ); emoticon->addText( "(yn)" ); append( emoticon ); emoticon = new Emoticon( "automobile", i18n("Auto") ); emoticon->addText( "(au)" ); append( emoticon ); emoticon = new Emoticon( "airplane", i18n("Airplane") ); emoticon->addText( "(ap)" ); append( emoticon ); emoticon = new Emoticon( "turtle", i18n("Turtle") ); emoticon->addText( "(tu)" ); append( emoticon ); emoticon = new Emoticon( "snail", i18n("Snail") ); emoticon->addText( "(sn)" ); append( emoticon ); emoticon = new Emoticon( "sheep", i18n("Black sheep") ); emoticon->addText( "(bah)" ); append( emoticon ); emoticon = new Emoticon( "goat", i18n("Goat") ); emoticon->addText( "(nah)" ); append( emoticon ); emoticon = new Emoticon( "bat", i18n("Vampire bat") ); emoticon->addText( ":[" ); emoticon->addText( ":-[" ); append( emoticon ); emoticon = new Emoticon( "pizza", i18n("Pizza") ); emoticon->addText( "(pi)" ); append( emoticon ); emoticon = new Emoticon( "beer", i18n( "Beer mug" ) ); emoticon->addText( "(B)" ); emoticon->addText( "(b)" ); append( emoticon ); emoticon = new Emoticon( "cocktail", i18n("Martini glass") ); emoticon->addText( "(D)" ); emoticon->addText( "(d)" ); append( emoticon ); emoticon = new Emoticon( "cup", i18n("Coffee cup") ); emoticon->addText( "(C)" ); emoticon->addText( "(c)" ); append( emoticon ); emoticon = new Emoticon( "cake", i18n("Birthday cake") ); emoticon->addText( "(^)" ); append( emoticon ); emoticon = new Emoticon( "plate", i18n("Plate") ); emoticon->addText( "(pl)" ); append( emoticon ); emoticon = new Emoticon( "bowl", i18n("Bowl") ); emoticon->addText( "(||)" ); append( emoticon ); emoticon = new Emoticon( "star", i18n("Star") ); emoticon->addText( "(*)" ); append( emoticon ); emoticon = new Emoticon( "rainbow", i18n("Rainbow") ); emoticon->addText( "(R)" ); emoticon->addText( "(r)" ); append( emoticon ); emoticon = new Emoticon( "storm", i18n("Stormy cloud") ); emoticon->addText( "(st)" ); append( emoticon ); emoticon = new Emoticon( "lightning", i18n("Lightning") ); emoticon->addText( "(li)" ); append( emoticon ); emoticon = new Emoticon( "umbrella", i18n("Umbrella") ); emoticon->addText( "(um)" ); append( emoticon ); emoticon = new Emoticon( "island", i18n("Island with a palm tree") ); emoticon->addText( "(ip)" ); append( emoticon ); emoticon = new Emoticon( "phone", i18n("Telephone receiver") ); emoticon->addText( "(T)" ); emoticon->addText( "(t)" ); append( emoticon ); emoticon = new Emoticon( "mobilephone", i18n("Mobile Phone") ); emoticon->addText( "(mp)" ); append( emoticon ); emoticon = new Emoticon( "envelope", i18n("Email") ); emoticon->addText( "(E)" ); emoticon->addText( "(e)" ); append( emoticon ); emoticon = new Emoticon( "clock", i18n("Clock") ); emoticon->addText( "(O)" ); emoticon->addText( "(o)" ); emoticon->addText( "(0)" ); // a zero, not O append( emoticon ); emoticon = new Emoticon( "camera", i18n("Camera") ); emoticon->addText( "(P)" ); emoticon->addText( "(p)" ); append( emoticon ); emoticon = new Emoticon( "film", i18n("Filmstrip") ); emoticon->addText( "(~)" ); append( emoticon ); emoticon = new Emoticon( "note", i18n("Note") ); emoticon->addText( "(8)" ); append( emoticon ); emoticon = new Emoticon( "handcuffs", i18n("Handcuffs") ); emoticon->addText( "(%)" ); append( emoticon ); emoticon = new Emoticon( "money", i18n("Money") ); emoticon->addText( "(mo)" ); append( emoticon ); emoticon = new Emoticon( "lightbulb", i18n("Light bulb") ); emoticon->addText( "(I)" ); emoticon->addText( "(i)" ); append( emoticon ); emoticon = new Emoticon( "cigarette", i18n("Cigarrette") ); emoticon->addText( "(ci)" ); append( emoticon ); emoticon = new Emoticon( "soccer", i18n("Soccer ball") ); emoticon->addText( "(so)" ); append( emoticon ); emoticon = new Emoticon( "present", i18n("Gift with a bow") ); emoticon->addText( "(G)" ); emoticon->addText( "(g)" ); append( emoticon ); emoticon = new Emoticon( "gameconsole", i18n("X-Box") ); emoticon->addText( "(xx)" ); append( emoticon ); emoticon = new Emoticon( "computer", i18n("Computer") ); emoticon->addText( "(co)" ); append( emoticon ); emoticon = new Emoticon( "messenger", i18n("KMess Icon") ); emoticon->addText( "(M)" ); emoticon->addText( "(m)" ); append( emoticon ); /* // Trick to generate the emoticons.xml file: for( emoticon = first(); emoticon != 0; emoticon = next() ) { kdDebug() << "<emoticon file=\"" << emoticon->getFileTitle() << "\">" << endl; const QStringList &codes = emoticon->getTextList(); for( QStringList::ConstIterator it = codes.begin(); it != codes.end(); ++it ) { kdDebug() << "\t<string>" << QString( *it ).replace("&", "&").replace("<", "<").replace(">", ">") << "</string>" << endl; } kdDebug() << "</emoticon>\n" << endl; } */ // Build a search-pattern for the regexp. updateCache(); } // The destructor EmoticonCollection::~EmoticonCollection() { } // Delete the instance of the emoticon collection void EmoticonCollection::destroy() { delete instance_; instance_ = 0; } // Return the search pattern to find emoticons in the text const QRegExp& EmoticonCollection::getPattern() const { return pattern_; } // Return one replacement code for the given emoticons const QString & EmoticonCollection::getReplacement(const QString &code, bool small) const { if( small ) { return smallReplacements_[code]; } else { return largeReplacements_[code]; } } // Return the replacement codes for all emoticons const QMap<QString,QString>& EmoticonCollection::getReplacements(bool small) const { if( small ) { return smallReplacements_; } else { return largeReplacements_; } } // Return a singleton instance of the emoticon collection EmoticonCollection* EmoticonCollection::instance() { // If the instance is null, create a new emoticon collection and return that. if ( instance_ == 0 ) { instance_ = new EmoticonCollection(); connect( CurrentAccount::instance(), SIGNAL(changedEmoticonSettings() ), instance_, SLOT(slotChangedEmoticonSettings() ) ); } return instance_; } // Replace the emoticons in the text with the urls to the emoticon pngs. void EmoticonCollection::parseEmoticons( QString &text, bool small ) { int pos = 0; const QMap<QString,QString> &replacements = getReplacements(small); while( pos != -1 ) { pos = pattern_.search(text, pos); if( pos != -1 ) { // Found a emoticon QString code = text.mid(pos, pattern_.matchedLength()); if( replacements.contains(code) ) { QString replacement = replacements[code]; text = text.replace(pos, pattern_.matchedLength(), replacement); pos += replacement.length(); } else { // No replacement found, preserve original code, move to next pos++; } } } } // The emoticon theme has changed void EmoticonCollection::slotChangedEmoticonSettings() { #ifdef KMESSDEBUG_EMOTICONS_GENERAL kdDebug() << "EmoticonCollection: emoticon style changed, updating emoticons." << endl; #endif // First try to open the theme. // This collection won't be replaced with the emoticon definitions from the theme file, // since KMess is an MSN-only client and other clients don't support these special emoticons. // Instead, the current emoticon collection is kept, and the code tries to detect which // filenames the theme uses for certain emoticon codes. Emoticon *emoticon; // Find out which theme is used now. const QString ¤tStyle = CurrentAccount::instance()->getEmoticonStyle(); QString xmlFileName = KGlobal::dirs()->findResource("emoticons", currentStyle + "/emoticons.xml"); QFile xmlFile(xmlFileName); // Try to read the XML if( ! xmlFile.open(IO_ReadOnly) ) { kdWarning() << "EmoticonCollection: could not open '" << xmlFileName << "'" << endl; } else { // Try to parse XML QDomDocument xml; QString xmlError; if( ! xml.setContent(xmlFile.readAll(), false, &xmlError) ) { kdWarning() << "EmoticonCollection: parsing '" << xmlFileName << "' failed: " << xmlError << endl; xmlFile.close(); } else { // Load the codes in a map for efficient lookup QMap<QString,QString> themeFiles; // Loop through all <emoticon> tags QDomNode xmlChild = xml.documentElement().firstChild(); while( ! xmlChild.isNull() ) { // <emoticon> tag found QDomElement emoticonTag = xmlChild.toElement(); if( ! emoticonTag.isNull() && emoticonTag.tagName() == "emoticon" ) { // Loop through all <string> tags QString emoticonFile = emoticonTag.attribute("file"); QDomNode xmlChild2 = emoticonTag.firstChild(); while( ! xmlChild2.isNull() ) { // <string> tag found QDomElement stringTag = xmlChild2.toElement(); if( ! stringTag.isNull() && stringTag.tagName() == "string" ) { // Insert code in the map themeFiles.insert( stringTag.text(), emoticonFile ); } xmlChild2 = xmlChild2.nextSibling(); } } xmlChild = xmlChild.nextSibling(); } // Loop through all current emoticons. for( emoticon = last(); emoticon != 0; emoticon = prev() ) { // Loop through all codes of the emoticon bool codeFound = false; const QStringList &codes = emoticon->getTextList(); for( QStringList::ConstIterator it = codes.begin(); it != codes.end(); ++it ) { const QString &code = *it; // If the emoticon uses that code, set it. if( themeFiles.contains(code) ) { emoticon->setFileTitle( themeFiles[code] ); // already calls updatePath() codeFound = true; break; } } // If the code was not found, the file still needs to be updated manually. if(! codeFound) { emoticon->updatePath(); } } // Update the search-replace cache updateCache(); // Each emoticon is updated, the function is done now. return; } } // Tell each emoticon to update their path // This is only called when the emoticon set can't be loaded. for( emoticon = last(); emoticon != 0; emoticon = prev() ) { emoticon->updatePath(); } // Update the search-replace cache updateCache(); } // Build the search-replace cache void EmoticonCollection::updateCache() { QStringList::const_iterator it; Emoticon *emoticon; QString emoticonPattern; // Remove all replacements, some emoticons are no longer valid smallReplacements_.clear(); largeReplacements_.clear(); for( emoticon = first(); emoticon != 0; emoticon = next() ) { // Traverse all codes of the emoticon if( emoticon->isValid() ) { const QStringList &codes = emoticon->getTextList(); for( it = codes.begin(); it != codes.end(); ++it) { // Add the emoticon to the replacement list. const QString &code = *it; largeReplacements_.insert(code, emoticon->getHtml(false)); smallReplacements_.insert(code, emoticon->getHtml(true)); // Add the emoticon pattern to the regexp // The code is already escaped if( emoticonPattern.length() > 0 ) { emoticonPattern += "|"; } emoticonPattern += QRegExp::escape(code); } } } // Cache the pattern. pattern_.setPattern(emoticonPattern); } #include "emoticoncollection.moc"