From bcb704366cb5e333a626c18c308c7e0448a8e69f Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kopete/libkopete/private/Makefile.am | 13 + kopete/libkopete/private/kopetecommand.cpp | 142 ++++++ kopete/libkopete/private/kopetecommand.h | 109 +++++ kopete/libkopete/private/kopeteemoticons.cpp | 559 +++++++++++++++++++++++ kopete/libkopete/private/kopeteemoticons.h | 184 ++++++++ kopete/libkopete/private/kopeteutils_private.cpp | 85 ++++ kopete/libkopete/private/kopeteutils_private.h | 60 +++ kopete/libkopete/private/kopeteviewmanager.cpp | 364 +++++++++++++++ kopete/libkopete/private/kopeteviewmanager.h | 103 +++++ 9 files changed, 1619 insertions(+) create mode 100644 kopete/libkopete/private/Makefile.am create mode 100644 kopete/libkopete/private/kopetecommand.cpp create mode 100644 kopete/libkopete/private/kopetecommand.h create mode 100644 kopete/libkopete/private/kopeteemoticons.cpp create mode 100644 kopete/libkopete/private/kopeteemoticons.h create mode 100644 kopete/libkopete/private/kopeteutils_private.cpp create mode 100644 kopete/libkopete/private/kopeteutils_private.h create mode 100644 kopete/libkopete/private/kopeteviewmanager.cpp create mode 100644 kopete/libkopete/private/kopeteviewmanager.h (limited to 'kopete/libkopete/private') diff --git a/kopete/libkopete/private/Makefile.am b/kopete/libkopete/private/Makefile.am new file mode 100644 index 00000000..15e930df --- /dev/null +++ b/kopete/libkopete/private/Makefile.am @@ -0,0 +1,13 @@ +METASOURCES = AUTO + +AM_CPPFLAGS = -DKDE_NO_COMPAT -DQT_NO_COMPAT -DQT_NO_CAST_ASCII -DQT_NO_ASCII_CAST \ + $(KOPETE_INCLUDES) $(all_includes) + +noinst_LTLIBRARIES = libkopeteprivate.la + +libkopeteprivate_la_SOURCES = kopeteemoticons.cpp \ + kopetecommand.cpp kopeteviewmanager.cpp kopeteutils_private.cpp +libkopeteprivate_la_LDFLAGS = $(all_libraries) +libkopeteprivate_la_LIBADD = $(LIB_KDEUI) +# vim: set noet: + diff --git a/kopete/libkopete/private/kopetecommand.cpp b/kopete/libkopete/private/kopetecommand.cpp new file mode 100644 index 00000000..52588f2e --- /dev/null +++ b/kopete/libkopete/private/kopetecommand.cpp @@ -0,0 +1,142 @@ +/* + kopetecommand.cpp - Command + + Copyright (c) 2003 by Jason Keirstead + + ************************************************************************* + * * + * 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 +#include +#include +#include +#include +#include + +#include "kopetechatsessionmanager.h" +#include "kopeteview.h" +#include "kopetecommand.h" +#include "kopeteuiglobal.h" + +Kopete::Command::Command( QObject *parent, const QString &command, const char* handlerSlot, + const QString &help, Kopete::CommandHandler::CommandType type, const QString &formatString, + uint minArgs, int maxArgs, const KShortcut &cut, const QString &pix ) + : KAction( command[0].upper() + command.right( command.length() - 1).lower(), pix, cut, parent, + ( command.lower() + QString::fromLatin1("_command") ).latin1() ) +{ + init( command, handlerSlot, help, type, formatString, minArgs, maxArgs ); +} + +void Kopete::Command::init( const QString &command, const char* slot, const QString &help, + Kopete::CommandHandler::CommandType type, const QString &formatString, uint minArgs, int maxArgs ) +{ + m_command = command; + m_help = help; + m_type = type; + m_formatString = formatString; + m_minArgs = minArgs; + m_maxArgs = maxArgs; + m_processing = false; + + if( m_type == Kopete::CommandHandler::Normal ) + { + QObject::connect( this, SIGNAL( handleCommand( const QString &, Kopete::ChatSession *) ), + parent(), slot ); + } + + QObject::connect( this, SIGNAL( activated() ), this, SLOT( slotAction() ) ); +} + +void Kopete::Command::slotAction() +{ + Kopete::ChatSession *manager = Kopete::ChatSessionManager::self()->activeView()->msgManager(); + + QString args; + if( m_minArgs > 0 ) + { + args = KInputDialog::getText( i18n("Enter Arguments"), i18n("Enter the arguments to %1:").arg(m_command) ); + if( args.isNull() ) + return; + } + + processCommand( args, manager, true ); +} + +void Kopete::Command::processCommand( const QString &args, Kopete::ChatSession *manager, bool gui ) +{ + QStringList mArgs = Kopete::CommandHandler::parseArguments( args ); + if( m_processing ) + { + printError( i18n("Alias \"%1\" expands to itself.").arg( text() ), manager, gui ); + } + else if( mArgs.count() < m_minArgs ) + { + printError( i18n("\"%1\" requires at least %n argument.", + "\"%1\" requires at least %n arguments.", m_minArgs) + .arg( text() ), manager, gui ); + } + else if( m_maxArgs > -1 && (int)mArgs.count() > m_maxArgs ) + { + printError( i18n("\"%1\" has a maximum of %n argument.", + "\"%1\" has a maximum of %n arguments.", m_minArgs) + .arg( text() ), manager, gui ); + } + else if( !KApplication::kApplication()->authorizeKAction( name() ) ) + { + printError( i18n("You are not authorized to perform the command \"%1\".").arg(text()), manager, gui ); + } + else + { + m_processing = true; + if( m_type == Kopete::CommandHandler::UserAlias || + m_type == Kopete::CommandHandler::SystemAlias ) + { + QString formatString = m_formatString; + + // Translate %s to the whole string and %n to current nickname + + formatString.replace( QString::fromLatin1("%n"), manager->myself()->nickName() ); + formatString.replace( QString::fromLatin1("%s"), args ); + + // Translate %1..%N to word1..wordN + + while( mArgs.count() > 0 ) + { + formatString = formatString.arg( mArgs.front() ); + mArgs.pop_front(); + } + + kdDebug(14010) << "New Command after processing alias: " << formatString << endl; + + Kopete::CommandHandler::commandHandler()->processMessage( QString::fromLatin1("/") + formatString, manager ); + } + else + { + emit( handleCommand( args, manager ) ); + } + m_processing = false; + } +} + +void Kopete::Command::printError( const QString &error, Kopete::ChatSession *manager, bool gui ) const +{ + if( gui ) + { + KMessageBox::error( Kopete::UI::Global::mainWidget(), error, i18n("Command Error") ); + } + else + { + Kopete::Message msg( manager->myself(), manager->members(), error, + Kopete::Message::Internal, Kopete::Message::PlainText ); + manager->appendMessage( msg ); + } +} + +#include "kopetecommand.moc" diff --git a/kopete/libkopete/private/kopetecommand.h b/kopete/libkopete/private/kopetecommand.h new file mode 100644 index 00000000..298872db --- /dev/null +++ b/kopete/libkopete/private/kopetecommand.h @@ -0,0 +1,109 @@ + +/* + kopetecommand.h - Command + + Copyright (c) 2003 by Jason Keirstead + Kopete (c) 2002-2003 by the Kopete developers + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef __KOPETECOMMAND_H__ +#define __KOPETECOMMAND_H__ + +#include +#include +#include "kopetecommandhandler.h" + +namespace Kopete +{ + +class ChatSession; + +class Command : public KAction +{ + Q_OBJECT + + public: + /** + * Creates a Kopete::Command object + * + * @param parent The plugin who owns this command + * @param command The command we want to handle, not including the '/' + * @param handlerSlot The slot used to handle the command. This slot must + * accept two parameters, a QString of arguments, and a Kopete::ChatSession + * pointer to the Manager under which the command was sent. + * @param help An optional help string to be shown when the user uses + * /help command + * @param type If this command is an alias, and what type + * @param formatString The formatString of the alias if any + * @param minArgs Minimum number of arguments + * @param maxArgs Maximum number of arguments + * @param cut The shortcut for the command + * @param pix The icon to use for the command + */ + Command( QObject *parent, const QString &command, const char* handlerSlot, + const QString &help = QString::null, CommandHandler::CommandType type = CommandHandler::Normal, const QString &formatString = QString::null, + uint minArgs = 0, int maxArgs = -1, const KShortcut &cut = 0, + const QString &pix = QString::null ); + + /** + * Process this command + */ + void processCommand( const QString &args, ChatSession *manager, bool gui = false ); + + /** + * Returns the command this object handles + */ + const QString &command() const { return m_command; }; + + /** + * Returns the help string for this command + */ + const QString &help() const { return m_help; }; + + /** + * Returns the type of the command + */ + const CommandHandler::CommandType type() const { return m_type; }; + + signals: + /** + * Emitted whenever a command is handled by this object. When a command + * has been handled, all processing on it stops by the command handler + * (a command cannot be handled twice) + */ + void handleCommand( const QString &args, Kopete::ChatSession *manager ); + + private slots: + /** + * Connected to our activated() signal + */ + void slotAction(); + + private: + void init( const QString &command, const char* slot, const QString &help, + CommandHandler::CommandType type, const QString &formatString, + uint minArgs, int maxArgs ); + + void printError( const QString &error, ChatSession *manager, bool gui = false ) const; + + QString m_command; + QString m_help; + QString m_formatString; + uint m_minArgs; + int m_maxArgs; + bool m_processing; + CommandHandler::CommandType m_type; +}; + +} + +#endif diff --git a/kopete/libkopete/private/kopeteemoticons.cpp b/kopete/libkopete/private/kopeteemoticons.cpp new file mode 100644 index 00000000..87da4cf7 --- /dev/null +++ b/kopete/libkopete/private/kopeteemoticons.cpp @@ -0,0 +1,559 @@ +/* + kopeteemoticons.cpp - Kopete Preferences Container-Class + + Copyright (c) 2002 by Stefan Gehn + Copyright (c) 2002-2006 by Olivier Goffart + Copyright (c) 2005 by Engin AYDOGAN + + Kopete (c) 2002-2005 by the Kopete developers + + ************************************************************************* + * * + * 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 "kopeteemoticons.h" + +#include "kopeteprefs.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + + +/* + * Testcases can be found in the kopeteemoticontest app in the tests/ directory. + */ + + +namespace Kopete { + + +struct Emoticons::Emoticon +{ + Emoticon(){} + /* sort by longest to shortest matchText */ + bool operator< (const Emoticon &e){ return matchText.length() > e.matchText.length(); } + QString matchText; + QString matchTextEscaped; + QString picPath; + QString picHTMLCode; +}; + +/* This is the object we will store each emoticon match in */ +struct Emoticons::EmoticonNode { + const Emoticon emoticon; + int pos; + EmoticonNode() : emoticon(), pos( -1 ) {} + EmoticonNode( const Emoticon e, int p ) : emoticon( e ), pos( p ) {} +}; + +class Emoticons::Private +{ +public: + QMap > emoticonMap; + QMap emoticonAndPicList; + + /** + * The current icon theme from KopetePrefs + */ + QString theme; + + +}; + + +Emoticons *Emoticons::s_self = 0L; + +Emoticons *Emoticons::self() +{ + if( !s_self ) + s_self = new Emoticons; + return s_self; +} + + +QString Emoticons::parseEmoticons(const QString& message, ParseMode mode ) //static +{ + return self()->parse( message, mode ); +} + +QValueList Emoticons::tokenizeEmoticons( const QString& message, ParseMode mode ) // static +{ + return self()->tokenize( message, mode ); +} + +QValueList Emoticons::tokenize( const QString& message, uint mode ) +{ + QValueList result; + if ( !KopetePrefs::prefs()->useEmoticons() ) + { + result.append( Token( Text, message ) ); + return result; + } + + if( ! ( mode & (StrictParse|RelaxedParse) ) ) + { + //if none of theses two mode are selected, use the mode from the config + mode |= KopetePrefs::prefs()->emoticonsRequireSpaces() ? StrictParse : RelaxedParse ; + } + + /* previous char, in the firs iteration assume that it is space since we want + * to let emoticons at the beginning, the very first previous QChar must be a space. */ + QChar p = ' '; + QChar c; /* current char */ + QChar n; /* next character after a match candidate, if strict this should be QChar::null or space */ + + /* This is the EmoticonNode container, it will represent each matched emoticon */ + QValueList foundEmoticons; + QValueList::const_iterator found; + /* First-pass, store the matched emoticon locations in foundEmoticons */ + QValueList emoticonList; + QValueList::const_iterator it; + size_t pos; + + bool inHTMLTag = false; + bool inHTMLLink = false; + bool inHTMLEntity = false; + QString needle; // search for this + for ( pos = 0; pos < message.length(); pos++ ) + { + c = message[ pos ]; + + if ( mode & SkipHTML ) // Shall we skip HTML ? + { + if ( !inHTMLTag ) // Are we already in an HTML tag ? + { + if ( c == '<' ) { // If not check if are going into one + inHTMLTag = true; // If we are, change the state to inHTML + p = c; + continue; + } + } + else // We are already in a HTML tag + { + if ( c == '>' ) { // Check if it ends + inHTMLTag = false; // If so, change the state + if ( p == 'a' ) + { + inHTMLLink = false; + } + } + else if ( c == 'a' && p == '<' ) // check if we just entered an achor tag + { + inHTMLLink = true; // don't put smileys in urls + } + p = c; + continue; + } + + if( !inHTMLEntity ) + { // are we + if( c == '&' ) + { + inHTMLEntity = true; + } + } + } + + if ( inHTMLLink ) // i can't think of any situation where a link adress might need emoticons + { + p = c; + continue; + } + + if ( (mode & StrictParse) && !p.isSpace() && p != '>') + { // '>' may mark the end of an html tag + p = c; + continue; + } /* strict requires space before the emoticon */ + if ( d->emoticonMap.contains( c ) ) + { + emoticonList = d->emoticonMap[ c ]; + bool found = false; + for ( it = emoticonList.begin(); it != emoticonList.end(); ++it ) + { + // If this is an HTML, then search for the HTML form of the emoticon. + // For instance >o) + needle = ( mode & SkipHTML ) ? (*it).matchTextEscaped : (*it).matchText; + if ( ( pos == (size_t)message.find( needle, pos ) ) ) + { + if( mode & StrictParse ) + { + /* check if the character after this match is space or end of string*/ + n = message[ pos + needle.length() ]; + //
marks the end of a line + if( n != '<' && !n.isSpace() && !n.isNull() && n!= '&') + break; + } + /* Perfect match */ + foundEmoticons.append( EmoticonNode( (*it), pos ) ); + found = true; + /* Skip the matched emoticon's matchText */ + pos += needle.length() - 1; + break; + } + } + if( !found ) + { + if( inHTMLEntity ){ + // If we are in an HTML entitiy such as > + int htmlEnd = message.find( ';', pos ); + // Search for where it ends + if( htmlEnd == -1 ) + { + // Apparently this HTML entity isn't ended, something is wrong, try skip the '&' + // and continue + kdDebug( 14000 ) << k_funcinfo << "Broken HTML entity, trying to recover." << endl; + inHTMLEntity = false; + pos++; + } + else + { + pos = htmlEnd; + inHTMLEntity = false; + } + } + } + } /* else no emoticons begin with this character, so don't do anything */ + p = c; + } + + /* if no emoticons found just return the text */ + if ( foundEmoticons.isEmpty() ) + { + result.append( Token( Text, message ) ); + return result; + } + + /* Second-pass, generate tokens based on the matches */ + + pos = 0; + int length; + + for ( found = foundEmoticons.begin(); found != foundEmoticons.end(); ++found ) + { + needle = ( mode & SkipHTML ) ? (*found).emoticon.matchTextEscaped : (*found).emoticon.matchText; + if ( ( length = ( (*found).pos - pos ) ) ) + { + result.append( Token( Text, message.mid( pos, length ) ) ); + result.append( Token( Image, (*found).emoticon.matchTextEscaped, (*found).emoticon.picPath, (*found).emoticon.picHTMLCode ) ); + pos += length + needle.length(); + } + else + { + result.append( Token( Image, (*found).emoticon.matchTextEscaped, (*found).emoticon.picPath, (*found).emoticon.picHTMLCode ) ); + pos += needle.length(); + } + } + + if ( message.length() - pos ) // if there is remaining regular text + { + result.append( Token( Text, message.mid( pos ) ) ); + } + + return result; +} + +Emoticons::Emoticons( const QString &theme ) : QObject( kapp, "KopeteEmoticons" ) +{ +// kdDebug(14010) << "KopeteEmoticons::KopeteEmoticons" << endl; + d=new Private; + if(theme.isNull()) + { + initEmoticons(); + connect( KopetePrefs::prefs(), SIGNAL(saved()), this, SLOT(initEmoticons()) ); + } + else + { + initEmoticons( theme ); + } +} + + +Emoticons::~Emoticons( ) +{ + delete d; +} + + + +void Emoticons::addIfPossible( const QString& filenameNoExt, const QStringList &emoticons ) +{ + KStandardDirs *dir = KGlobal::dirs(); + QString pic; + + //maybe an extension was given, so try to find the exact file + pic = dir->findResource( "emoticons", d->theme + QString::fromLatin1( "/" ) + filenameNoExt ); + + if( pic.isNull() ) + pic = dir->findResource( "emoticons", d->theme + QString::fromLatin1( "/" ) + filenameNoExt + QString::fromLatin1( ".mng" ) ); + if ( pic.isNull() ) + pic = dir->findResource( "emoticons", d->theme + QString::fromLatin1( "/" ) + filenameNoExt + QString::fromLatin1( ".png" ) ); + if ( pic.isNull() ) + pic = dir->findResource( "emoticons", d->theme + QString::fromLatin1( "/" ) + filenameNoExt + QString::fromLatin1( ".gif" ) ); + + if( !pic.isNull() ) // only add if we found one file + { + QPixmap p; + QString result; + + d->emoticonAndPicList.insert( pic, emoticons ); + + for ( QStringList::const_iterator it = emoticons.constBegin(), end = emoticons.constEnd(); + it != end; ++it ) + { + QString matchEscaped=QStyleSheet::escape(*it); + + Emoticon e; + e.picPath = pic; + + // We need to include size (width, height attributes) hints in the emoticon HTML code + // Unless we do so, ChatMessagePart::slotScrollView does not work properly and causing + // HTMLPart not to be scrolled to the very last message. + p.load( e.picPath ); + result = QString::fromLatin1( "" ); + + e.picHTMLCode = result; + e.matchTextEscaped = matchEscaped; + e.matchText = *it; + d->emoticonMap[ matchEscaped[0] ].append( e ); + d->emoticonMap[ (*it)[0] ].append( e ); + } + } +} + +void Emoticons::initEmoticons( const QString &theme ) +{ + if(theme.isNull()) + { + if ( d->theme == KopetePrefs::prefs()->iconTheme() ) + return; + + d->theme = KopetePrefs::prefs()->iconTheme(); + } + else + { + d->theme = theme; + } + +// kdDebug(14010) << k_funcinfo << "Called" << endl; + d->emoticonAndPicList.clear(); + d->emoticonMap.clear(); + + QString filename= KGlobal::dirs()->findResource( "emoticons", d->theme + QString::fromLatin1( "/emoticons.xml" ) ); + if(!filename.isEmpty()) + return initEmoticon_emoticonsxml( filename ); + filename= KGlobal::dirs()->findResource( "emoticons", d->theme + QString::fromLatin1( "/icondef.xml" ) ); + if(!filename.isEmpty()) + return initEmoticon_JEP0038( filename ); + kdWarning(14010) << k_funcinfo << "emotiucon XML theme description not found" < keys = d->emoticonMap.keys(); + for ( QValueList::const_iterator it = keys.begin(); it != keys.end(); ++it ) + { + QChar key = (*it); + QValueList keyValues = d->emoticonMap[key]; + qHeapSort(keyValues.begin(), keyValues.end()); + d->emoticonMap[key] = keyValues; + } +} + + + + +QMap Emoticons::emoticonAndPicList() +{ + return d->emoticonAndPicList; +} + + +QString Emoticons::parse( const QString &message, ParseMode mode ) +{ + if ( !KopetePrefs::prefs()->useEmoticons() ) + return message; + + QValueList tokens = tokenize( message, mode ); + QValueList::const_iterator token; + QString result; + QPixmap p; + for ( token = tokens.begin(); token != tokens.end(); ++token ) + { + switch ( (*token).type ) + { + case Text: + result += (*token).text; + break; + case Image: + result += (*token).picHTMLCode; + kdDebug( 14010 ) << k_funcinfo << "Emoticon html code: " << result << endl; + break; + default: + kdDebug( 14010 ) << k_funcinfo << "Unknown token type. Something's broken." << endl; + } + } + return result; +} + +} //END namesapce Kopete + +#include "kopeteemoticons.moc" + + + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/libkopete/private/kopeteemoticons.h b/kopete/libkopete/private/kopeteemoticons.h new file mode 100644 index 00000000..848185e6 --- /dev/null +++ b/kopete/libkopete/private/kopeteemoticons.h @@ -0,0 +1,184 @@ +/* + kopeteemoticons.cpp - Kopete Preferences Container-Class + + Copyright (c) 2002-2003 by Stefan Gehn + Kopete (c) 2002-2004 by the Kopete developers + Copyright (c) 2005 by Engin AYDOGAN + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef kopeteemoticons_h__ +#define kopeteemoticons_h__ + +#include +#include +#include + +#include "kopete_export.h" + +namespace Kopete { + +class KOPETE_EXPORT Emoticons : public QObject +{ + Q_OBJECT +public: + /** + * Constructor: DON'T use it if you want to use the emoticon theme + * chosen by the user. + * Instead, use @ref Kopete::Emoticons::self() + **/ + Emoticons( const QString &theme = QString::null ); + + ~Emoticons( ); + + /** + * The emoticons container-class by default is a singleton object. + * Use this method to retrieve the instance. + */ + static Emoticons *self(); + + /** + * The possible parse modes + */ + enum ParseMode { DefaultParseMode = 0x0 , /** Use strict or relaxed according the config */ + StrictParse = 0x1, /** Strict parsing requires a space between each emoticon */ + RelaxedParse = 0x4, /** Parse mode where all possible emoticon matches are allowed */ + SkipHTML = 0x2 /** Skip emoticons within HTML */ + }; + + /** + * Use it to parse emoticons in a text. + * You don't need to use this for chat windows, + * There is a special class that abstract a chat view + * and uses emoticons parser. + * This function will use the selected emoticon theme. + * If nicks is provided, they will not be parsed if they + * exist in message. + */ + static QString parseEmoticons( const QString &message, ParseMode = SkipHTML ) ; + + + QString parse( const QString &message, ParseMode = SkipHTML ); + + /** + * TokenType, a token might be an image ( emoticon ) or text. + */ + enum TokenType { Undefined, /** Undefined, for completeness only */ + Image, /** Token contains a path to an image */ + Text /** Token contains test */ + }; + + /** + * A token consists of a QString text which is either a regular text + * or a path to image depending on the type. + * If type is Image the text refers to an image path. + * If type is Text the text refers to a regular text. + */ + struct Token { + Token() : type( Undefined ) {} + Token( TokenType t, const QString &m ) : type( t ), text(m) {} + Token( TokenType t, const QString &m, const QString &p, const QString &html ) + : type( t ), text( m ), picPath( p ), picHTMLCode( html ) {} + TokenType type; + QString text; + QString picPath; + QString picHTMLCode; + }; + + + /** + * Static function which will call tokenize + * @see tokenize( const QString& ) + */ + static QValueList tokenizeEmoticons( const QString &message, ParseMode mode = DefaultParseMode ); + + /** + * Tokenizes an message. + * For example; + * Assume :], (H), :-x are three emoticons. + * A text "(H)(H) foo bar john :] :-x" would be tokenized as follows (not strict): + * 1- /path/to/shades.png + * 2- /path/to/shades.png + * 3- " foo bar john " + * 4- /path/to/bat.png + * 5- " " + * 6- /path/to/kiss.png + * + * Strict tokenization (require spaces around emoticons): + * 1- "(H)(H) foo bar john " + * 2- /path/to/bat.png + * 3- " " + * 4- /path/to/kiss.png + * Note: quotation marks are used to emphasize white spaces. + * @param message is the message to tokenize + * @param mode is a bitmask of ParseMode enum + * @return a QValueList which consiste of ordered tokens of the text. + * @author Engin AYDOGAN < engin@bzzzt.biz > + * @since 23-03-05 + */ + QValueList tokenize( const QString &message, uint mode = DefaultParseMode ); + + /** + * Return all emoticons and the corresponding icon. + * (only one emoticon per image) + */ + QMap emoticonAndPicList(); + + +private: + /** + * Our instance + **/ + static Emoticons *s_self; + + /** + * add an emoticon to our mapping if + * an animation/pixmap has been found for it + **/ + void addIfPossible( const QString& filenameNoExt, const QStringList &emoticons ); + + /** + * uses the kopete's emoticons.xml for the theme + * @see initEmoticons + */ + void initEmoticon_emoticonsxml( const QString & filename); + + /** + * uses the JEP-0038 xml description for the theme + * @see initEmoticons + */ + void initEmoticon_JEP0038( const QString & filename); + + /** + * sorts emoticons for convenient parsing, which yields greedy matching on + * matchText + */ + void sortEmoticons(); + + + struct Emoticon; + struct EmoticonNode; + class Private; + Private *d; +private slots: + + /** + * Fills the map with paths and emoticons + * This needs to be done on every emoticon-theme change + **/ + void initEmoticons ( const QString &theme = QString::null ); +}; + + +} //END namespace Kopete + +#endif +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/libkopete/private/kopeteutils_private.cpp b/kopete/libkopete/private/kopeteutils_private.cpp new file mode 100644 index 00000000..3746bcd3 --- /dev/null +++ b/kopete/libkopete/private/kopeteutils_private.cpp @@ -0,0 +1,85 @@ +/* + Kopete Utils. + Copyright (c) 2005 Duncan Mac-Vicar Prett + + Kopete (c) 2002-2005 by the Kopete developers + + ************************************************************************* + * * + * 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 + +#include + +#include + +#include "knotification.h" +#include "kopeteutils_private.h" +#include "kopeteuiglobal.h" + +namespace Kopete +{ +namespace Utils +{ + +NotifyHelper* NotifyHelper::s_self = 0L; + +NotifyHelper::NotifyHelper() +{ +} + +NotifyHelper::~NotifyHelper() +{ +} + +NotifyHelper* NotifyHelper::self() +{ + if (!s_self) + s_self = new NotifyHelper(); + + return s_self; +} + +void NotifyHelper::slotEventActivated(unsigned int action) +{ + const KNotification *n = dynamic_cast(QObject::sender()); + if (n) + { + ErrorNotificationInfo info = m_events[n]; + if ( info.debugInfo.isEmpty() ) + KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Information, info.explanation, info.caption); + else + KMessageBox::queuedDetailedError( Kopete::UI::Global::mainWidget(), info.explanation, info.debugInfo, info.caption); + + unregisterNotification(n); + } +} + +void NotifyHelper::slotEventClosed() +{ + const KNotification *n = dynamic_cast(QObject::sender()); + if (n) + unregisterNotification(n); +} + +void NotifyHelper::registerNotification(const KNotification* event, ErrorNotificationInfo error) +{ + m_events.insert( event, error); +} + +void NotifyHelper::unregisterNotification(const KNotification* event) +{ + m_events.remove(event); +} + +} // end ns ErrorNotifier +} // end ns Kopete + +#include "kopeteutils_private.moc" diff --git a/kopete/libkopete/private/kopeteutils_private.h b/kopete/libkopete/private/kopeteutils_private.h new file mode 100644 index 00000000..a684c965 --- /dev/null +++ b/kopete/libkopete/private/kopeteutils_private.h @@ -0,0 +1,60 @@ +/* + Kopete Utils. + + Copyright (c) 2005 Duncan Mac-Vicar Prett + + Kopete (c) 2002-2005 by the Kopete developers + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef KOPETE_UTILS_PRIVATE_H +#define KOPETE_UTILS_PRIVATE_H + +#include "qobject.h" +#include "qstring.h" +#include "qpixmap.h" + +class KNotification; + +namespace Kopete +{ + +namespace Utils +{ + +typedef struct +{ + QString caption; + QString explanation; + QString debugInfo; +} ErrorNotificationInfo; + +class NotifyHelper : public QObject +{ +Q_OBJECT +public: + static NotifyHelper* self(); + void registerNotification(const KNotification* event, ErrorNotificationInfo error); + void unregisterNotification(const KNotification* event); +public slots: + void slotEventActivated(unsigned int action); + void slotEventClosed(); +private: + NotifyHelper(); + ~NotifyHelper(); + QMap m_events; + static NotifyHelper *s_self; +}; + +} // end ns Utils +} // end ns Kopete + +#endif diff --git a/kopete/libkopete/private/kopeteviewmanager.cpp b/kopete/libkopete/private/kopeteviewmanager.cpp new file mode 100644 index 00000000..c6d295fd --- /dev/null +++ b/kopete/libkopete/private/kopeteviewmanager.cpp @@ -0,0 +1,364 @@ +/* + kopeteviewmanager.cpp - View Manager + + Copyright (c) 2003 by Jason Keirstead + Kopete (c) 2002-2005 by the Kopete developers + + ************************************************************************* + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kopeteprefs.h" +#include "kopeteaccount.h" +#include "kopetepluginmanager.h" +#include "kopeteviewplugin.h" +#include "kopetechatsessionmanager.h" +#include "kopetemetacontact.h" +#include "kopetenotifyevent.h" +#include "kopetemessageevent.h" +#include "kopeteview.h" +//#include "systemtray.h" + +#include "kopeteviewmanager.h" + +typedef QMap ManagerMap; +typedef QPtrList EventList; + +struct KopeteViewManagerPrivate +{ + ManagerMap managerMap; + EventList eventList; + KopeteView *activeView; + + bool useQueueOrStack; + bool raiseWindow; + bool queueUnreadMessages; + bool queueOnlyHighlightedMessagesInGroupChats; + bool queueOnlyMessagesOnAnotherDesktop; + bool balloonNotifyIgnoreClosesChatView; + bool foreignMessage; +}; + +KopeteViewManager *KopeteViewManager::s_viewManager = 0L; + +KopeteViewManager *KopeteViewManager::viewManager() +{ + if( !s_viewManager ) + s_viewManager = new KopeteViewManager(); + return s_viewManager; +} + +KopeteViewManager::KopeteViewManager() +{ + s_viewManager=this; + d = new KopeteViewManagerPrivate; + d->activeView = 0L; + d->foreignMessage=false; + + connect( KopetePrefs::prefs(), SIGNAL( saved() ), this, SLOT( slotPrefsChanged() ) ); + + connect( Kopete::ChatSessionManager::self() , SIGNAL( display( Kopete::Message &, Kopete::ChatSession *) ), + this, SLOT ( messageAppended( Kopete::Message &, Kopete::ChatSession *) ) ); + + connect( Kopete::ChatSessionManager::self() , SIGNAL( readMessage() ), + this, SLOT ( nextEvent() ) ); + + slotPrefsChanged(); +} + +KopeteViewManager::~KopeteViewManager() +{ +// kdDebug(14000) << k_funcinfo << endl; + + //delete all open chatwindow. + ManagerMap::Iterator it; + for ( it = d->managerMap.begin(); it != d->managerMap.end(); ++it ) + it.data()->closeView( true ); //this does not clean the map, but we don't care + + delete d; +} + +void KopeteViewManager::slotPrefsChanged() +{ + d->useQueueOrStack = KopetePrefs::prefs()->useQueue() || KopetePrefs::prefs()->useStack(); + d->raiseWindow = KopetePrefs::prefs()->raiseMsgWindow(); + d->queueUnreadMessages = KopetePrefs::prefs()->queueUnreadMessages(); + d->queueOnlyHighlightedMessagesInGroupChats = KopetePrefs::prefs()->queueOnlyHighlightedMessagesInGroupChats(); + d->queueOnlyMessagesOnAnotherDesktop = KopetePrefs::prefs()->queueOnlyMessagesOnAnotherDesktop(); + d->balloonNotifyIgnoreClosesChatView = KopetePrefs::prefs()->balloonNotifyIgnoreClosesChatView(); +} + +KopeteView *KopeteViewManager::view( Kopete::ChatSession* session, const QString &requestedPlugin ) +{ +// kdDebug(14000) << k_funcinfo << endl; + + if( d->managerMap.contains( session ) && d->managerMap[ session ] ) + { + return d->managerMap[ session ]; + } + else + { + Kopete::PluginManager *pluginManager = Kopete::PluginManager::self(); + Kopete::ViewPlugin *viewPlugin = 0L; + + QString pluginName = requestedPlugin.isEmpty() ? KopetePrefs::prefs()->interfacePreference() : requestedPlugin; + if( !pluginName.isEmpty() ) + { + viewPlugin = (Kopete::ViewPlugin*)pluginManager->loadPlugin( pluginName ); + + if( !viewPlugin ) + { + kdWarning(14000) << "Requested view plugin, " << pluginName << + ", was not found. Falling back to chat window plugin" << endl; + } + } + + if( !viewPlugin ) + viewPlugin = (Kopete::ViewPlugin*)pluginManager->loadPlugin( QString::fromLatin1("kopete_chatwindow") ); + + if( viewPlugin ) + { + KopeteView *newView = viewPlugin->createView(session); + + d->foreignMessage = false; + d->managerMap.insert( session, newView ); + + connect( session, SIGNAL( closing(Kopete::ChatSession *) ), + this, SLOT(slotChatSessionDestroyed(Kopete::ChatSession*)) ); + + return newView; + } + else + { + kdError(14000) << "Could not create a view, no plugins available!" << endl; + return 0L; + } + } +} + + +void KopeteViewManager::messageAppended( Kopete::Message &msg, Kopete::ChatSession *manager) +{ +// kdDebug(14000) << k_funcinfo << endl; + + bool outgoingMessage = ( msg.direction() == Kopete::Message::Outbound ); + + if( !outgoingMessage || d->managerMap.contains( manager ) ) + { + d->foreignMessage=!outgoingMessage; //let know for the view we are about to create + manager->view(true,msg.requestedPlugin())->appendMessage( msg ); + d->foreignMessage=false; //the view is created, reset the flag + + bool appendMessageEvent = d->useQueueOrStack; + + QWidget *w; + if( d->queueUnreadMessages && ( w = dynamic_cast(view( manager )) ) ) + { + // append msg event to queue if chat window is active but not the chat view in it... + appendMessageEvent = appendMessageEvent && !(w->isActiveWindow() && manager->view() == d->activeView); + // ...and chat window is on another desktop + appendMessageEvent = appendMessageEvent && (!d->queueOnlyMessagesOnAnotherDesktop || !KWin::windowInfo( w->topLevelWidget()->winId(), NET::WMDesktop ).isOnCurrentDesktop()); + } + else + { + // append if no chat window exists already + appendMessageEvent = appendMessageEvent && !view( manager )->isVisible(); + } + + // in group chats always append highlighted messages to queue + appendMessageEvent = appendMessageEvent && (!d->queueOnlyHighlightedMessagesInGroupChats || manager->members().count() == 1 || msg.importance() == Kopete::Message::Highlight); + + if( appendMessageEvent ) + { + if ( !outgoingMessage ) + { + Kopete::MessageEvent *event=new Kopete::MessageEvent(msg,manager); + d->eventList.append( event ); + connect(event, SIGNAL(done(Kopete::MessageEvent *)), this, SLOT(slotEventDeleted(Kopete::MessageEvent *))); + Kopete::ChatSessionManager::self()->postNewEvent(event); + } + } + else if( d->eventList.isEmpty() ) + { + readMessages( manager, outgoingMessage ); + } + + if ( !outgoingMessage && ( !manager->account()->isAway() || KopetePrefs::prefs()->soundIfAway() ) + && msg.direction() != Kopete::Message::Internal ) + { + QWidget *w=dynamic_cast(manager->view(false)); + KConfig *config = KGlobal::config(); + config->setGroup("General"); + if( (!manager->view(false) || !w || manager->view() != d->activeView || + config->readBoolEntry("EventIfActive", true) || !w->isActiveWindow()) + && msg.from()) + { + QString msgFrom = QString::null; + if( msg.from()->metaContact() ) + msgFrom = msg.from()->metaContact()->displayName(); + else + msgFrom = msg.from()->contactId(); + + QString msgText = msg.plainBody(); + if( msgText.length() > 90 ) + msgText = msgText.left(88) + QString::fromLatin1("..."); + + QString event; + QString body =i18n( "Incoming message from %1
\"%2\"
" ); + + switch( msg.importance() ) + { + case Kopete::Message::Low: + event = QString::fromLatin1( "kopete_contact_lowpriority" ); + break; + case Kopete::Message::Highlight: + event = QString::fromLatin1( "kopete_contact_highlight" ); + body = i18n( "A highlighted message arrived from %1
\"%2\"
" ); + break; + default: + event = QString::fromLatin1( "kopete_contact_incoming" ); + } + KNotification *notify=KNotification::event(msg.from()->metaContact() , event, body.arg( QStyleSheet::escape(msgFrom), QStyleSheet::escape(msgText) ), 0, /*msg.from()->metaContact(),*/ + w , i18n("View") ); + + connect(notify,SIGNAL(activated(unsigned int )), manager , SLOT(raiseView()) ); + } + } + } +} + +void KopeteViewManager::readMessages( Kopete::ChatSession *manager, bool outgoingMessage, bool activate ) +{ +// kdDebug( 14000 ) << k_funcinfo << endl; + d->foreignMessage=!outgoingMessage; //let know for the view we are about to create + KopeteView *thisView = manager->view( true ); + d->foreignMessage=false; //the view is created, reset the flag + if( ( outgoingMessage && !thisView->isVisible() ) || d->raiseWindow || activate ) + thisView->raise( activate ); + else if( !thisView->isVisible() ) + thisView->makeVisible(); + + QPtrListIterator it( d->eventList ); + Kopete::MessageEvent* event; + while ( ( event = it.current() ) != 0 ) + { + ++it; + if ( event->message().manager() == manager ) + { + event->apply(); + d->eventList.remove( event ); + } + } +} + +void KopeteViewManager::slotEventDeleted( Kopete::MessageEvent *event ) +{ +// kdDebug(14000) << k_funcinfo << endl; + Kopete::ChatSession *kmm=event->message().manager(); + if(!kmm) + return; + + d->eventList.remove( event ); + + if ( event->state() == Kopete::MessageEvent::Applied ) + { + readMessages( kmm, false, true ); + } + else if ( event->state() == Kopete::MessageEvent::Ignored && d->balloonNotifyIgnoreClosesChatView ) + { + bool bAnotherWithThisManager = false; + for( QPtrListIterator it( d->eventList ); it; ++it ) + { + Kopete::MessageEvent *event = it.current(); + if ( event->message().manager() == kmm ) + bAnotherWithThisManager = true; + } + if ( !bAnotherWithThisManager && kmm->view( false ) ) + kmm->view()->closeView( true ); + } +} + +void KopeteViewManager::nextEvent() +{ +// kdDebug( 14000 ) << k_funcinfo << endl; + + if( d->eventList.isEmpty() ) + return; + + Kopete::MessageEvent* event = d->eventList.first(); + + if ( event ) + event->apply(); +} + +void KopeteViewManager::slotViewActivated( KopeteView *view ) +{ +// kdDebug( 14000 ) << k_funcinfo << endl; + d->activeView = view; + + QPtrListIterator it ( d->eventList ); + Kopete::MessageEvent* event; + while ( ( event = it.current() ) != 0 ) + { + ++it; + if ( event->message().manager() == view->msgManager() ) + event->deleteLater(); + } + +} + +void KopeteViewManager::slotViewDestroyed( KopeteView *closingView ) +{ +// kdDebug( 14000 ) << k_funcinfo << endl; + + if( d->managerMap.contains( closingView->msgManager() ) ) + { + d->managerMap.remove( closingView->msgManager() ); +// closingView->msgManager()->setCanBeDeleted( true ); + } + + if( closingView == d->activeView ) + d->activeView = 0L; +} + +void KopeteViewManager::slotChatSessionDestroyed( Kopete::ChatSession *manager ) +{ +// kdDebug( 14000 ) << k_funcinfo << endl; + + if( d->managerMap.contains( manager ) ) + { + KopeteView *v=d->managerMap[ manager ]; + v->closeView( true ); + delete v; //closeView call deleteLater, but in this case this is not enough, because some signal are called that case crash + d->managerMap.remove( manager ); + } +} + +KopeteView* KopeteViewManager::activeView() const +{ + return d->activeView; +} + + +#include "kopeteviewmanager.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/libkopete/private/kopeteviewmanager.h b/kopete/libkopete/private/kopeteviewmanager.h new file mode 100644 index 00000000..b1706906 --- /dev/null +++ b/kopete/libkopete/private/kopeteviewmanager.h @@ -0,0 +1,103 @@ +/* + kopeteviewmanager.h - View Manager + + Copyright (c) 2003 by Jason Keirstead + Kopete (c) 2002-2003 by the Kopete developers + + ************************************************************************* + * * + * 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 KOPETEVIEWMANAGER_H +#define KOPETEVIEWMANAGER_H + +#include "kopetemessage.h" +#include "kopete_export.h" + +namespace Kopete +{ + class ChatSession; + class Protocol; + class Contact; + class MessageEvent; +} + +class KopeteView; +class QTextEdit; + +struct KopeteViewManagerPrivate; + +/** + * Relates an actual chat to the means used to view it. + */ +class KOPETE_EXPORT KopeteViewManager : public QObject +{ + Q_OBJECT + public: + /** This is a singleton class. Call this method to get a pointer to + * a KopeteViewManager. + */ + static KopeteViewManager *viewManager(); + + KopeteViewManager(); + ~KopeteViewManager(); + + /** + * Return a view for the supplied Kopete::ChatSession. If one already + * exists, it will be returned, otherwise, a new view is created. + * @param session The Kopete::ChatSession we are viewing. + * @param requestedPlugin Specifies the view plugin to use. + */ + KopeteView *view( Kopete::ChatSession *session, const QString &requestedPlugin = QString::null ); + + /** + * Provide access to the list of KopeteChatWindow the class maintains. + */ + KopeteView *activeView() const; + + private: + + + KopeteViewManagerPrivate *d; + static KopeteViewManager *s_viewManager; + + public slots: + /** + * Make a view visible and on top. + * @param manager The originating Kopete::ChatSession. + * @param outgoingMessage Whether the message is inbound or outbound. + * @param activate Indicate whether the view should be activated + * @todo Document @p activate + */ + void readMessages( Kopete::ChatSession* manager, bool outgoingMessage, bool activate = false ); + + /** + * Called when a new message has been appended to the given + * Kopete::ChatSession. Procures a view for the message, and generates any notification events or displays messages, as appropriate. + * @param msg The new message + * @param manager The originating Kopete::ChatSession + */ + void messageAppended( Kopete::Message &msg, Kopete::ChatSession *manager); + + void nextEvent(); + + private slots: + void slotViewDestroyed( KopeteView *); + void slotChatSessionDestroyed( Kopete::ChatSession * ); + + /** + * An event has been deleted. + */ + void slotEventDeleted( Kopete::MessageEvent * ); + + void slotPrefsChanged(); + void slotViewActivated( KopeteView * ); +}; + +#endif -- cgit v1.2.1