You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
642 lines
18 KiB
642 lines
18 KiB
/*
|
|
kopetemessage.cpp - Base class for Kopete messages
|
|
|
|
Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
|
|
Copyright (c) 2002-2006 by Olivier Goffart <ogoffart @ kde.org>
|
|
|
|
Kopete (c) 2002-2005 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 <tqcolor.h>
|
|
#include <tqbuffer.h>
|
|
#include <tqimage.h>
|
|
#include <tqstylesheet.h>
|
|
#include <tqregexp.h>
|
|
#include <tqtextcodec.h>
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
#include <kiconloader.h>
|
|
#include <kstringhandler.h>
|
|
#include <kmdcodec.h>
|
|
#include <tqguardedptr.h>
|
|
|
|
#include "kopetemessage.h"
|
|
#include "kopetemetacontact.h"
|
|
#include "kopeteprotocol.h"
|
|
#include "kopetechatsession.h"
|
|
#include "kopeteprefs.h"
|
|
#include "kopetecontact.h"
|
|
#include "kopeteemoticons.h"
|
|
|
|
|
|
using namespace Kopete;
|
|
|
|
class Message::Private
|
|
: public KShared
|
|
{
|
|
public:
|
|
Private( const TQDateTime &timeStamp, const Contact *from, const ContactPtrList &to,
|
|
const TQString &subject, MessageDirection direction,
|
|
const TQString &requestedPlugin, MessageType type );
|
|
|
|
TQGuardedPtr<const Contact> from;
|
|
ContactPtrList to;
|
|
ChatSession *manager;
|
|
|
|
MessageDirection direction;
|
|
MessageFormat format;
|
|
MessageType type;
|
|
TQString requestedPlugin;
|
|
MessageImportance importance;
|
|
bool bgOverride;
|
|
bool fgOverride;
|
|
bool rtfOverride;
|
|
bool isRightToLeft;
|
|
TQDateTime timeStamp;
|
|
TQFont font;
|
|
|
|
TQColor fgColor;
|
|
TQColor bgColor;
|
|
TQString body;
|
|
TQString subject;
|
|
};
|
|
|
|
Message::Private::Private( const TQDateTime &timeStamp, const Contact *from,
|
|
const ContactPtrList &to, const TQString &subject,
|
|
MessageDirection direction, const TQString &requestedPlugin, MessageType type )
|
|
: from( from ), to( to ), manager( 0 ), direction( direction ), format( PlainText ), type( type ),
|
|
requestedPlugin( requestedPlugin ), importance( (to.count() <= 1) ? Normal : Low ),
|
|
bgOverride( false ), fgOverride( false ), rtfOverride( false ), isRightToLeft( false ),
|
|
timeStamp( timeStamp ), body( TQString::null ), subject( subject )
|
|
{
|
|
}
|
|
|
|
Message::Message()
|
|
: d( new Private( TQDateTime::currentDateTime(), 0L, TQPtrList<Contact>(), TQString::null, Internal,
|
|
TQString::null, TypeNormal ) )
|
|
{
|
|
}
|
|
|
|
Message::Message( const Contact *fromKC, const TQPtrList<Contact> &toKC, const TQString &body,
|
|
MessageDirection direction, MessageFormat f, const TQString &requestedPlugin, MessageType type )
|
|
: d( new Private( TQDateTime::currentDateTime(), fromKC, toKC, TQString::null, direction, requestedPlugin, type ) )
|
|
{
|
|
doSetBody( body, f );
|
|
}
|
|
|
|
Message::Message( const Contact *fromKC, const Contact *toKC, const TQString &body,
|
|
MessageDirection direction, MessageFormat f, const TQString &requestedPlugin, MessageType type )
|
|
{
|
|
TQPtrList<Contact> to;
|
|
to.append(toKC);
|
|
d = new Private( TQDateTime::currentDateTime(), fromKC, to, TQString::null, direction, requestedPlugin, type );
|
|
doSetBody( body, f );
|
|
}
|
|
|
|
Message::Message( const Contact *fromKC, const TQPtrList<Contact> &toKC, const TQString &body,
|
|
const TQString &subject, MessageDirection direction, MessageFormat f, const TQString &requestedPlugin, MessageType type )
|
|
: d( new Private( TQDateTime::currentDateTime(), fromKC, toKC, subject, direction, requestedPlugin, type ) )
|
|
{
|
|
doSetBody( body, f );
|
|
}
|
|
|
|
Message::Message( const TQDateTime &timeStamp, const Contact *fromKC, const TQPtrList<Contact> &toKC,
|
|
const TQString &body, MessageDirection direction, MessageFormat f, const TQString &requestedPlugin, MessageType type )
|
|
: d( new Private( timeStamp, fromKC, toKC, TQString::null, direction, requestedPlugin, type ) )
|
|
{
|
|
doSetBody( body, f );
|
|
}
|
|
|
|
|
|
Message::Message( const TQDateTime &timeStamp, const Contact *fromKC, const TQPtrList<Contact> &toKC,
|
|
const TQString &body, const TQString &subject, MessageDirection direction, MessageFormat f, const TQString &requestedPlugin, MessageType type )
|
|
: d( new Private( timeStamp, fromKC, toKC, subject, direction, requestedPlugin, type ) )
|
|
{
|
|
doSetBody( body, f );
|
|
}
|
|
|
|
Kopete::Message::Message( const Message &other )
|
|
: d(other.d)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
Message& Message::operator=( const Message &other )
|
|
{
|
|
d = other.d;
|
|
return *this;
|
|
}
|
|
|
|
Message::~Message()
|
|
{
|
|
}
|
|
|
|
void Message::detach()
|
|
{
|
|
// there is no detach in KSharedPtr :(
|
|
if( d.count() == 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.
|
|
d = new Private(*d);
|
|
}
|
|
|
|
void Message::setBgOverride( bool enabled )
|
|
{
|
|
detach();
|
|
d->bgOverride = enabled;
|
|
}
|
|
|
|
void Message::setFgOverride( bool enabled )
|
|
{
|
|
detach();
|
|
d->fgOverride = enabled;
|
|
}
|
|
|
|
void Message::setRtfOverride( bool enabled )
|
|
{
|
|
detach();
|
|
d->rtfOverride = enabled;
|
|
}
|
|
|
|
void Message::setFg( const TQColor &color )
|
|
{
|
|
detach();
|
|
d->fgColor=color;
|
|
}
|
|
|
|
void Message::setBg( const TQColor &color )
|
|
{
|
|
detach();
|
|
d->bgColor=color;
|
|
}
|
|
|
|
void Message::setFont( const TQFont &font )
|
|
{
|
|
detach();
|
|
d->font = font;
|
|
}
|
|
|
|
void Message::doSetBody( const TQString &_body, Message::MessageFormat f )
|
|
{
|
|
TQString body = _body;
|
|
|
|
//TODO: move that in ChatTextEditPart::contents
|
|
if( f == RichText )
|
|
{
|
|
//This is coming from the RichTextEditor component.
|
|
//Strip off the containing HTML document
|
|
body.replace( TQRegExp( TQString::fromLatin1(".*<body[^>]*>(.*)</body>.*") ), TQString::fromLatin1("\\1") );
|
|
|
|
//Strip <p> tags
|
|
body.replace( TQString::fromLatin1("<p>"), TQString::null );
|
|
|
|
//Replace </p> with a <br/>
|
|
body.replace( TQString::fromLatin1("</p>"), TQString::fromLatin1("<br/>") );
|
|
|
|
//Remove trailing </br>
|
|
if ( body.endsWith( TQString::fromLatin1("<br/>") ) )
|
|
body.truncate( body.length() - 5 );
|
|
|
|
body.remove( TQString::fromLatin1("\n") );
|
|
body.replace( TQRegExp( TQString::fromLatin1( "\\s\\s" ) ), TQString::fromLatin1( " " ) );
|
|
}
|
|
/*
|
|
else if( f == ParsedHTML )
|
|
{
|
|
kdWarning( 14000 ) << k_funcinfo << "using ParsedHTML which is internal! Message: '" <<
|
|
body << "', Backtrace: " << kdBacktrace() << endl;
|
|
}
|
|
*/
|
|
|
|
d->body = body;
|
|
d->format = f;
|
|
|
|
// unescaping is very expensive, do it only once and cache the result
|
|
d->isRightToLeft = ( f & RichText ? unescape( d->body ).isRightToLeft() : d->body.isRightToLeft() );
|
|
}
|
|
|
|
void Message::setBody( const TQString &body, MessageFormat f )
|
|
{
|
|
detach();
|
|
|
|
doSetBody( body, f );
|
|
}
|
|
|
|
bool Message::isRightToLeft() const
|
|
{
|
|
return d->isRightToLeft;
|
|
}
|
|
|
|
void Message::setImportance(Message::MessageImportance i)
|
|
{
|
|
detach();
|
|
d->importance = i;
|
|
}
|
|
|
|
TQString Message::unescape( const TQString &xml )
|
|
{
|
|
TQString data = xml;
|
|
|
|
// Remove linebreak and multiple spaces. First return nbsp's to normal spaces :)
|
|
data.simplifyWhiteSpace();
|
|
|
|
int pos;
|
|
while ( ( pos = data.find( '<' ) ) != -1 )
|
|
{
|
|
int endPos = data.find( '>', pos + 1 );
|
|
if( endPos == -1 )
|
|
break; // No more complete elements left
|
|
|
|
// Take the part between < and >, and extract the element name from that
|
|
int matchWidth = endPos - pos + 1;
|
|
TQString match = data.mid( pos + 1, matchWidth - 2 ).simplifyWhiteSpace();
|
|
int elemEndPos = match.find( ' ' );
|
|
TQString elem = ( elemEndPos == -1 ? match.lower() : match.left( elemEndPos ).lower() );
|
|
if ( elem == TQString::fromLatin1( "img" ) )
|
|
{
|
|
// Replace smileys with their original text'
|
|
const TQString attrTitle = TQString::fromLatin1( "title=\"" );
|
|
int titlePos = match.find( attrTitle, elemEndPos );
|
|
int titleEndPos = match.find( '"', titlePos + attrTitle.length() );
|
|
if( titlePos == -1 || titleEndPos == -1 )
|
|
{
|
|
// Not a smiley but a normal <img>
|
|
// Don't update pos, we restart at this position :)
|
|
data.remove( pos, matchWidth );
|
|
}
|
|
else
|
|
{
|
|
TQString orig = match.mid( titlePos + attrTitle.length(),
|
|
titleEndPos - titlePos - attrTitle.length() );
|
|
data.replace( pos, matchWidth, orig );
|
|
pos += orig.length();
|
|
}
|
|
}
|
|
else if ( elem == TQString::fromLatin1( "/p" ) || elem == TQString::fromLatin1( "/div" ) ||
|
|
elem == TQString::fromLatin1( "br" ) )
|
|
{
|
|
// Replace paragraph, div and line breaks with a newline
|
|
data.replace( pos, matchWidth, '\n' );
|
|
pos++;
|
|
}
|
|
else
|
|
{
|
|
// Remove all other elements entirely
|
|
// Don't update pos, we restart at this position :)
|
|
data.remove( pos, matchWidth );
|
|
}
|
|
}
|
|
|
|
// Replace stuff starting with '&'
|
|
data.replace( TQString::fromLatin1( ">" ), TQString::fromLatin1( ">" ) );
|
|
data.replace( TQString::fromLatin1( "<" ), TQString::fromLatin1( "<" ) );
|
|
data.replace( TQString::fromLatin1( """ ), TQString::fromLatin1( "\"" ) );
|
|
data.replace( TQString::fromLatin1( " " ), TQString::fromLatin1( " " ) );
|
|
data.replace( TQString::fromLatin1( "&" ), TQString::fromLatin1( "&" ) );
|
|
data.replace( TQString::fromLatin1( " " ), TQString::fromLatin1( " " ) ); //this one is used in jabber: note, we should escape all &#xx;
|
|
|
|
return data;
|
|
}
|
|
|
|
TQString Message::escape( const TQString &text )
|
|
{
|
|
TQString html = TQStyleSheet::escape( text );
|
|
//Replace carriage returns inside the text
|
|
html.replace( TQString::fromLatin1( "\n" ), TQString::fromLatin1( "<br />" ) );
|
|
//Replace a tab with 4 spaces
|
|
html.replace( TQString::fromLatin1( "\t" ), TQString::fromLatin1( " " ) );
|
|
|
|
//Replace multiple spaces with
|
|
//do not replace every space so we break the linebreak
|
|
html.replace( TQRegExp( TQString::fromLatin1( "\\s\\s" ) ), TQString::fromLatin1( " " ) );
|
|
|
|
return html;
|
|
}
|
|
|
|
|
|
|
|
TQString Message::plainBody() const
|
|
{
|
|
TQString body=d->body;
|
|
if( d->format & RichText )
|
|
{
|
|
body = unescape( body );
|
|
}
|
|
return body;
|
|
}
|
|
|
|
TQString Message::escapedBody() const
|
|
{
|
|
TQString escapedBody=d->body;
|
|
// kdDebug(14000) << k_funcinfo << escapedBody << " " << d->rtfOverride << endl;
|
|
|
|
if( d->format & PlainText )
|
|
{
|
|
escapedBody=escape( escapedBody );
|
|
}
|
|
else if( d->format & RichText && d->rtfOverride)
|
|
{
|
|
//remove the rich text
|
|
escapedBody = escape (unescape( escapedBody ) );
|
|
}
|
|
|
|
return escapedBody;
|
|
}
|
|
|
|
TQString Message::parsedBody() const
|
|
{
|
|
//kdDebug(14000) << k_funcinfo << "messageformat: " << d->format << endl;
|
|
|
|
if( d->format == ParsedHTML )
|
|
{
|
|
return d->body;
|
|
}
|
|
else
|
|
{
|
|
return Kopete::Emoticons::parseEmoticons(parseLinks(escapedBody(), RichText));
|
|
}
|
|
}
|
|
|
|
static TQString makeRegExp( const char *pattern )
|
|
{
|
|
const TQString urlChar = TQString::fromLatin1( "\\+\\-\\w\\./#@&;:=\\?~%_,\\!\\$\\*\\(\\)" );
|
|
const TQString boundaryStart = TQString::fromLatin1( "(^|[^%1])(" ).arg( urlChar );
|
|
const TQString boundaryEnd = TQString::fromLatin1( ")([^%1]|$)" ).arg( urlChar );
|
|
|
|
return boundaryStart + TQString::fromLatin1(pattern) + boundaryEnd;
|
|
}
|
|
|
|
TQString Message::parseLinks( const TQString &message, MessageFormat format )
|
|
{
|
|
if ( format == ParsedHTML )
|
|
return message;
|
|
|
|
if ( format & RichText )
|
|
{
|
|
// < in HTML *always* means start-of-tag
|
|
TQStringList entries = TQStringList::split( TQChar('<'), message, true );
|
|
|
|
TQStringList::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 )
|
|
{
|
|
TQString curr = *it;
|
|
// > in HTML means start-of-tag if and only if it's the first one after a <
|
|
int tagclose = curr.find( TQChar('>') );
|
|
// no >: the HTML is broken, but we can cope
|
|
if ( tagclose == -1 )
|
|
continue;
|
|
TQString tag = curr.left( tagclose + 1 );
|
|
TQString body = curr.mid( tagclose + 1 );
|
|
*it = tag + parseLinks( body, PlainText );
|
|
}
|
|
return entries.join(TQString::fromLatin1("<"));
|
|
}
|
|
|
|
TQString result = message;
|
|
|
|
// common subpatterns - may not contain matching parens!
|
|
const TQString name = TQString::fromLatin1( "[\\w\\+\\-=_\\.]+" );
|
|
const TQString userAndPassword = TQString::fromLatin1( "(?:%1(?::%1)?\\@)" ).arg( name );
|
|
const TQString urlChar = TQString::fromLatin1( "\\+\\-\\w\\./#@&;:=\\?~%_,\\!\\$\\*\\(\\)" );
|
|
const TQString urlSection = TQString::fromLatin1( "[%1]+" ).arg( urlChar );
|
|
const TQString domain = TQString::fromLatin1( "[\\-\\w_]+(?:\\.[\\-\\w_]+)+" );
|
|
|
|
//Replace http/https/ftp links:
|
|
// Replace (stuff)://[user:password@](linkstuff) with a link
|
|
result.replace(
|
|
TQRegExp( makeRegExp("\\w+://%1?\\w%2").arg( userAndPassword, urlSection ) ),
|
|
TQString::fromLatin1("\\1<a href=\"\\2\" title=\"\\2\">\\2</a>\\3" ) );
|
|
|
|
// Replace www.X.Y(linkstuff) with a http: link
|
|
result.replace(
|
|
TQRegExp( makeRegExp("%1?www\\.%2%3").arg( userAndPassword, domain, urlSection ) ),
|
|
TQString::fromLatin1("\\1<a href=\"http://\\2\" title=\"http://\\2\">\\2</a>\\3" ) );
|
|
|
|
//Replace Email Links
|
|
// Replace user@domain with a mailto: link
|
|
result.replace(
|
|
TQRegExp( makeRegExp("%1@%2").arg( name, domain ) ),
|
|
TQString::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 is included in the url.
|
|
result.replace( TQRegExp( TQString::fromLatin1("(<a href=\"[^\"]+)( )(\")") ) , TQString::fromLatin1("\\1\\3") );
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
TQDateTime Message::timestamp() const
|
|
{
|
|
return d->timeStamp;
|
|
}
|
|
|
|
const Contact *Message::from() const
|
|
{
|
|
return d->from;
|
|
}
|
|
|
|
TQPtrList<Contact> Message::to() const
|
|
{
|
|
return d->to;
|
|
}
|
|
|
|
Message::MessageType Message::type() const
|
|
{
|
|
return d->type;
|
|
}
|
|
|
|
TQString Message::requestedPlugin() const
|
|
{
|
|
return d->requestedPlugin;
|
|
}
|
|
|
|
TQColor Message::fg() const
|
|
{
|
|
return d->fgColor;
|
|
}
|
|
|
|
TQColor Message::bg() const
|
|
{
|
|
return d->bgColor;
|
|
}
|
|
|
|
TQFont Message::font() const
|
|
{
|
|
//TQDomElement bodyNode = d->xmlDoc.elementsByTagName( TQString::fromLatin1("body") ).item(0).toElement();
|
|
//return TQFont( bodyNode.attribute( TQString::fromLatin1("font") ), bodyNode.attribute( TQString::fromLatin1("fontsize") ).toInt() );
|
|
return d->font;
|
|
}
|
|
|
|
TQString Message::subject() const
|
|
{
|
|
return d->subject;
|
|
}
|
|
|
|
Message::MessageFormat Message::format() const
|
|
{
|
|
return d->format;
|
|
}
|
|
|
|
Message::MessageDirection Message::direction() const
|
|
{
|
|
return d->direction;
|
|
}
|
|
|
|
Message::MessageImportance Message::importance() const
|
|
{
|
|
return d->importance;
|
|
}
|
|
|
|
ChatSession *Message::manager() const
|
|
{
|
|
return d->manager;
|
|
}
|
|
|
|
void Message::setManager(ChatSession *kmm)
|
|
{
|
|
detach();
|
|
d->manager=kmm;
|
|
}
|
|
|
|
TQString Message::getHtmlStyleAttribute() const
|
|
{
|
|
TQString styleAttribute;
|
|
|
|
styleAttribute = TQString::fromUtf8("style=\"");
|
|
|
|
// Affect foreground(color) and background color to message.
|
|
if( !d->fgOverride && d->fgColor.isValid() )
|
|
{
|
|
styleAttribute += TQString::fromUtf8("color: %1; ").arg(d->fgColor.name());
|
|
}
|
|
if( !d->bgOverride && d->bgColor.isValid() )
|
|
{
|
|
styleAttribute += TQString::fromUtf8("background-color: %1; ").arg(d->bgColor.name());
|
|
}
|
|
|
|
// Affect font parameters.
|
|
if( !d->rtfOverride && d->font!=TQFont() )
|
|
{
|
|
TQString fontstr;
|
|
if(!d->font.family().isNull())
|
|
fontstr+=TQString::fromLatin1("font-family: ")+d->font.family()+TQString::fromLatin1("; ");
|
|
if(d->font.italic())
|
|
fontstr+=TQString::fromLatin1("font-style: italic; ");
|
|
if(d->font.strikeOut())
|
|
fontstr+=TQString::fromLatin1("text-decoration: line-through; ");
|
|
if(d->font.underline())
|
|
fontstr+=TQString::fromLatin1("text-decoration: underline; ");
|
|
if(d->font.bold())
|
|
fontstr+=TQString::fromLatin1("font-weight: bold;");
|
|
|
|
styleAttribute += fontstr;
|
|
}
|
|
|
|
styleAttribute += TQString::fromUtf8("\"");
|
|
|
|
return styleAttribute;
|
|
}
|
|
|
|
// KDE4: Move that to a utils class/namespace
|
|
TQString Message::decodeString( const TQCString &message, const TQTextCodec *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.
|
|
*/
|
|
|
|
if( success )
|
|
*success = true;
|
|
|
|
// Avoid heavy codec tests on empty message.
|
|
if( message.isEmpty() )
|
|
return TQString::fromAscii( message );
|
|
|
|
//Check first 128 chars
|
|
int charsToCheck = message.length();
|
|
charsToCheck = 128 > charsToCheck ? charsToCheck : 128;
|
|
|
|
//They are providing a possible codec. Check if it is valid
|
|
if( providedCodec && providedCodec->heuristicContentMatch( message, charsToCheck ) >= 0 )
|
|
{
|
|
//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 TQString::fromUtf8( message );
|
|
}
|
|
|
|
//Try codecForContent - exact match
|
|
TQTextCodec *testCodec = TQTextCodec::codecForContent(message, charsToCheck);
|
|
if( testCodec && testCodec->heuristicContentMatch( message, charsToCheck ) >= 0 )
|
|
{
|
|
//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 = TQTextCodec::codecForLocale();
|
|
if( testCodec && testCodec->heuristicContentMatch( message, charsToCheck ) >= 0 )
|
|
{
|
|
//All chars decodable.
|
|
kdDebug(14000) << k_funcinfo << "Using locale's codec" << endl;
|
|
return testCodec->toUnicode( message );
|
|
}
|
|
|
|
//Try latin1 codec
|
|
testCodec = TQTextCodec::codecForMib(4);
|
|
if( testCodec && testCodec->heuristicContentMatch( message, charsToCheck ) >= 0 )
|
|
{
|
|
//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.
|
|
TQString result = TQString::fromLatin1( message );
|
|
const uint length = message.length();
|
|
for( uint i = 0; i < length; ++i )
|
|
{
|
|
if( !result[i].isPrint() )
|
|
result[i] = '?';
|
|
}
|
|
|
|
return result;
|
|
}
|