Logo Search packages:      
Sourcecode: kdenetwork version File versions

kopetemessage.cpp

/*
    kopetemessage.cpp  -  Base class for Kopete messages

    Copyright (c) 2002-2003 by Martijn Klingens       <klingens@kde.org>
    Copyright (c) 2002-2004 by Olivier Goffart        <ogoffart@tiscalinet.be>

    Kopete    (c) 2002-2004 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This library is free software; you can redistribute it and/or         *
    * modify it under the terms of the GNU Lesser General Public            *
    * License as published by the Free Software Foundation; either          *
    * version 2 of the License, or (at your option) any later version.      *
    *                                                                       *
    *************************************************************************
*/

#include <stdlib.h>

#include <qstylesheet.h>
#include <qregexp.h>
#include <qtextcodec.h>
#include <kdebug.h>
#include <klocale.h>
#include <kiconloader.h>
#include <kstringhandler.h>

#include "kopeteglobal.h"
#include "kopetemessage.h"
#include "kopeteemoticons.h"
#include "kopetemetacontact.h"
#include "kopeteprotocol.h"

struct KopeteMessagePrivate
{
      uint refCount;

      const KopeteContact *from;
      KopeteMessageManager *manager;
      KopeteContactPtrList to;

      KopeteMessage::MessageDirection direction;
      KopeteMessage::MessageFormat format;
      KopeteMessage::MessageType type;
      KopeteMessage::MessageImportance importance;
      bool bgOverride;
      bool fgOverride;
      bool rtfOverride;
      QDateTime timeStamp;
      QFont font;

      QColor fgColor;
      QColor bgColor;
      QString body;
      QString subject;
};

// These colors are used for coloring nicknames. I tried to use
// colors both visible on light and dark background.
static const char* nameColors[] =
{
      "red", "blue" , "gray", "magenta", "violet", "olive", "yellowgreen",
      "darkred", "darkgreen", "darksalmon", "darkcyan", "darkyellow",
      "mediumpurple", "peru", "olivedrab", "royalred", "darkorange", "stateblue",
      "stategray", "goldenrod", "orangered", "tomato", "dogderblue", "steelblue",
      "deeppink", "saddlebrown", "coral", "royalblue"
};

00070 KopeteMessage::KopeteMessage()
{
      d = new KopeteMessagePrivate;
      init( QDateTime::currentDateTime(), 0L, KopeteContactPtrList(), QString::null, QString::null, Internal, PlainText, Chat );
}

00076 KopeteMessage::KopeteMessage( const KopeteContact *fromKC, const KopeteContactPtrList &toKC, const QString &body,
      MessageDirection direction, MessageFormat f, MessageType type )
{
      d = new KopeteMessagePrivate;
      init( QDateTime::currentDateTime(), fromKC, toKC, body, QString::null, direction, f, type );
}

00083 KopeteMessage::KopeteMessage( const KopeteContact *fromKC, const KopeteContact *toKC, const QString &body,
      MessageDirection direction, MessageFormat f, MessageType type )
{
      d = new KopeteMessagePrivate;
      KopeteContactPtrList to;
      to.append(toKC);
      init( QDateTime::currentDateTime(), fromKC, to, body, QString::null, direction, f, type );
}


00093 KopeteMessage::KopeteMessage( const KopeteContact *fromKC, const KopeteContactPtrList &toKC, const QString &body,
      const QString &subject, MessageDirection direction, MessageFormat f, MessageType type )
{
      d = new KopeteMessagePrivate;

      init( QDateTime::currentDateTime(), fromKC, toKC, body, subject, direction, f, type );
}

00101 KopeteMessage::KopeteMessage( const QDateTime &timeStamp, const KopeteContact *fromKC, const KopeteContactPtrList &toKC,
      const QString &body, MessageDirection direction, MessageFormat f, MessageType type )
{
      d = new KopeteMessagePrivate;

      init( timeStamp, fromKC, toKC, body, QString::null, direction, f, type );
}

00109 KopeteMessage::KopeteMessage( const QDateTime &timeStamp, const KopeteContact *fromKC, const KopeteContactPtrList &toKC,
      const QString &body, const QString &subject, MessageDirection direction, MessageFormat f, MessageType type )
{
      d = new KopeteMessagePrivate;

      init( timeStamp, fromKC, toKC, body, subject, direction, f, type );
}

00117 KopeteMessage::KopeteMessage( const KopeteMessage &other )
{
      d = other.d;
      d->refCount++;
}


00124 void KopeteMessage::init( const QDateTime &timeStamp, const KopeteContact *from, const KopeteContactPtrList &to,
      const QString &body, const QString &subject, MessageDirection direction, MessageFormat f, MessageType type )
{
      d->refCount = 1;
      d->from = from;
      d->to   = to;
      d->importance = (to.count() <= 1) ? Normal : Low;
      d->timeStamp = timeStamp;
      d->direction = direction;
      d->manager=0l;
      d->format = f;
      d->bgOverride = false;
      d->fgOverride = false;
      d->rtfOverride = false;
      d->type = type;


      d->subject=subject;

      //Make the body correct

      QString theBody = body;
      if( f == RichText )
      {
            //This is coming from the RichTextEditor component.
            //Strip off the containing HTML document
            theBody.replace( QRegExp( QString::fromLatin1(".*<body.*>\\s+(.*)\\s+</body>.*") ), QString::fromLatin1("\\1") );

            //Strip <p> tags
            theBody.replace( QString::fromLatin1("<p>"), QString::null );

            //Replace </p> with a <br/>
            theBody.replace( QString::fromLatin1("</p>") , QString::fromLatin1("<br/>") );

            //Remove trailing <br/>
            if ( theBody.endsWith( QString::fromLatin1("<br/>") ) )
                  theBody.truncate( theBody.length() - 5 );
            theBody.remove(  QString::fromLatin1("\n") );
      }

      d->body=theBody;
}


00168 KopeteMessage& KopeteMessage::operator=( const KopeteMessage &other )
{
      if( other.d == d )
            return *this;

      detach();
      delete d;

      d = other.d;
      d->refCount++;

      return *this;
}

00182 KopeteMessage::~KopeteMessage()
{
      d->refCount--;
      if( !d->refCount )
            delete d;
}


00190 void KopeteMessage::detach()
{
      if( d->refCount == 1 )
            return;

      // Warning: this only works as long as the private object doesn't contain pointers to allocated objects.
      // The from contact for example is fine, but it's a shallow copy this way.
      KopeteMessagePrivate *newD = new KopeteMessagePrivate(*d);
      newD->refCount = 1;
      d->refCount--;

      d = newD;
}

00204 void KopeteMessage::setBgOverride( bool enabled )
{
      detach();
      d->bgOverride = enabled;
}

00210 void KopeteMessage::setFgOverride( bool enabled )
{
      detach();
      d->fgOverride = enabled;
}

00216 void KopeteMessage::setRtfOverride( bool enabled )
{
      detach();
      d->rtfOverride = enabled;
}

00222 void KopeteMessage::setFg( const QColor &color )
{
      detach();
      d->fgColor=color;
}

00228 void KopeteMessage::setBg( const QColor &color )
{
      detach();
      d->bgColor=color;
}

00234 void KopeteMessage::setFont( const QFont &font )
{
      detach();
      d->font = font;
}

00240 void KopeteMessage::setBody( const QString &body, MessageFormat f )
{
      detach();

      QString theBody = body;
      if( f == RichText )
      {
            //This is coming from the RichTextEditor component.
            //Strip off the containing HTML document
            theBody.replace( QRegExp( QString::fromLatin1(".*<body.*>\\s+(.*)\\s+</body>.*") ), QString::fromLatin1("\\1") );

            //Strip <p> tags
            theBody.replace( QString::fromLatin1("<p>"), QString::null );

            //Replace </p> with a <br/>
            theBody.replace( QString::fromLatin1("</p>"), QString::fromLatin1("<br/>") );

            //Remove trailing </br>
            if ( theBody.endsWith( QString::fromLatin1("<br/>") ) )
                  theBody.truncate( theBody.length() - 5 );

            theBody.remove( QString::fromLatin1("\n") );
      }

      d->body=body;
      d->format = f;
}

00268 void KopeteMessage::setImportance(KopeteMessage::MessageImportance i)
{
      detach();
      d->importance = i;
}

00274 QString KopeteMessage::unescape( const QString &xml )
{
      QString data = xml;

      data.replace( QRegExp( QString::fromLatin1( "< *img[^>]*title=\"([^>\"]*)\"[^>]*>" ) , false ), QString::fromLatin1( "\\1" ) );  //escape smeleys, return to the original code
      data.replace( QRegExp( QString::fromLatin1( "< */ *p[^>]*>" ) , false ), QString::fromLatin1( "\n" ) );
      data.replace( QRegExp( QString::fromLatin1( "< *br */? *>" ) , false ), QString::fromLatin1( "\n" ) );
      data.replace( QRegExp( QString::fromLatin1( "<[^>]*>" ) ), QString::null );

      data.replace( QString::fromLatin1( "&gt;" ), QString::fromLatin1( ">" ) );
      data.replace( QString::fromLatin1( "&lt;" ), QString::fromLatin1( "<" ) );
      data.replace( QString::fromLatin1( "&quot;" ), QString::fromLatin1( "\"" ) );
      data.replace( QString::fromLatin1( "&nbsp;" ), QString::fromLatin1( " " ) );
      data.replace( QString::fromLatin1( "&amp;" ), QString::fromLatin1( "&" ) );

      return data;
}

00292 QString KopeteMessage::escape( const QString &text )
{
      QString html = QStyleSheet::escape( text );
      //Replace carriage returns inside the text
      html.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "<br />" ) );
      //Replace a tab with 4 spaces
      html.replace( QString::fromLatin1( "\t" ), QString::fromLatin1( "&nbsp;&nbsp;&nbsp;&nbsp;" ) );

      //Replace multiple spaces with &nbsp;
      //do not replace every space so we break the linebreak
      html.replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( "&nbsp; " ) );

      return html;
}



00309 QString KopeteMessage::plainBody() const
{
      QString body=d->body;
      if( d->format & RichText )
      {
            body = unescape( body );
      }
      return body;
}

00319 QString KopeteMessage::escapedBody() const
{
      QString escapedBody=d->body;

      if( d->format & PlainText )
      {
            escapedBody=escape( escapedBody );
      }

      return escapedBody;
}

00331 QString KopeteMessage::parsedBody() const
{
      //kdDebug(14000) << k_funcinfo << "messageformat: " << d->format << endl;

      if( d->format == ParsedHTML )
      {
            return d->body;
      }
      else
      {
            return KopeteEmoticons::parseEmoticons(parseLinks(escapedBody(), d->format));
      }
}

static QString makeRegExp( const char *pattern )
{
      const QString urlChar = QString::fromLatin1( "\\+\\-\\w\\./#@&;:=\\?~%_,\\!\\$\\*\\(\\)" );
      const QString boundaryStart = QString::fromLatin1( "(^|[^%1])(" ).arg( urlChar );
      const QString boundaryEnd = QString::fromLatin1( ")([^%1]|$)" ).arg( urlChar );

      return boundaryStart + QString::fromLatin1(pattern) + boundaryEnd;
}

QString KopeteMessage::parseLinks( const QString &message, MessageFormat format )
{
      if ( format == ParsedHTML )
            return message;

      if ( format & RichText )
      {
            // < in HTML *always* means start-of-tag
            QStringList entries = QStringList::split( QChar('<'), message, true );

            QStringList::Iterator it = entries.begin();

            // first one is different: it doesn't start with an HTML tag.
            if ( it != entries.end() )
            {
                  *it = parseLinks( *it, PlainText );
                  ++it;
            }

            for ( ; it != entries.end(); ++it )
            {
                  QString curr = *it;
                  // > in HTML means start-of-tag if and only if it's the first one after a <
                  int tagclose = curr.find( QChar('>') );
                  // no >: the HTML is broken, but we can cope
                  if ( tagclose == -1 )
                        continue;
                  QString tag = curr.left( tagclose + 1 );
                  QString body = curr.mid( tagclose + 1 );
                  *it = tag + parseLinks( body, PlainText );
            }
            return entries.join(QString::fromLatin1("<"));
      }

      QString result = message;

      // common subpatterns - may not contain matching parens!
      const QString name = QString::fromLatin1( "[\\w\\+\\-=_\\.]+" );
      const QString userAndPassword = QString::fromLatin1( "(?:%1(?::%1)?\\@)" ).arg( name );
      const QString urlChar = QString::fromLatin1( "\\+\\-\\w\\./#@&;:=\\?~%_,\\!\\$\\*\\(\\)" );
      const QString urlSection = QString::fromLatin1( "[%1]+" ).arg( urlChar );
      const QString domain = QString::fromLatin1( "[\\-\\w_]+(?:\\.[\\-\\w_]+)+" );

      //Replace http/https/ftp links:
      // Replace (stuff)://[user:password@](linkstuff) with a link
      result.replace(
            QRegExp( makeRegExp("\\w+://%1?\\w%2").arg( userAndPassword, urlSection ) ),
            QString::fromLatin1("\\1<a href=\"\\2\" title=\"\\2\">\\2</a>\\3" ) );

      // Replace www.X.Y(linkstuff) with a http: link
      result.replace(
            QRegExp( makeRegExp("%1?www\\.%2%3").arg( userAndPassword, domain, urlSection ) ),
            QString::fromLatin1("\\1<a href=\"http://\\2\" title=\"http://\\2\">\\2</a>\\3" ) );

      //Replace Email Links
      // Replace user@domain with a mailto: link
      result.replace(
            QRegExp( makeRegExp("%1@%2").arg( name, domain ) ),
            QString::fromLatin1("\\1<a href=\"mailto:\\2\" title=\"mailto:\\2\">\\2</a>\\3") );
            
      //Workaround for Bug 85061: Highlighted URLs adds a ' ' after the URL itself
      // the trailing  &nbsp; is included in the url.
      result.replace( QRegExp( QString::fromLatin1("(<a href=\"[^\"]+)(&nbsp;)(\")")  ) , QString::fromLatin1("\\1\\3") );

      return result;
}



00423 QDateTime KopeteMessage::timestamp() const
{
      return d->timeStamp;
}

00428 const KopeteContact *KopeteMessage::from() const
{
      return d->from;
}

00433 KopeteContactPtrList KopeteMessage::to() const
{
      return d->to;
}

00438 KopeteMessage::MessageType KopeteMessage::type() const
{
      return d->type;
}

00443 QColor KopeteMessage::fg() const
{
      return d->fgColor;
}

00448 QColor KopeteMessage::bg() const
{
      return d->bgColor;
}

00453 QFont KopeteMessage::font() const
{
      //QDomElement bodyNode = d->xmlDoc.elementsByTagName( QString::fromLatin1("body") ).item(0).toElement();
      //return QFont( bodyNode.attribute( QString::fromLatin1("font") ), bodyNode.attribute( QString::fromLatin1("fontsize") ).toInt() );
      return d->font;
}

00460 QString KopeteMessage::subject() const
{
      return d->subject;
}

00465 KopeteMessage::MessageFormat KopeteMessage::format() const
{
      return d->format;
}

00470 KopeteMessage::MessageDirection KopeteMessage::direction() const
{
      return d->direction;
}

00475 KopeteMessage::MessageImportance KopeteMessage::importance() const
{
      return d->importance;
}

00480 KopeteMessageManager *KopeteMessage::manager() const
{
      return d->manager;
}

00485 void KopeteMessage::setManager(KopeteMessageManager *kmm)
{
      detach();
      d->manager=kmm;
}



00493 const QDomDocument KopeteMessage::asXML() const
{
      QDomDocument doc;
      QDomElement messageNode = doc.createElement( QString::fromLatin1("message") );
      messageNode.setAttribute( QString::fromLatin1("time"),
            KGlobal::locale()->formatTime(d->timeStamp.time(), true) );
      messageNode.setAttribute( QString::fromLatin1("timestamp"),
            KGlobal::locale()->formatDateTime(d->timeStamp) );
      if( d->timeStamp.date() == QDate::currentDate() )
      {
            messageNode.setAttribute( QString::fromLatin1("formattedTimestamp"),
                  KGlobal::locale()->formatTime(d->timeStamp.time(), true) );
      }
      else
      {
            messageNode.setAttribute( QString::fromLatin1("formattedTimestamp"),
                  KGlobal::locale()->formatDateTime(d->timeStamp) );
      }
      messageNode.setAttribute( QString::fromLatin1("subject"), QStyleSheet::escape( d->subject ) );
      messageNode.setAttribute( QString::fromLatin1("direction"), d->direction );
      messageNode.setAttribute( QString::fromLatin1("importance"), d->importance );


      //build the <from> and <to>  node
      if( d->direction == Inbound ? d->from : d->to.first() )
      {
            messageNode.setAttribute( QString::fromLatin1("mainContactId"),
                  (d->direction == Inbound) ? d->from->contactId() :
                        d->to.first()->contactId() );
      }

      doc.appendChild( messageNode );

      if( d->from )
      {
            //the nickname
            QString nick=d->from->property(Kopete::Global::Properties::self()->nickName()).value().toString();
            if(nick.isEmpty())
                  nick= d->from->metaContact() ? d->from->metaContact()->displayName() : d->from->contactId() ;

            QString fromName = d->from->metaContact() ? d->from->metaContact()->displayName(): nick;
            QDomElement fromNode = doc.createElement( QString::fromLatin1("from") );
            QDomElement fromContactNode = doc.createElement( QString::fromLatin1("contact") );
            fromContactNode.setAttribute( QString::fromLatin1("contactId"), d->from->contactId() );

            QDomElement fromDisplayName = doc.createElement( QString::fromLatin1("contactDisplayName") );
            fromDisplayName.setAttribute( QString::fromLatin1("dir"), nick.isRightToLeft() ?
                  QString::fromLatin1("rtl") : QString::fromLatin1("ltr") );
            fromDisplayName.setAttribute( QString::fromLatin1("text"), QStyleSheet::escape( nick ) );
            fromContactNode.appendChild( fromDisplayName );

            QDomElement fromMcDisplayname = doc.createElement( QString::fromLatin1("metaContactDisplayName") );
            fromMcDisplayname.setAttribute( QString::fromLatin1("dir"), fromName.isRightToLeft() ?
                  QString::fromLatin1("rtl") : QString::fromLatin1("ltr") );
            fromMcDisplayname.setAttribute( QString::fromLatin1("text"), QStyleSheet::escape( fromName ) );
            fromContactNode.appendChild( fromMcDisplayname );

            QString iconPath = KGlobal::iconLoader()->iconPath( d->from->protocol()->pluginIcon(),          KIcon::Small );
            fromContactNode.setAttribute( QString::fromLatin1("protocolIcon"), iconPath );

            fromNode.appendChild( fromContactNode );

            //quick and simple hash algoritm.
            int hash=0;
            const QString &contactId = d->from->contactId();
            for( uint f = 0; f < contactId.length(); ++f )
                  hash += contactId[f].latin1() * f;

            fromContactNode.setAttribute( QString::fromLatin1("color"), QString::fromLatin1( nameColors[hash % (sizeof(nameColors)/sizeof(char*)) ] )  );
            messageNode.appendChild( fromNode );
      }

      QDomElement toNode = doc.createElement( QString::fromLatin1("to") );
      ///for( KopeteContact *c = d->to.first(); c; c = d->to.next() )
      //{
            KopeteContact *c = d->to.first();
            if( c )
            {
            //the nickname
                  QString nick=c->property(Kopete::Global::Properties::self()->nickName()).value().toString();
                  if(nick.isEmpty())
                        nick= c->metaContact() ? c->metaContact()->displayName() : c->contactId() ;

                  QDomElement cNode = doc.createElement( QString::fromLatin1("contact") );
                  cNode.setAttribute( QString::fromLatin1("contactId"), c->contactId() );

                  QDomElement toDisplayName = doc.createElement( QString::fromLatin1("contactDisplayName") );
                  toDisplayName.setAttribute( QString::fromLatin1("dir"), nick.isRightToLeft() ?
                        QString::fromLatin1("rtl") : QString::fromLatin1("ltr"));
                  toDisplayName.setAttribute( QString::fromLatin1("text"), QStyleSheet::escape( nick ) );
                  cNode.appendChild( toDisplayName );

                  QString toName = c->metaContact() ? c->metaContact()->displayName() : nick;
                  QDomElement toMDisplayName = doc.createElement( QString::fromLatin1("metaContactDisplayName") );
                  toMDisplayName.setAttribute( QString::fromLatin1("dir"), toName.isRightToLeft() ?
                        QString::fromLatin1("rtl") : QString::fromLatin1("ltr") );
                  toMDisplayName.setAttribute( QString::fromLatin1("text"), QStyleSheet::escape( toName ) );
                  cNode.appendChild( toMDisplayName );

                  QString iconPath = KGlobal::iconLoader()->iconPath(c->protocol()->pluginIcon(),           KIcon::Small );
                  cNode.setAttribute( QString::fromLatin1("protocolIcon"), iconPath );
                  toNode.appendChild( cNode );
            }
      //}
      messageNode.appendChild( toNode );

      QDomElement bodyNode = doc.createElement( QString::fromLatin1("body") );

      if( !d->fgOverride && d->fgColor.isValid() )
            bodyNode.setAttribute( QString::fromLatin1("color"), d->fgColor.name() );
      if( !d->bgOverride && d->bgColor.isValid() )
            bodyNode.setAttribute( QString::fromLatin1("bgcolor"), d->bgColor.name() );

      if( !d->rtfOverride && d->font!=QFont() )
      {
            QString fontstr;
            if(!d->font.family().isNull())
                  fontstr+=QString::fromLatin1("font-family: ")+d->font.family()+QString::fromLatin1("; ");
            if(d->font.italic())
                  fontstr+=QString::fromLatin1("font-style: italic; ");
            if(d->font.strikeOut())
                  fontstr+=QString::fromLatin1("text-decoration: line-through; ");
            if(d->font.underline())
                  fontstr+=QString::fromLatin1("text-decoration: underline; ");
            if(d->font.bold())
                  fontstr+=QString::fromLatin1("font-weight: bold;");

            bodyNode.setAttribute( QString::fromLatin1("font"), fontstr );
      }

      bodyNode.setAttribute( QString::fromLatin1("dir"),
            plainBody().isRightToLeft() ? QString::fromLatin1("rtl") : QString::fromLatin1("ltr") );
      QDomCDATASection bodyText = doc.createCDATASection( parsedBody() );
      bodyNode.appendChild( bodyText );

      messageNode.appendChild( bodyNode );

      return doc;
}

00633 QString KopeteMessage::decodeString( const QCString &message, const QTextCodec *providedCodec, bool *success )
{
      /*
      Note to everyone. This function is not the most efficient, that is for sure.
      However, it *is* the only way we can be guarenteed that a given string is
      decoded properly.
      */

      //Check first 128 chars
      int charsToCheck = message.length();
      charsToCheck = 128 > charsToCheck ? charsToCheck : 128;

      if( success )
            *success = true;

      //They are providing a possible codec. Check if it is valid
      if( providedCodec && providedCodec->heuristicContentMatch( message, charsToCheck ) >= charsToCheck )
      {
            //All chars decodable.
            return providedCodec->toUnicode( message );
      }

      //Check if it is UTF
      if( KStringHandler::isUtf8(message) )
      {
            //We have a UTF string almost for sure. At least we know it will be decoded.
            return QString::fromUtf8( message );
      }

      //Try codecForContent - exact match
      QTextCodec *testCodec = QTextCodec::codecForContent(message, charsToCheck);
      if( testCodec && testCodec->heuristicContentMatch( message, charsToCheck ) >= charsToCheck )
      {
            //All chars decodable.
            return testCodec->toUnicode( message );
      }

      kdWarning(14000) << k_funcinfo << "Unable to decode string using provided codec(s), taking best guesses!" << endl;
      if( success )
            *success = false;

      //We don't have any clues here.

      //Try local codec
      testCodec = QTextCodec::codecForLocale();
      if( testCodec && testCodec->heuristicContentMatch( message, charsToCheck ) >= charsToCheck )
      {
            //All chars decodable.
            kdDebug(14000) << k_funcinfo << "Using locale's codec" << endl;
            return testCodec->toUnicode( message );
      }

      //Try latin1 codec
      testCodec = QTextCodec::codecForMib(4);
      if( testCodec && testCodec->heuristicContentMatch( message, charsToCheck ) >= charsToCheck )
      {
            //All chars decodable.
            kdDebug(14000) << k_funcinfo << "Using latin1" << endl;
            return testCodec->toUnicode( message );
      }

      kdDebug(14000) << k_funcinfo << "Using latin1 and cleaning string" << endl;
      //No codec decoded. Just decode latin1, and clean out any junk.
      QString result = testCodec->toUnicode( message );
      for( uint i = 0; i < message.length(); ++i )
      {
            if( !result[i].isPrint() )
                  result[i] = '?';
      }

      return result;
}

// vim: set noet ts=4 sts=4 sw=4:


Generated by  Doxygen 1.6.0   Back to index