Index: src/kmesstest.h
===================================================================
--- src/kmesstest.h	(revisione 3025)
+++ src/kmesstest.h	(copia locale)
@@ -61,6 +61,7 @@
     void       testCrash();
     void       testAddContactDialog();
     void       testContactAddedUserDialog();
+    void       testMsnPlus();
     void       testNotificationConnection();
     void       testContactList();
     void       testConnect();
Index: src/CMakeLists.txt
===================================================================
--- src/CMakeLists.txt	(revisione 3025)
+++ src/CMakeLists.txt	(copia locale)
@@ -90,6 +90,7 @@
    settings/settingsdialog.cpp
    utils/crashhandler.cpp
    utils/idletimer.cpp
+   utils/msnplus.cpp
    utils/nowlisteningclient.cpp
    utils/thumbnailprovider.cpp
    utils/xautolock.cpp
Index: src/contactlistviewdelegate.cpp
===================================================================
--- src/contactlistviewdelegate.cpp	(revisione 3025)
+++ src/contactlistviewdelegate.cpp	(copia locale)
@@ -21,6 +21,7 @@
 #include "emoticonmanager.h"
 #include "contact/contactlistmodelitem.h"
 #include "contact/msnstatus.h"
+#include "utils/msnplus.h"
 #include "kmessdebug.h"
 
 #include <QLabel>
@@ -262,12 +263,12 @@
       else if( ! currentAccount_->getShowListPictures() )
       {
         // Person with message after it
-        textWidget_->setText( Qt::escape( friendlyName ) + "<small><font color=\"#666\"> - " + messageString + "</font></small>" );
+        textWidget_->setText( MsnPlus::getFormattedString( Qt::escape( friendlyName ) ) + "<small><font color=\"#666\"> - " + messageString + "</font></small>" );
       }
       else
       {
         // Message below, 
-        textWidget_->setText( Qt::escape( friendlyName ) + "<br /><small><font color=\"#666\">" + messageString + "</font></small>" );
+        textWidget_->setText( MsnPlus::getFormattedString( Qt::escape( friendlyName ) ) + "<br /><small><font color=\"#666\">" + messageString + "</font></small>" );
       }
 
       textWidget_->adjustSize();
Index: src/utils/msnplus.cpp
===================================================================
--- src/utils/msnplus.cpp	(revisione 0)
+++ src/utils/msnplus.cpp	(revisione 0)
@@ -0,0 +1,319 @@
+/***************************************************************************
+                          msnplus.cpp -  adds MSN Plus! codes/features
+                             -------------------
+    begin                : April 30, 2008
+    copyright            : (C) 2008 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 "msnplus.h"
+
+#include <math.h>
+
+#include <QColor>
+#include <QRegExp>
+#include <QString>
+#include <QTextDocument>
+
+#include "../kmessdebug.h"
+
+
+/// KILLME @todo put in kmessdebug.h
+#define KMESSDEBUG_MSNPLUS
+
+
+
+// Return the given string with MSN Plus! formatting stripped out
+QString MsnPlus::getCleanString( const QString &string )
+{
+  static QHash<QString, QString> cleanedStringsCache;
+  // Check if the string is already in cache
+  if( cleanedStringsCache.contains( string ) )
+  {
+    return cleanedStringsCache.value( string );
+  }
+
+  // Make a changeable copy of the given string
+  QString parsed = string;
+
+  // First check if the string does not need modification
+  if( ! parsed.contains( "[" ) /*&& ! parsed.contains( "\xB7" )*/ )
+  {
+    return string;
+  }
+
+  // The output string will contain HTML, extra care has to be taken not to fool the parsers with false positives
+  if( Qt::mightBeRichText( parsed ) )
+  {
+    parsed.replace( "&", "&amp;" ).replace( "<", "&lt;" ).replace( ">", "&gt;" );
+  }
+
+  parsed.replace(  "[b]", "" )
+        .replace( "[/b]", "" )
+        .replace(  "[i]", "" )
+        .replace( "[/i]", "" )
+        .replace(  "[u]", "" )
+        .replace( "[/u]", "" )
+        .replace(  "[s]", "" )
+        .replace( "[/s]", "" );
+
+  parsed.replace( QRegExp( "\\[/?c=?#?[0-9a-z,]*\\]", Qt::CaseInsensitive ), "" )
+        .replace( QRegExp( "\\[/?a=?#?[0-9a-z,]*\\]", Qt::CaseInsensitive ), "" );
+
+#ifdef KMESSDEBUG_MSNPLUS
+  kDebug() << "Original:" << string;
+  kDebug() << "Parsed:"   << parsed;
+#endif
+
+  // Add this to the cache
+  cleanedStringsCache.insert( string, parsed );
+
+  // Keep the queue size to the maximum allowed length
+  if( cleanedStringsCache.count() > MSN_PLUS_STRINGCACHESIZE )
+  {
+    cleanedStringsCache.remove( cleanedStringsCache.constBegin().key() );
+  }
+
+  return parsed;
+}
+
+
+
+// Return the given string with MSN Plus! formatting parsed
+QString MsnPlus::getFormattedString( const QString &string )
+{
+  static QHash<QString, QString> formattedStringsCache;
+
+  // Check if the string is already in cache
+  if( formattedStringsCache.contains( string ) )
+  {
+    return formattedStringsCache.value( string );
+  }
+
+  // Make a changeable copy of the given string
+  QString parsed = string;
+
+  // First check if the string does not need modification
+  if( ! parsed.contains( "[" ) /*&& ! parsed.contains( "\xB7" )*/ )
+  {
+    return string;
+  }
+
+  // The output string will contain HTML, extra care has to be taken not to fool the parsers with false positives
+  if( Qt::mightBeRichText( parsed ) )
+  {
+    parsed.replace( "&", "&amp;" ).replace( "<", "&lt;" ).replace( ">", "&gt;" );
+  }
+
+  parsed.replace(  "[b]",  "<b>" )
+        .replace( "[/b]", "</b>" )
+        .replace(  "[i]",  "<i>" )
+        .replace( "[/i]", "</i>" )
+        .replace(  "[u]",  "<u>" )
+        .replace( "[/u]", "</u>" )
+        .replace(  "[s]",  "<s>" )
+        .replace( "[/s]", "</s>" );
+
+  QRegExp colorMatch( "\\[(c|a)=(#?[0-9a-z]+)\\](.*)\\[/\\1(?:=(#?[0-9a-z]+))?\\]", Qt::CaseInsensitive );
+
+  while( colorMatch.indexIn( parsed ) != -1 )
+  {
+    bool isForeground = ( colorMatch.cap( 1 ) == "c" );
+
+    // match a solid color
+    if( colorMatch.cap( 4 ).isEmpty() )
+    {
+      parsed.replace( colorMatch.pos(), colorMatch.matchedLength(),
+                      "<span style='" + QString( isForeground ? "color" : "background-color" ) + ":" + getHtmlColor( colorMatch.cap( 2 ) ) + ";'>" +
+                      colorMatch.cap( 3 ) + "</span>" );
+    }
+    // Match a foreground color gradient
+    else if( isForeground )
+    {
+      parsed.replace( colorMatch.pos(), colorMatch.matchedLength(),
+                      getHtmlGradient( colorMatch.cap( 3 ), colorMatch.cap( 2 ), colorMatch.cap( 4 ) ) );
+    }
+    // Match a background color gradient
+    else
+    {
+      parsed.replace( colorMatch.pos(), colorMatch.matchedLength(),
+                      "<span style='background-color:qlineargradient(x1:0,y1:0,x2:1,y2:0,"
+                      "stop:0 " + getHtmlColor( colorMatch.cap( 2 ) ) + ",stop:1 " + getHtmlColor( colorMatch.cap( 4 ) ) + ");'>" +
+                      colorMatch.cap( 3 ) + "</span>" );
+    }
+  }
+//color: ;
+#ifdef KMESSDEBUG_MSNPLUS
+  kDebug() << "Original:" << string;
+  kDebug() << "Parsed:"   << parsed;
+#endif
+
+  // Add the parsed string in a tag which is not usually used elsewhere: this drastically reduces parsing problems
+  // originated by, for example, missing closing tags.
+  parsed = "<font>" + parsed + "</font>";
+
+  // Add this to the cache
+  formattedStringsCache.insert( string, parsed );
+
+  // Keep the queue size to the maximum allowed length
+  if( formattedStringsCache.count() > MSN_PLUS_STRINGCACHESIZE )
+  {
+    formattedStringsCache.remove( formattedStringsCache.constBegin().key() );
+  }
+
+  return parsed;
+}
+
+
+
+// Turns color codes (english color names, RGB triplets, MSN Plus! palette colors) into an HTML RGB color code
+QString MsnPlus::getHtmlColor( const QString& color )
+{
+  // Find arbitrary RGB triplets
+  if( color.contains( "," ) )
+  {
+    QStringList rgb = color.split( ",", QString::KeepEmptyParts );
+    QColor rgbColor( rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt() );
+
+    if( rgbColor.isValid() )
+    {
+      return rgbColor.name();
+    }
+  }
+
+  // Find HTML (#RRGGBB/#RGB) or CSS (red,blue) color codes
+  QColor cssOrHtmlColor( color );
+  if( cssOrHtmlColor.isValid() )
+  {
+   return cssOrHtmlColor.name();
+  }
+
+  // Find colors in the MSN Plus! palette. Note that a whole lot of colors are still missing from the list.
+  switch( color.toInt() )
+  {
+    case MSN_PLUS_COLOR_WHITE:   return "#FFFFFF";
+    case MSN_PLUS_COLOR_MARINE:  return "#000066";
+    case MSN_PLUS_COLOR_GREEN:   return "#009900";
+    case MSN_PLUS_COLOR_RED:     return "#FF0000";
+    case MSN_PLUS_COLOR_BROWN:   return "#660000";
+    case MSN_PLUS_COLOR_PURPLE:  return "#990099";
+    case MSN_PLUS_COLOR_ORANGE:  return "#FF6600";
+    case MSN_PLUS_COLOR_YELLOW:  return "#FFFF00";
+    case MSN_PLUS_COLOR_LIME:    return "#00FF00";
+    case MSN_PLUS_COLOR_TEAL:    return "#006699";
+    case MSN_PLUS_COLOR_AQUA:    return "#00FFFF";
+    case MSN_PLUS_COLOR_BLUE:    return "#0000FF";
+    case MSN_PLUS_COLOR_PINK:    return "#FF00FF";
+    case MSN_PLUS_COLOR_GRAY:    return "#666666";
+    case MSN_PLUS_COLOR_SILVER:  return "#CCCCCC";
+    case MSN_PLUS_COLOR_BLACK:
+    default:                     return "#000000";
+  }
+}
+
+
+
+// Turns a string into a gradient colored one, using Qt HTML tags
+QString MsnPlus::getHtmlGradient( const QString& text, const QString& startColor, const QString& endColor )
+{
+  QColor start( getHtmlColor( startColor ) );
+  QColor end  ( getHtmlColor( endColor   ) );
+
+  if( ! start.isValid() || ! end.isValid() || text.isEmpty() )
+  {
+    return text;
+  }
+
+  bool inTag = false, inEntity = false;
+  QColor current = start;
+  QChar character;
+  QString outputText = "";
+  int indexFullString;
+  unsigned int levels, indexGradient = 0;
+  int differenceRed, differenceGreen, differenceBlue, tempRed, tempGreen, tempBlue;
+
+  // Get the length of the string which has to be converted, stripped from HTML tags (which will not be
+  // colored) and from HTML entities, which don't count towards the gradient levels, since are output
+  // as a whole with a single color
+  levels = QString( text ).replace( QRegExp( "<[^>]+>|&[a-z]+;" ), "" ).length();
+
+  // Calculate the RGB difference between the starting and ending color
+  differenceRed   = (int)floor( ( start.red  () - end.red  () ) / (float)levels );
+  differenceGreen = (int)floor( ( start.green() - end.green() ) / (float)levels );
+  differenceBlue  = (int)floor( ( start.blue () - end.blue () ) / (float)levels );
+
+#ifdef KMESSDEBUG_MSNPLUS
+  kDebug() << "Size is " << text.length() << " (" << levels << " stripped) - "
+           << "Colored from " << start.name() << " to " << end.name() << ", difference: ("
+           << differenceRed << "," << differenceGreen << "," << differenceBlue << ")." << endl;
+#endif
+
+  // Proceed through the entire original string
+  for( indexFullString = 0; indexFullString < text.length(); indexFullString++ )
+  {
+    character = text[ indexFullString ];
+
+    // Match HTML tags: they must be skipped entirely
+    if( character == '<' || character == '>' )
+    {
+      inTag = ( character == '<' );
+      outputText += character;
+      continue;
+    }
+    // Match HTML entities: they must be enclosed as a whole in a single font
+    else if( character == '&' || character == ';' )
+    {
+      if( character == '&' )
+      {
+        inEntity = true;
+        outputText += "<span style=\"color:" + current.name() + ";\">" + character;
+      }
+      else
+      {
+        inEntity = false;
+        outputText += character;
+        outputText += "</span>";
+      }
+      indexGradient++;
+      continue;
+    }
+
+    // Characters into tags or entities are output directly, and no gradient is calculated.
+    if( inTag || inEntity )
+    {
+      outputText += character;
+      continue;
+    }
+
+    // Get the new values for the current gradient character
+    tempRed   = start.red  () - ( differenceRed   * indexGradient );
+    tempGreen = start.green() - ( differenceGreen * indexGradient );
+    tempBlue  = start.blue () - ( differenceBlue  * indexGradient );
+
+    // The values may get out of the limits, and since setRgb() voids the whole RGB color if one of the values is
+    // out of range, we must assure them to be always in range.
+    current.setRgb( tempRed   < 0 ? 0 : ( tempRed   > 255 ? 255 : tempRed   ),
+                    tempGreen < 0 ? 0 : ( tempGreen > 255 ? 255 : tempGreen ),
+                    tempBlue  < 0 ? 0 : ( tempBlue  > 255 ? 255 : tempBlue  ) );
+
+//    kdDebug() << indexGradient << " -> " << character << ": " << current.name() << endl;
+
+    // Use the <font> to save characters
+    outputText += "<font color='" + current.name() + "'>" + character + "</font>";
+
+    indexGradient++;
+  }
+
+  return outputText;
+}
+
+
Index: src/utils/msnplus.h
===================================================================
--- src/utils/msnplus.h	(revisione 0)
+++ src/utils/msnplus.h	(revisione 0)
@@ -0,0 +1,117 @@
+/***************************************************************************
+                          msnplus.h -  adds MSN Plus! codes/features
+                             -------------------
+    begin                : April 30, 2008
+    copyright            : (C) 2008 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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef MSNPLUS_H
+#define MSNPLUS_H
+
+#include <QString>
+#include <QHash>
+
+
+#define MSN_PLUS_STRINGCACHESIZE     100
+
+
+
+/**
+ * @brief Implements features from the MSN Plus extension to KMess
+ *
+ * Implements features from the "MSN Plus! Live" extension for MSN Messenger
+ * and Windows Live Messenger. These features can be: custom formatting in
+ * nick names, personal messages and in chat text; sending/receiving of
+ * little sound clips; contact pinging; special colored phrases which trigger
+ * a sound; various IRC-style commands, like /me; et cetera.
+ * Some of them are worth adding in KMess, some others are kind of useless but
+ * someday they could be implemented.
+ *
+ * Currently the only implemented feature is text formatting, which is the
+ * most used and also the most annoying to not have; reading friendly names
+ * with all the tags and codes really IS annoying.
+ *
+ * @author Valerio Pilo <valerio@kmess.org>
+ */
+class MsnPlus
+{
+
+  public: // Public static methods
+
+    // Return the given string with MSN Plus! formatting stripped out
+    static QString        getCleanString( const QString &string );
+    // Return the given string with MSN Plus! formatting parsed
+    static QString        getFormattedString( const QString &string );
+
+
+  private: // private properties
+
+    // Available kinds of text formatting
+    enum MsnPlusFormatTypes
+    {
+    // Do not use this one
+      MSN_PLUS_FORMAT_OLD             =  1,
+
+    // Plus! Live formatting tags
+      MSN_PLUS_FORMAT_BOLD            =  2,   /* Bold */
+      MSN_PLUS_FORMAT_ITALIC          =  4,   /* Italics */
+      MSN_PLUS_FORMAT_UNDERLINE       =  8,   /* Underline */
+      MSN_PLUS_FORMAT_STRIKETHROUGH   = 16,   /* Strikethrough */
+      MSN_PLUS_FORMAT_FOREGROUND      = 32,   /* Foreground colors */
+      MSN_PLUS_FORMAT_BACKGROUND      = 64,   /* Background colors */
+
+    // Legacy (but still used) MSN Plus! formatting codes
+      MSN_PLUS_FORMAT_BOLD_OLD              = MSN_PLUS_FORMAT_OLD | MSN_PLUS_FORMAT_BOLD,
+      MSN_PLUS_FORMAT_ITALIC_OLD            = MSN_PLUS_FORMAT_OLD | MSN_PLUS_FORMAT_ITALIC,
+      MSN_PLUS_FORMAT_UNDERLINE_OLD         = MSN_PLUS_FORMAT_OLD | MSN_PLUS_FORMAT_UNDERLINE,
+      MSN_PLUS_FORMAT_STRIKETHROUGH_OLD     = MSN_PLUS_FORMAT_OLD | MSN_PLUS_FORMAT_STRIKETHROUGH,
+      MSN_PLUS_FORMAT_FOREGROUND_OLD        = MSN_PLUS_FORMAT_OLD | MSN_PLUS_FORMAT_FOREGROUND,
+      MSN_PLUS_FORMAT_BACKGROUND_OLD        = MSN_PLUS_FORMAT_OLD | MSN_PLUS_FORMAT_BACKGROUND,
+
+      MSN_PLUS_FORMAT_COUNT
+    };
+
+    // Predefined MSN Plus! colors
+    enum MsnPlusColors
+    {
+      MSN_PLUS_COLOR_WHITE   = 0,
+      MSN_PLUS_COLOR_BLACK   = 1,
+      MSN_PLUS_COLOR_MARINE  = 2,
+      MSN_PLUS_COLOR_GREEN   = 3,
+      MSN_PLUS_COLOR_RED     = 4,
+      MSN_PLUS_COLOR_BROWN   = 5,
+      MSN_PLUS_COLOR_PURPLE  = 6,
+      MSN_PLUS_COLOR_ORANGE  = 7,
+      MSN_PLUS_COLOR_YELLOW  = 8,
+      MSN_PLUS_COLOR_LIME    = 9,
+      MSN_PLUS_COLOR_TEAL    = 10,
+      MSN_PLUS_COLOR_AQUA    = 11,
+      MSN_PLUS_COLOR_BLUE    = 12,
+      MSN_PLUS_COLOR_PINK    = 13,
+      MSN_PLUS_COLOR_GRAY    = 14,
+      MSN_PLUS_COLOR_SILVER  = 15
+    };
+
+
+  private: // private members
+
+    // Turns color codes (english color names, RGB triplets, MSN Plus! palette colors) into an HTML RGB color code
+    static QString        getHtmlColor( const QString& color );
+    // Turns a string into a gradient colored one, using Qt HTML tags
+    static QString        getHtmlGradient( const QString& text, const QString& start, const QString& end );
+
+};
+
+
+
+#endif
Index: src/kmesstest.cpp
===================================================================
--- src/kmesstest.cpp	(revisione 3025)
+++ src/kmesstest.cpp	(copia locale)
@@ -38,6 +38,7 @@
 #include "notification/notificationcontactstatus.h"
 #include "notification/passivepopup.h"
 #include "settings/settingsdialog.h"
+#include "utils/msnplus.h"
 #include "utils/nowlisteningclient.h"
 #include "contactlistviewdelegate.h"
 #include "currentaccount.h"
@@ -95,6 +96,7 @@
   TESTCASE( "crash",            testCrash() );
   TESTCASE( "addcontact",       testAddContactDialog() );
   TESTCASE( "contactaddeduser", testContactAddedUserDialog() );
+  TESTCASE( "msnplus",          testMsnPlus() );
   TESTCASE( "ns",               testNotificationConnection() );
   TESTCASE( "contactlist",      testContactList() );
   TESTCASE( "connect",          testConnect() );
@@ -263,6 +265,42 @@
 
 
 
+void KMessTest::testMsnPlus()
+{
+  QStringList strings;
+
+  strings << QString::fromUtf8( "[c=#660000],.-~*'¨¯¨'*·~-.¸-).[i]Simo[/i].(-,.-~*'¨¯¨'*·~-.¸[/c=#00FFF]" );
+//   strings << QString::fromUtf8( "·$#FF2291·#·$#FF3E9FM·$#FF5AADa·$#FF77BBr·$#FF5AADt·$#FF3E9Fy·$#FF2291·#" );
+  strings << QString::fromUtf8( "[b][a=1][c=4]- Emet -[/c][/a][/b]" );
+//   strings << QString::fromUtf8( "·$39Bellissima...·$38perplessa e dubbiosa..cercasi i miei occhiali ke non trovo più...." );
+//   strings << QString::fromUtf8( "·$#F37AF0Vanitosa ·$ ·$#0A79F5Da strega cattiva a principessa" );
+
+  foreach( QString str, strings )
+  {
+    KMessageBox::information( kmess_, "<html>" + MsnPlus::getFormattedString( str ) + "<br><br>" + MsnPlus::getCleanString( str ) + "</html>" );
+  }
+
+  exit(0);
+/*
+  QLineEdit *edit = new QLineEdit();
+  edit->show();
+  
+
+
+  QLabel *label = new QLabel();
+  label->setTextFormat( Qt::AutoText );
+  label->setText( MsnPlus::getFormattedString( strings.first() ) );
+  label->show();
+  connect( edit, SIGNAL( textChanged(const QString &) ), label, SLOT( setText(const QString &)));
+  
+  edit->setText( MsnPlus::getFormattedString( strings.first() ) );
+  label->setStyleSheet("QLabel{color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: s0, stop: 0 #660000, stop: 1 #FFFFFF );}");
+  return;
+  */
+}
+
+
+
 void KMessTest::testSoapConnection()
 {
   MsnAppDirectoryService *appDir = new MsnAppDirectoryService();

