Logo Search packages:      
Sourcecode: kmess version File versions

emoticontheme.cpp

/***************************************************************************
                          emoticontheme.cpp - holds a collection of emoticons
                             -------------------
    begin                : Tue April 10 2007
    copyright            : (C) 2007 by Valerio Pilo
    email                : valerio@kmess.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "emoticontheme.h"

#include "utils/kmessconfig.h"
#include "utils/kmessshared.h"
#include "kmessdebug.h"

#include <QDir>
#include <QDomDocument>
#include <QFile>
#include <QTextCodec>
#include <QTextDocument>

#include <KLocale>
#include <KMessageBox>
#include <KStandardDirs>


#ifdef KMESSDEBUG_EMOTICONS
  #define KMESSDEBUG_EMOTICON_THEMES
#endif



/**
 * Constructor
 */
00044 EmoticonTheme::EmoticonTheme()
  : QObject()
  , loadingTheme_(false)
  , isCustomTheme_(false)
{
}



/**
 * Copy constructor
 *
 * Also duplicates the list of emoticons: simply copying the source's list would cause our list to be deleted
 * when the source theme is deleted
 *
 * @param other  The theme to duplicate
 */
00061 EmoticonTheme::EmoticonTheme( const EmoticonTheme &other )
  : QObject()
  , loadingTheme_(other.loadingTheme_)
  , isCustomTheme_(other.isCustomTheme_)
  , themePath_(other.themePath_)
{
  // Duplicate one by one the other theme's emoticons
  const QList<Emoticon*>emoticonList( other.getEmoticons() );
  foreach( Emoticon *emoticon, emoticonList )
  {
    emoticon = new Emoticon( *emoticon );
    emoticon->update();
    emoticons_.append( emoticon );
  }

  // The caches cannot be easily copied (due to QHashs being implicitly shared), so regenerate them.
  updateCache();
}



/**
 * Destructor
 */
00085 EmoticonTheme::~EmoticonTheme()
{
  qDeleteAll( emoticons_ );
}



/**
 * Create a new emoticon and add it to the theme
 *
 * Finds out by itself if we're adding a custom emoticon or a standard one.
 *
 * @param pictureFile  The file name of the emoticon, without path and without extension (will be guessed)
 * @param shortcuts    A list of text shortcuts which will be translated to the pictureFile image
 */
00100 void EmoticonTheme::addEmoticon( QString pictureFile, QStringList shortcuts )
{
  Emoticon *emoticon;

  if( isCustomTheme_ )
  {
    emoticon = new Emoticon( pictureFile, shortcuts.first(), themePath_ );
  }
  else
  {
    emoticon = new Emoticon( pictureFile, shortcuts.first() );
  }

  emoticon->setShortcuts( shortcuts );

  emoticons_.append( emoticon );

  // Only update the caches if we're adding a new custom emoticon
  if( ! loadingTheme_ && isCustomTheme_ )
  {
    updateCache();
  }
}



/**
 * Check if the theme contains a certain emoticon
 *
 * @param shortcut    The shortcut text to search for
 */
00131 bool EmoticonTheme::contains( QString shortcut )
{
  foreach( Emoticon *emoticon, emoticons_ )
  {
    if( emoticon->getShortcuts().indexOf( shortcut ) != -1 )
    {
      return true;
    }
  }

  return false;
}



/**
 * Create the theme from nothing
 *
 * Any emoticons currently in the theme will be discarded!
 *
 * @param themeDir  The absolute path where the theme's emoticons.xml definition file is located
 */
00153 bool EmoticonTheme::createTheme( QString themeDir )
{
  QString themeFileName( themeDir + "emoticons.xml" );
  QFile xmlFile( themeFileName );

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Loading emoticons from theme XML: '" << themeFileName << "'";
#endif

  // Try to read the XML
  if( ! xmlFile.open( QIODevice::ReadOnly ) )
  {
#ifdef KMESSDEBUG_EMOTICON_THEMES
    kDebug() << "Could not open '" << themeFileName << "'";
#endif
    return false;
  }

  // Then try to parse it
  QDomDocument xml;
  QString      xmlError;
  if( ! xml.setContent( xmlFile.readAll(), false, &xmlError ) )
  {
#ifdef KMESSDEBUG_EMOTICON_THEMES
    kDebug() << "Failure parsing '" << themeFileName << "': " << xmlError;
#endif
    xmlFile.close();
    return false;
  }

  // Clean the current list
  qDeleteAll( emoticons_ );
  emoticons_.clear();

  // 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" )
    {
      QString emoticonFile( emoticonTag.attribute( "file" ) );

      // Loop through all <string> tags
      QStringList shortcuts;
      QDomNode xmlChild2 = emoticonTag.firstChild();
      while( ! xmlChild2.isNull() )
      {
        // <string> tag found
        QDomElement stringTag = xmlChild2.toElement();
        if( ! stringTag.isNull() && stringTag.tagName() == "string" )
        {
          // Force the shortcuts to be at most 7 characters long, as MSN does.
          stringTag.text().truncate( 7 );
          shortcuts.append( stringTag.text() );
        }
        xmlChild2 = xmlChild2.nextSibling();
      }

      addEmoticon( emoticonFile, shortcuts );
    }
    xmlChild = xmlChild.nextSibling();
  }

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Added " << emoticons_.count() << " emoticons.";
#endif

  return true;
}



/**
 * Obtain an emoticon by its shortcut
 *
 * @param shortcut Text shortcut of the emoticon to get.
 * @return Pointer to the emoticon. Zero on failure.
 */
00233 const Emoticon *EmoticonTheme::getEmoticon( QString shortcut ) const
{
  foreach( Emoticon *emoticon, emoticons_ )
  {
    // This is not the emoticon the user wants
    if( emoticon->getShortcuts().indexOf( shortcut ) == -1 )
    {
      continue;
    }

    return emoticon;
  }

  return 0;
}



/**
 * Return the full list of emoticons
 */
00254 const QList<Emoticon*> &EmoticonTheme::getEmoticons() const
{
  return emoticons_;
}



/**
 * Return the picture file names of all emoticons, mapped by their first shortcut code
 */
00264 const QHash<QString,QString>& EmoticonTheme::getFileNames() const
{
  return themeFileNames_;
}



/**
 * Return the search pattern to find emoticons in an HTML text
 */
00274 const QRegExp& EmoticonTheme::getHtmlPattern() const
{
  return patternHtml_;
}



/**
 * Return the HTML replacement codes for all emoticons
 */
00284 const QHash<QString,QString>& EmoticonTheme::getHtmlReplacements( bool small ) const
{
  if( small )
  {
    return smallHtmlReplacements_;
  }
  else
  {
    return largeHtmlReplacements_;
  }
}



/**
 * Return a QStringList of emoticons
 */
00301 const QStringList& EmoticonTheme::getList() const
{
  return emoticonList_;
}



/**
 * Return the search pattern to find emoticons in a text
 */
00311 const QRegExp& EmoticonTheme::getPattern() const
{
  return patternText_;
}



/**
 * Return one replacement code for the given emoticon
 */
00321 QString EmoticonTheme::getReplacement( const QString &code, bool small ) const
{
  if( small )
  {
    return smallReplacements_[ code ];
  }
  else
  {
    return largeReplacements_[ code ];
  }
}



/**
 * Return the replacement codes for all emoticons
 */
00338 const QHash<QString,QString>& EmoticonTheme::getReplacements( bool small ) const
{
  if( small )
  {
    return smallReplacements_;
  }
  else
  {
    return largeReplacements_;
  }
}



/**
 * Return the full path of the first emoticon of the specified theme
 *
 * This method is used by EmoticonWidget. You use it to get a "quick preview" of how a theme
 * looks like.
 *
 * @param themeDir  The absolute path where the theme's emoticons.xml definition file is located
 * @return          The absolute path of the first emoticon in the theme, or null value on error
 */
00361 QString EmoticonTheme::getThemeIcon( QString themeDir )
{
  // Add a trailing slash if the specified path doesn't have it
  if( themeDir.right( 1 ) != "/" )
  {
    themeDir += "/";
  }

  QString themeFileName( themeDir + "emoticons.xml" );
  QFile xmlFile( themeFileName );

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Getting first emoticon from theme XML: '" << themeFileName << "'";
#endif

  // Try to read the XML
  if( ! xmlFile.open( QIODevice::ReadOnly ) )
  {
#ifdef KMESSDEBUG_EMOTICON_THEMES
    kDebug() << "Could not open '" << themeFileName << "'";
#endif
    return QString::null;
  }

  // Then try to parse it
  QDomDocument xml;
  QString      xmlError;
  if( ! xml.setContent( xmlFile.readAll(), false, &xmlError ) )
  {
#ifdef KMESSDEBUG_EMOTICON_THEMES
    kDebug() << "Failure parsing '" << themeFileName << "': " << xmlError;
#endif
    xmlFile.close();
    return QString::null;
  }

  // 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" )
    {
      Emoticon emoticon( emoticonTag.attribute( "file" ), "", themeDir );

      // Search the theme until a valid emoticon is found, and return it
      if( emoticon.isValid() )
      {
        return emoticon.getPicturePath();
      }
    }
    xmlChild = xmlChild.nextSibling();
  }

  // The theme contained no valid emoticons
  return QString::null;
}



/**
 * Return where the picture files for this theme are located
 */
00425 const QString &EmoticonTheme::getThemePath()
{
  return themePath_;
}



/**
 * Load a theme, by creating it anew or by refreshing the current one
 *
 * @param themeName      The name of the theme, must be the same as the name of the folder which directly contains
 *                       emoticons.xml and the pictures
 * @param isCustomTheme  False if loading a standard theme, true if loading a custom theme
 */
00439 bool EmoticonTheme::loadTheme( QString themeName, bool isCustomTheme )
{
  bool           success = false;

  QString oldThemePath( themePath_ );
  isCustomTheme_ = isCustomTheme;

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Loading " << themeName << " as " << (isCustomTheme?"custom":"default") << " theme.";
#endif

  // Change the theme name (where it is stored)
  setThemeName( themeName );

  if( oldThemePath == themePath_ )
  {
#ifdef KMESSDEBUG_EMOTICON_THEMES
    kDebug() << "Theme" << themeName << "was already loaded!";
#endif
    return true;
  }

  // Set the theme loading status, needed by addEmoticon()
  loadingTheme_ = true;

  if( isCustomTheme || emoticons_.isEmpty() )
  {
    // If we're loading a standard theme, but there's none to update, we need to create it from scratch.
    success = createTheme( themePath_ );
  }
  else
  {
    // If loading a standard theme, always update the current one.
    success = updateTheme( themePath_ );
  }

  // Update the search&replace caches
  updateCache();

  // Reset the theme loading status, needed by addEmoticon()
  loadingTheme_ = false;

  return success;
}



/**
 * Delete a custom emoticon from the theme
 *
 * @param shortcut  The shortcut of the emoticon to remove
 */
00491 bool EmoticonTheme::removeEmoticon( QString shortcut )
{
  // Can't remove emoticons from a standard theme
  if( ! isCustomTheme_ )
  {
    return false;
  }

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Removing emoticon" << shortcut << ".";
#endif

  foreach( Emoticon *emoticon, emoticons_ )
  {
    // This is not the emoticon we need to delete
    if( emoticon->getShortcuts().indexOf( shortcut ) == -1 )
    {
      continue;
    }

    // Delete the file
    QFile( emoticon->getPicturePath() ).remove();

    // Remove the emoticon from the theme
    delete emoticons_.takeAt( emoticons_.indexOf( emoticon ) );

    // Update the cache
    updateCache();

    return true;
  }

  return false;
}



/**
 * Change the shortcut and the tooltip of a custom emoticon
 *
 * @param oldShortcut  The shortcut of the emoticon to replace
 * @param newShortcut  The new shortcut for that emoticon
 */
00534 bool EmoticonTheme::renameEmoticon( QString oldShortcut, QString newShortcut )
{
  // Can't remove emoticons from a standard theme
  if( ! isCustomTheme_ )
  {
    return false;
  }

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Renaming emoticon" << oldShortcut << "to" << newShortcut << ".";
#endif

  foreach( Emoticon *emoticon, emoticons_ )
  {
    // This is not the emoticon we need to rename
    if( emoticon->getShortcuts().indexOf( oldShortcut ) == -1 )
    {
      continue;
    }

    // Replace all the shortcuts with the new one
    emoticon->setShortcut( newShortcut );

    // Also update its tooltip
    emoticon->setTooltip( newShortcut );

    // Update the cache
    updateCache();

    return true;
  }

  return false;
}



/**
 * Save the current theme to its XML theme definition file
 *
 * See the attached URL to view the specification for the XML emoticon definitions file.
 * We deliberately chose not to save the pictures' extensions.
 *
 * @see http://kopete.kde.org/emoticons/emoticonspec.html
 */
00579 bool EmoticonTheme::saveTheme()
{
  // Standard emoticons sets do not get changed, so there is no need to save them.
  if( ! isCustomTheme_ )
  {
    return true;
  }

  // Create any missing directories.
  QDir dir;
  dir.mkpath( themePath_ );

  // Check if the folder exists
  if( ! dir.exists() )
  {
    // The theme hasn't been created!
    kWarning() << "Couldn't create the new theme folder '" << themePath_ << "'!";

    return false;
  }

  QString themeFileName( themePath_ + "emoticons.xml" );
  QFile xmlFile( themeFileName );

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Saving emoticons to theme XML: '" << themeFileName << "'";
#endif


  // Open the destination XML file
  if( ! xmlFile.open( QIODevice::WriteOnly ) )
  {
    kWarning() << "Save failed - couldn't open file '" << themeFileName << "'.";
    KMessageBox::sorry( 0, i18n("Could not save the emoticon theme. Make sure you have permission to write to the theme folder '%1'.",  themePath_ ) );
    return false;
  }

  // Get encoding
  QTextCodec *codec = QTextCodec::codecForLocale();
  if( codec == 0 )
  {
    kWarning() << "Could not find codec '" << QTextCodec::codecForLocale() << "', special characters might not be saved correctly!";
  }

  // Start the XML output using the right text encoding
  QTextStream xmlOutput( &xmlFile );
  if( codec != 0 )
  {
    xmlOutput.setCodec( codec );
  }

  // Start the XML tree by writing the root tag
  xmlOutput << "<?xml version=\"1.0\"?>" << endl << "<messaging-emoticon-map>" << endl << endl;

  foreach( Emoticon *emoticon, emoticons_ )
  {
    if( ! emoticon->isValid() )
    {
      continue;
    }

    // We only need base names of the emoticons: the specs 
    QString file( QFileInfo( emoticon->getPicturePath() ).baseName() );

    // New emoticon: the file name is encoded as HTML to avoid XML parsing errors
    xmlOutput << "\t<emoticon file=\"" << Qt::escape( file ) << "\">" << endl;

    // Add a <string> for each shortcut which maps to this emoticon,
    const QStringList &codes = emoticon->getShortcuts();
    for( QStringList::ConstIterator it = codes.begin(); it != codes.end(); ++it )
    {
      // Encode the shortcuts as HTML to avoid XML parsing errors
      QString code( *it );
      KMessShared::htmlEscape( code );

      xmlOutput << "\t\t<string>" << code << "</string>" << endl;
    }

    // End of emoticon
    xmlOutput << "\t</emoticon>" << endl << endl;
  }

  // Close the XML tree by closing the root
  xmlOutput << "</messaging-emoticon-map>" << endl;

  // Close the file, we're done
  xmlFile.close();

  return true;
}



/**
 * @brief Change the theme name
 *
 * Alter the name of the theme, which is the name of the directory where the theme will
 * be stored.
 *
 * @param newThemeName  The new name
 */
00680 void EmoticonTheme::setThemeName( const QString &newThemeName )
{
  KStandardDirs *dirs = KGlobal::dirs();

  // Try to find the theme between all possible locations
  if( isCustomTheme_ )
  {
    // Custom themes are located in: <kmess app data folder>/<account@email>/customemoticons
    themePath_ = KMessConfig::instance()->getAccountDirectory( newThemeName ) + "/customemoticons/";
  }
  else
  {
    // Standard themes can be in the KDE global emoticons dir, or in the ~/.kde/share/emoticons folder, and possibly elsewhere
    themePath_ = dirs->findResourceDir( "emoticons", newThemeName + "/emoticons.xml" );

    // findResourceDir() only returns the base path for the file you pass to it.
    themePath_ += newThemeName + "/";
  }
}



/**
 * Rebuild the search&replace caches
 */
00705 void EmoticonTheme::updateCache()
{
  QStringList::const_iterator it;
  QString   emoticonHtmlPattern;
  QString   emoticonTextPattern;
  QFileInfo file;

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Updating the caches.";
#endif

  // Remove all replacements, some emoticons may be no longer valid
  emoticonList_.clear();
  smallReplacements_.clear();
  largeReplacements_.clear();
  smallHtmlReplacements_.clear();
  largeHtmlReplacements_.clear();
  themeFileNames_.clear();

  // HACK: Find all HTML entities followed by ")" or "-)". Since there is no replacement for this match, text
  // like "&amp;)" will not be replaced by "&amp<img..alt=';)'..>" but will be skipped. See the pertinent hack
  // at ChatMessageStyle::parseMsnPlus().
  // TODO: Check if this is avoidable with Qt4's HTML implementation
  emoticonHtmlPattern = "\x26#?[a-z0-9]+;-?\\)|";

  foreach( Emoticon *emoticon, emoticons_ )
  {
    if( ! emoticon->isValid() )
    {
#ifdef KMESSDEBUG_EMOTICON_THEMES
      kDebug() << "Invalid emoticon '" << emoticon->getPicturePath()
                << "' with code: " << emoticon->getShortcut() << endl;
#endif
      continue;
    }

    // Generate the HTML tag which represents the emoticon
    QString htmlSmall( emoticon->getHtml( true  ) );
    QString htmlLarge( emoticon->getHtml( false ) );

    // Traverse all shortcuts of this emoticon
    const QStringList &codes = emoticon->getShortcuts();
    foreach( const QString &code, codes )
    {
      // Convert to HTML the main significant HTML control characters
      QString htmlCode( code );
      KMessShared::htmlEscape( htmlCode );

      // Add the current code to the replacement lists..
      emoticonList_ << code;
      smallReplacements_.insert( code, htmlSmall );
      largeReplacements_.insert( code, htmlLarge );
      smallHtmlReplacements_.insert( htmlCode, htmlSmall );
      largeHtmlReplacements_.insert( htmlCode, htmlLarge );

      // ..to the filenames list..
      file.setFile( emoticon->getPicturePath() );
      themeFileNames_.insert( code, file.fileName() );

      // ..and to the emoticon regexp patterns
      if( emoticonTextPattern.length() > 0 )
      {
        // The code is already escaped
        emoticonHtmlPattern += "|";
        emoticonTextPattern += "|";
      }

      emoticonHtmlPattern += QRegExp::escape( htmlCode );
      emoticonTextPattern += QRegExp::escape( code );
    }
  }

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Cache contains " << largeReplacements_.count() << " emoticon shortcuts.";
#endif

  // Cache the patterns.
  if( emoticonHtmlPattern.isEmpty() || emoticonTextPattern.isEmpty() )
  {
    patternHtml_.setPattern( "\0" );
    patternText_.setPattern( "\0" );
  }
  else
  {
    patternHtml_.setPattern( emoticonHtmlPattern );
    patternText_.setPattern( emoticonTextPattern );
  }

  // Update the emoticon titles (only for standard themes)
  if( ! isCustomTheme_ )
  {
    updateTitles();
  }

  emit updated();
}



/**
 * Update a standard theme with names for the standard MSN emoticons
 */
00807 void EmoticonTheme::updateTitles()
{
  // Preset titles are only valid for standard themes
  if( isCustomTheme_ )
  {
    return;
  }

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Updating standard emoticons names...";
#endif

  QString name;
  QHash<QString,QString> emoticonTitles;

  emoticonTitles[ "smile"          ] = i18n( "Smile"                   );
  emoticonTitles[ "wink"           ] = i18n( "Wink"                    );
  emoticonTitles[ "tongue"         ] = i18n( "Tongue out"              );
  emoticonTitles[ "teeth"          ] = i18n( "Big smile"               );
  emoticonTitles[ "sad"            ] = i18n( "Sad"                     );
  emoticonTitles[ "cry"            ] = i18n( "Crying"                  );
  emoticonTitles[ "angry"          ] = i18n( "Angry"                   );
  emoticonTitles[ "confused"       ] = i18n( "Confused"                );
  emoticonTitles[ "embarrassed"    ] = i18n( "Embarrassed"             );
  emoticonTitles[ "ugly"           ] = i18n( "Disappointed"            );
  emoticonTitles[ "shade"          ] = i18n( "Hot"                     );
  emoticonTitles[ "baringteeth"    ] = i18n( "Baring teeth"            );
  emoticonTitles[ "nerd"           ] = i18n( "Nerd"                    );
  emoticonTitles[ "sick"           ] = i18n( "Sick"                    );
  emoticonTitles[ "omg"            ] = i18n( "Surprised"               );
  emoticonTitles[ "party"          ] = i18n( "Party"                   );
  emoticonTitles[ "sleepy"         ] = i18n( "Sleepy"                  );
  emoticonTitles[ "thinking"       ] = i18n( "Thinking"                );
  emoticonTitles[ "sshh"           ] = i18n( "Don't tell anyone"       );
  emoticonTitles[ "secret"         ] = i18n( "Secret telling"          );
  emoticonTitles[ "eyeroll"        ] = i18n( "Eye-rolling"             );
  emoticonTitles[ "sarcastic"      ] = i18n( "Sarcastic"               );
  emoticonTitles[ "huh"            ] = i18n( "I don't know"            );
  emoticonTitles[ "brb"            ] = i18n( "Be right back"           );
  emoticonTitles[ "angel"          ] = i18n( "Angel"                   );
  emoticonTitles[ "dude_hug"       ] = i18n( "Left hug"                );
  emoticonTitles[ "boy"            ] = i18n( "Boy"                     );
  emoticonTitles[ "love"           ] = i18n( "Red heart"               );
  emoticonTitles[ "rose"           ] = i18n( "Red rose"                );
  emoticonTitles[ "thumbs_up"      ] = i18n( "Thumbs up"               );
  emoticonTitles[ "dog"            ] = i18n( "Dog face"                );
  emoticonTitles[ "sun"            ] = i18n( "Sun"                     );
  emoticonTitles[ "devil"          ] = i18n( "Devil"                   );
  emoticonTitles[ "girl_hug"       ] = i18n( "Right hug"               );
  emoticonTitles[ "girl"           ] = i18n( "Girl"                    );
  emoticonTitles[ "unlove"         ] = i18n( "Broken heart"            );
  emoticonTitles[ "wilted_rose"    ] = i18n( "Wilted rose"             );
  emoticonTitles[ "thumbs_down"    ] = i18n( "Thumbs down"             );
  emoticonTitles[ "cat"            ] = i18n( "Cat face"                );
  emoticonTitles[ "moon"           ] = i18n( "Sleeping half-moon"      );
  emoticonTitles[ "kiss"           ] = i18n( "Red lips"                );
  emoticonTitles[ "highfive"       ] = i18n( "Clapping"                );
  emoticonTitles[ "fingerscrossed" ] = i18n( "Crossed fingers"         );
  emoticonTitles[ "automobile"     ] = i18n( "Auto"                    );
  emoticonTitles[ "airplane"       ] = i18n( "Airplane"                );
  emoticonTitles[ "turtle"         ] = i18n( "Turtle"                  );
  emoticonTitles[ "snail"          ] = i18n( "Snail"                   );
  emoticonTitles[ "sheep"          ] = i18n( "Sheep"                   );
  emoticonTitles[ "goat"           ] = i18n( "Goat"                    );
  emoticonTitles[ "bat"            ] = i18n( "Vampire bat"             );
  emoticonTitles[ "pizza"          ] = i18n( "Pizza"                   );
  emoticonTitles[ "beer"           ] = i18n( "Beer mug"                );
  emoticonTitles[ "cocktail"       ] = i18n( "Martini glass"           );
  emoticonTitles[ "cup"            ] = i18n( "Coffee cup"              );
  emoticonTitles[ "cake"           ] = i18n( "Birthday cake"           );
  emoticonTitles[ "plate"          ] = i18n( "Plate"                   );
  emoticonTitles[ "bowl"           ] = i18n( "Bowl"                    );
  emoticonTitles[ "star"           ] = i18n( "Star"                    );
  emoticonTitles[ "rainbow"        ] = i18n( "Rainbow"                 );
  emoticonTitles[ "storm"          ] = i18n( "Stormy cloud"            );
  emoticonTitles[ "lightning"      ] = i18n( "Lightning"               );
  emoticonTitles[ "umbrella"       ] = i18n( "Umbrella"                );
  emoticonTitles[ "island"         ] = i18n( "Island with a palm tree" );
  emoticonTitles[ "phone"          ] = i18n( "Telephone receiver"      );
  emoticonTitles[ "mobilephone"    ] = i18n( "Mobile Phone"            );
  emoticonTitles[ "envelope"       ] = i18n( "Email"                   );
  emoticonTitles[ "clock"          ] = i18n( "Clock"                   );
  emoticonTitles[ "camera"         ] = i18n( "Camera"                  );
  emoticonTitles[ "film"           ] = i18n( "Filmstrip"               );
  emoticonTitles[ "note"           ] = i18n( "Note"                    );
  emoticonTitles[ "handcuffs"      ] = i18n( "Handcuffs"               );
  emoticonTitles[ "money"          ] = i18n( "Money"                   );
  emoticonTitles[ "lightbulb"      ] = i18n( "Light bulb"              );
  emoticonTitles[ "cigarette"      ] = i18n( "Cigarette"               );
  emoticonTitles[ "soccer"         ] = i18n( "Football ball"           );
  emoticonTitles[ "present"        ] = i18n( "Gift with a bow"         );
  emoticonTitles[ "gameconsole"    ] = i18n( "X-Box"                   );
  emoticonTitles[ "computer"       ] = i18n( "Computer"                );
  emoticonTitles[ "messenger"      ] = i18n( "KMess Icon"              );

  foreach( Emoticon *emoticon, emoticons_ )
  {
    name = emoticon->getPictureName();
    if( ! emoticonTitles.value( name, QString() ).isEmpty() )
    {
      emoticon->setTooltip( emoticonTitles[ name ] );
    }
  }
}



/**
 * Update the currently loaded theme with new images
 *
 * The existing emoticons are not deleted: every emoticon in the new theme which matches a
 * shortcut in the current one will have the corresponding image updated.
 *
 * @param themeDir  The absolute path where the theme's emoticons.xml definition file is located
 */
00922 bool EmoticonTheme::updateTheme( QString themeDir )
{
  QString themeFileName( themeDir + "emoticons.xml" );
  QFile xmlFile( themeFileName );

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Updating emoticons from theme XML: '" << themeFileName << "'";
#endif


  // Try to read the XML
  if( ! xmlFile.open( QIODevice::ReadOnly ) )
  {
#ifdef KMESSDEBUG_EMOTICON_THEMES
    kDebug() << "Could not open '" << themeFileName << "'";
#endif
    return false;
  }

  // Try to parse XML
  QDomDocument xml;
  QString      xmlError;
  if( ! xml.setContent( xmlFile.readAll(), false, &xmlError ) )
  {
#ifdef KMESSDEBUG_EMOTICON_THEMES
    kDebug() << "Failure parsing '" << themeFileName << "': " << xmlError;
#endif

    xmlFile.close();
    return false;
  }


  // Load the codes in a map for efficient lookup
  QHash<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" )
        {
          // Force the shortcuts to be at most 7 characters long, as MSN does.
          stringTag.text().truncate( 7 );

          // Insert code in the map
          themeFiles.insert( stringTag.text(), emoticonFile );
        }
        xmlChild2 = xmlChild2.nextSibling();
      }
    }
    xmlChild = xmlChild.nextSibling();
  }

#ifdef KMESSDEBUG_EMOTICON_THEMES
  kDebug() << "Updated " << themeFiles.count() << " emoticons, setting them up.";
#endif

  // Loop through all current emoticons.
  foreach( Emoticon *emoticon, emoticons_ )
  {
    // Loop through all codes of the emoticon
    bool  codeFound = false;
    const QStringList &codes = emoticon->getShortcuts();

    for( QStringList::ConstIterator it = codes.begin(); it != codes.end(); ++it )
    {
      const QString &code = *it;
      // If the emoticon uses that code, set it.
      if( ! themeFiles.value( code, QString() ).isEmpty() )
      {
        emoticon->setPictureName( themeFiles[code] );    // already calls update()
        codeFound = true;
        break;
      }
    }

    // If the code was not found, the file still needs to be updated manually.
    if( ! codeFound )
    {
      emoticon->update();
    }
  }

  return true;
}



#include "emoticontheme.moc"

Generated by  Doxygen 1.6.0   Back to index