/*
kopeteaccount . cpp - Kopete Account
Copyright ( c ) 2003 - 2005 by Olivier Goffart < ogoffart @ kde . org >
Copyright ( c ) 2003 - 2004 by Martijn Klingens < klingens @ kde . org >
Copyright ( c ) 2004 by Richard Smith < kde @ metafoo . co . uk >
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 <tqapplication.h>
# include <tqtimer.h>
# include <kconfig.h>
# include <kdebug.h>
# include <tdeversion.h>
# include <kdialogbase.h>
# include <klocale.h>
# include <kiconloader.h>
# include <kiconeffect.h>
# include <kaction.h>
# include <kpopupmenu.h>
# include <kmessagebox.h>
# include <knotifyclient.h>
# include "kabcpersistence.h"
# include "kopetecontactlist.h"
# include "kopeteaccount.h"
# include "kopeteaccountmanager.h"
# include "kopetecontact.h"
# include "kopetemetacontact.h"
# include "kopeteprotocol.h"
# include "kopetepluginmanager.h"
# include "kopetegroup.h"
# include "kopeteprefs.h"
# include "kopeteutils.h"
# include "kopeteuiglobal.h"
# include "kopeteblacklister.h"
# include "kopeteonlinestatusmanager.h"
# include "editaccountwidget.h"
namespace Kopete
{
class Account : : Private
{
public :
Private ( Protocol * protocol , const TQString & accountId )
: protocol ( protocol ) , id ( accountId )
, excludeconnect ( true ) , priority ( 0 ) , myself ( 0 )
, suppressStatusTimer ( 0 ) , suppressStatusNotification ( false )
, blackList ( new Kopete : : BlackLister ( protocol - > pluginId ( ) , accountId ) )
, connectionTry ( 0 )
{ }
~ Private ( ) { delete blackList ; }
Protocol * protocol ;
TQString id ;
TQString accountLabel ;
bool excludeconnect ;
uint priority ;
TQDict < Contact > contacts ;
TQColor color ;
Contact * myself ;
TQTimer suppressStatusTimer ;
bool suppressStatusNotification ;
Kopete : : BlackLister * blackList ;
KConfigGroup * configGroup ;
uint connectionTry ;
TQString customIcon ;
Kopete : : OnlineStatus restoreStatus ;
TQString restoreMessage ;
} ;
Account : : Account ( Protocol * parent , const TQString & accountId , const char * name )
: TQObject ( parent , name ) , d ( new Private ( parent , accountId ) )
{
d - > configGroup = new KConfigGroup ( KGlobal : : config ( ) , TQString : : fromLatin1 ( " Account_%1_%2 " ) . arg ( d - > protocol - > pluginId ( ) , d - > id ) ) ;
d - > excludeconnect = d - > configGroup - > readBoolEntry ( " ExcludeConnect " , false ) ;
d - > color = d - > configGroup - > readColorEntry ( " Color " , & d - > color ) ;
d - > customIcon = d - > configGroup - > readEntry ( " Icon " , TQString ( ) ) ;
d - > priority = d - > configGroup - > readNumEntry ( " Priority " , 0 ) ;
d - > restoreStatus = Kopete : : OnlineStatus : : Online ;
d - > restoreMessage = " " ;
TQObject : : connect ( & d - > suppressStatusTimer , TQT_SIGNAL ( timeout ( ) ) ,
this , TQT_SLOT ( slotStopSuppression ( ) ) ) ;
}
Account : : ~ Account ( )
{
d - > contacts . remove ( d - > myself - > contactId ( ) ) ;
// Delete all registered child contacts first
while ( ! d - > contacts . isEmpty ( ) )
delete * TQDictIterator < Contact > ( d - > contacts ) ;
kdDebug ( 14010 ) < < k_funcinfo < < " account ' " < < d - > id < < " ' about to emit accountDestroyed " < < endl ;
emit accountDestroyed ( this ) ;
delete d - > myself ;
delete d - > configGroup ;
delete d ;
}
void Account : : reconnect ( )
{
kdDebug ( 14010 ) < < k_funcinfo < < " account " < < d - > id < < " restoreStatus " < < d - > restoreStatus . status ( ) < < " restoreMessage " < < d - > restoreMessage < < endl ;
setOnlineStatus ( d - > restoreStatus , d - > restoreMessage ) ;
}
void Account : : disconnected ( DisconnectReason reason )
{
kdDebug ( 14010 ) < < k_funcinfo < < reason < < endl ;
//reconnect if needed
if ( reason = = BadPassword )
{
TQTimer : : singleShot ( 0 , this , TQT_SLOT ( reconnect ( ) ) ) ;
}
else if ( KopetePrefs : : prefs ( ) - > reconnectOnDisconnect ( ) = = true & & reason > Manual )
{
d - > connectionTry + + ;
//use a timer to allow the plugins to clean up after return
if ( d - > connectionTry < 3 )
TQTimer : : singleShot ( 10000 , this , TQT_SLOT ( reconnect ( ) ) ) ; // wait 10 seconds before reconnect
}
if ( reason = = OtherClient )
{
Kopete : : Utils : : notifyConnectionLost ( this , i18n ( " You have been disconnected " ) , i18n ( " You have connected from another client or computer to the account '%1' " ) . arg ( d - > id ) , i18n ( " Most proprietary Instant Messaging services do not allow you to connect from more than one location. Check that nobody is using your account without your permission. If you need a service that supports connection from various locations at the same time, use the Jabber protocol. " ) ) ;
}
}
Protocol * Account : : protocol ( ) const
{
return d - > protocol ;
}
TQString Account : : accountId ( ) const
{
return d - > id ;
}
const TQColor Account : : color ( ) const
{
return d - > color ;
}
void Account : : setColor ( const TQColor & color )
{
d - > color = color ;
if ( d - > color . isValid ( ) )
d - > configGroup - > writeEntry ( " Color " , d - > color ) ;
else
d - > configGroup - > deleteEntry ( " Color " ) ;
emit colorChanged ( color ) ;
}
void Account : : setPriority ( uint priority )
{
d - > priority = priority ;
d - > configGroup - > writeEntry ( " Priority " , d - > priority ) ;
}
uint Account : : priority ( ) const
{
return d - > priority ;
}
TQPixmap Account : : accountIcon ( const int size ) const
{
TQString icon = d - > customIcon . isEmpty ( ) ? d - > protocol - > pluginIcon ( ) : d - > customIcon ;
// FIXME: this code is duplicated with OnlineStatus, can we merge it somehow?
TQPixmap base = KGlobal : : instance ( ) - > iconLoader ( ) - > loadIcon (
icon , KIcon : : Small , size ) ;
if ( d - > color . isValid ( ) )
{
KIconEffect effect ;
base = effect . apply ( base , KIconEffect : : Colorize , 1 , d - > color , 0 ) ;
}
if ( size > 0 & & base . width ( ) ! = size )
{
base = TQPixmap ( base . convertToImage ( ) . smoothScale ( size , size ) ) ;
}
return base ;
}
KConfigGroup * Kopete : : Account : : configGroup ( ) const
{
return d - > configGroup ;
}
void Account : : setAccountLabel ( const TQString & label )
{
d - > accountLabel = label ;
}
TQString Account : : accountLabel ( ) const
{
if ( d - > accountLabel . isNull ( ) )
return d - > id ;
return d - > accountLabel ;
}
void Account : : setExcludeConnect ( bool b )
{
d - > excludeconnect = b ;
d - > configGroup - > writeEntry ( " ExcludeConnect " , d - > excludeconnect ) ;
}
bool Account : : excludeConnect ( ) const
{
return d - > excludeconnect ;
}
void Account : : registerContact ( Contact * c )
{
d - > contacts . insert ( c - > contactId ( ) , c ) ;
TQObject : : connect ( c , TQT_SIGNAL ( contactDestroyed ( Kopete : : Contact * ) ) ,
TQT_SLOT ( contactDestroyed ( Kopete : : Contact * ) ) ) ;
}
void Account : : contactDestroyed ( Contact * c )
{
d - > contacts . remove ( c - > contactId ( ) ) ;
}
const TQDict < Contact > & Account : : contacts ( )
{
return d - > contacts ;
}
Kopete : : MetaContact * Account : : addContact ( const TQString & contactId , const TQString & displayName , Group * group , AddMode mode )
{
if ( contactId = = d - > myself - > contactId ( ) )
{
KMessageBox : : queuedMessageBox ( Kopete : : UI : : Global : : mainWidget ( ) , KMessageBox : : Error ,
i18n ( " You are not allowed to add yourself to the contact list. The addition of \" %1 \" to account \" %2 \" will not take place. " ) . arg ( contactId , accountId ( ) ) , i18n ( " Error Creating Contact " )
) ;
return false ;
}
bool isTemporary = mode = = Temporary ;
Contact * c = d - > contacts [ contactId ] ;
if ( ! group )
group = Group : : topLevel ( ) ;
if ( c & & c - > metaContact ( ) )
{
if ( c - > metaContact ( ) - > isTemporary ( ) & & ! isTemporary )
{
kdDebug ( 14010 ) < < k_funcinfo < < " You are trying to add an existing temporary contact. Just add it on the list " < < endl ;
c - > metaContact ( ) - > setTemporary ( false , group ) ;
ContactList : : self ( ) - > addMetaContact ( c - > metaContact ( ) ) ;
}
else
{
// should we here add the contact to the parentContact if any?
kdDebug ( 14010 ) < < k_funcinfo < < " Contact already exists " < < endl ;
}
return c - > metaContact ( ) ;
}
MetaContact * parentContact = new MetaContact ( ) ;
if ( ! displayName . isEmpty ( ) )
parentContact - > setDisplayName ( displayName ) ;
//Set it as a temporary contact if requested
if ( isTemporary )
parentContact - > setTemporary ( true ) ;
else
parentContact - > addToGroup ( group ) ;
if ( c )
{
c - > setMetaContact ( parentContact ) ;
if ( mode = = ChangeKABC )
{
kdDebug ( 14010 ) < < k_funcinfo < < " changing KABC " < < endl ;
KABCPersistence : : self ( ) - > write ( parentContact ) ;
}
}
else
{
if ( ! createContact ( contactId , parentContact ) )
{
delete parentContact ;
return 0L ;
}
}
ContactList : : self ( ) - > addMetaContact ( parentContact ) ;
return parentContact ;
}
bool Account : : addContact ( const TQString & contactId , MetaContact * parent , AddMode mode )
{
if ( contactId = = myself ( ) - > contactId ( ) )
{
KMessageBox : : error ( Kopete : : UI : : Global : : mainWidget ( ) ,
i18n ( " You are not allowed to add yourself to the contact list. The addition of \" %1 \" to account \" %2 \" will not take place. " ) . arg ( contactId , accountId ( ) ) , i18n ( " Error Creating Contact " )
) ;
return 0L ;
}
bool isTemporary = parent - > isTemporary ( ) ;
Contact * c = d - > contacts [ contactId ] ;
if ( c & & c - > metaContact ( ) )
{
if ( c - > metaContact ( ) - > isTemporary ( ) & & ! isTemporary )
{
kdDebug ( 14010 ) < <
" Account::addContact: You are trying to add an existing temporary contact. Just add it on the list " < < endl ;
//setMetaContact ill take care about the deletion of the old contact
c - > setMetaContact ( parent ) ;
return true ;
}
else
{
// should we here add the contact to the parentContact if any?
kdDebug ( 14010 ) < < " Account::addContact: Contact already exists " < < endl ;
}
return false ; //(the contact is not in the correct metacontact, so false)
}
bool success = createContact ( contactId , parent ) ;
if ( success & & mode = = ChangeKABC )
{
kdDebug ( 14010 ) < < k_funcinfo < < " changing KABC " < < endl ;
KABCPersistence : : self ( ) - > write ( parent ) ;
}
return success ;
}
KActionMenu * Account : : actionMenu ( )
{
//default implementation
KActionMenu * menu = new KActionMenu ( accountId ( ) , myself ( ) - > onlineStatus ( ) . iconFor ( this ) , this ) ;
TQString nick = myself ( ) - > property ( Kopete : : Global : : Properties : : self ( ) - > nickName ( ) ) . value ( ) . toString ( ) ;
menu - > popupMenu ( ) - > insertTitle ( myself ( ) - > onlineStatus ( ) . iconFor ( myself ( ) ) ,
nick . isNull ( ) ? accountLabel ( ) : i18n ( " %2 <%1> " ) . arg ( accountLabel ( ) , nick )
) ;
OnlineStatusManager : : self ( ) - > createAccountStatusActions ( this , menu ) ;
menu - > popupMenu ( ) - > insertSeparator ( ) ;
menu - > insert ( new KAction ( i18n ( " Properties " ) , 0 , this , TQT_SLOT ( editAccount ( ) ) , menu , " actionAccountProperties " ) ) ;
return menu ;
}
bool Account : : isConnected ( ) const
{
return myself ( ) & & myself ( ) - > isOnline ( ) ;
}
bool Account : : isAway ( ) const
{
return d - > myself & & ( d - > myself - > onlineStatus ( ) . status ( ) = = Kopete : : OnlineStatus : : Away ) ;
}
Contact * Account : : myself ( ) const
{
return d - > myself ;
}
void Account : : setMyself ( Contact * myself )
{
bool wasConnected = isConnected ( ) ;
if ( d - > myself )
{
TQObject : : disconnect ( d - > myself , TQT_SIGNAL ( onlineStatusChanged ( Kopete : : Contact * , const Kopete : : OnlineStatus & , const Kopete : : OnlineStatus & ) ) ,
this , TQT_SLOT ( slotOnlineStatusChanged ( Kopete : : Contact * , const Kopete : : OnlineStatus & , const Kopete : : OnlineStatus & ) ) ) ;
TQObject : : disconnect ( d - > myself , TQT_SIGNAL ( propertyChanged ( Kopete : : Contact * , const TQString & , const TQVariant & , const TQVariant & ) ) ,
this , TQT_SLOT ( slotContactPropertyChanged ( Kopete : : Contact * , const TQString & , const TQVariant & , const TQVariant & ) ) ) ;
}
d - > myself = myself ;
// d->contacts.remove( myself->contactId() );
TQObject : : connect ( d - > myself , TQT_SIGNAL ( onlineStatusChanged ( Kopete : : Contact * , const Kopete : : OnlineStatus & , const Kopete : : OnlineStatus & ) ) ,
this , TQT_SLOT ( slotOnlineStatusChanged ( Kopete : : Contact * , const Kopete : : OnlineStatus & , const Kopete : : OnlineStatus & ) ) ) ;
TQObject : : connect ( d - > myself , TQT_SIGNAL ( propertyChanged ( Kopete : : Contact * , const TQString & , const TQVariant & , const TQVariant & ) ) ,
this , TQT_SLOT ( slotContactPropertyChanged ( Kopete : : Contact * , const TQString & , const TQVariant & , const TQVariant & ) ) ) ;
if ( isConnected ( ) ! = wasConnected )
emit isConnectedChanged ( ) ;
}
void Account : : slotOnlineStatusChanged ( Contact * /* contact */ ,
const OnlineStatus & newStatus , const OnlineStatus & oldStatus )
{
bool wasOffline = ! oldStatus . isDefinitelyOnline ( ) ;
bool isOffline = ! newStatus . isDefinitelyOnline ( ) ;
if ( wasOffline | | newStatus . status ( ) = = OnlineStatus : : Offline )
{
// Wait for five seconds until we treat status notifications for contacts
// as unrelated to our own status change.
// Five seconds may seem like a long time, but just after your own
// connection it's basically neglectible, and depending on your own
// contact list's size, the protocol you are using, your internet
// connection's speed and your computer's speed you *will* need it.
d - > suppressStatusNotification = true ;
d - > suppressStatusTimer . start ( 5000 , true ) ;
//the timer is also used to reset the d->connectionTry
}
if ( ! isOffline )
{
d - > restoreStatus = newStatus ;
d - > restoreMessage = myself ( ) - > property ( Kopete : : Global : : Properties : : self ( ) - > awayMessage ( ) ) . value ( ) . toString ( ) ;
// kdDebug( 14010 ) << k_funcinfo << "account " << d->id << " restoreStatus " << d->restoreStatus.status() << " restoreMessage " << d->restoreMessage << endl;
}
/* kdDebug(14010) << k_funcinfo << "account " << d->id << " changed status. was "
< < Kopete : : OnlineStatus : : statusTypeToString ( oldStatus . status ( ) ) < < " , is "
< < Kopete : : OnlineStatus : : statusTypeToString ( newStatus . status ( ) ) < < endl ; */
if ( wasOffline ! = isOffline )
emit isConnectedChanged ( ) ;
}
void Account : : setAllContactsStatus ( const Kopete : : OnlineStatus & status )
{
d - > suppressStatusNotification = true ;
d - > suppressStatusTimer . start ( 5000 , true ) ;
for ( TQDictIterator < Contact > it ( d - > contacts ) ; it . current ( ) ; + + it )
if ( it . current ( ) ! = d - > myself )
it . current ( ) - > setOnlineStatus ( status ) ;
}
void Account : : slotContactPropertyChanged ( Contact * /* contact */ ,
const TQString & key , const TQVariant & old , const TQVariant & newVal )
{
if ( key = = TQString : : fromLatin1 ( " awayMessage " ) & & old ! = newVal & & isConnected ( ) )
{
d - > restoreMessage = newVal . toString ( ) ;
// kdDebug( 14010 ) << k_funcinfo << "account " << d->id << " restoreMessage " << d->restoreMessage << endl;
}
}
void Account : : slotStopSuppression ( )
{
d - > suppressStatusNotification = false ;
if ( isConnected ( ) )
d - > connectionTry = 0 ;
}
bool Account : : suppressStatusNotification ( ) const
{
return d - > suppressStatusNotification ;
}
bool Account : : removeAccount ( )
{
//default implementation
return true ;
}
BlackLister * Account : : blackLister ( )
{
return d - > blackList ;
}
void Account : : block ( const TQString & contactId )
{
d - > blackList - > addContact ( contactId ) ;
}
void Account : : unblock ( const TQString & contactId )
{
d - > blackList - > removeContact ( contactId ) ;
}
bool Account : : isBlocked ( const TQString & contactId )
{
return d - > blackList - > isBlocked ( contactId ) ;
}
void Account : : editAccount ( TQWidget * parent )
{
KDialogBase * editDialog = new KDialogBase ( parent , " KopeteAccountConfig::editDialog " , true ,
i18n ( " Edit Account " ) , KDialogBase : : Ok | KDialogBase : : Cancel ,
KDialogBase : : Ok , true ) ;
KopeteEditAccountWidget * m_accountWidget = protocol ( ) - > createEditAccountWidget ( this , editDialog ) ;
if ( ! m_accountWidget )
return ;
// FIXME: Why the #### is EditAccountWidget not a TQWidget?!? This sideways casting
// is braindead and error-prone. Looking at MSN the only reason I can see is
// because it allows direct subclassing of designer widgets. But what is
// wrong with embedding the designer widget in an empty TQWidget instead?
// Also, if this REALLY has to be a pure class and not a widget, then the
// class should at least be renamed to EditAccountIface instead - Martijn
TQWidget * w = dynamic_cast < TQWidget * > ( m_accountWidget ) ;
if ( ! w )
return ;
editDialog - > setMainWidget ( w ) ;
if ( editDialog - > exec ( ) = = TQDialog : : Accepted )
{
if ( m_accountWidget - > validateData ( ) )
m_accountWidget - > apply ( ) ;
}
editDialog - > deleteLater ( ) ;
}
void Account : : setPluginData ( Plugin * /*plugin*/ , const TQString & key , const TQString & value )
{
configGroup ( ) - > writeEntry ( key , value ) ;
}
TQString Account : : pluginData ( Plugin * /*plugin*/ , const TQString & key ) const
{
return configGroup ( ) - > readEntry ( key ) ;
}
void Account : : setAway ( bool away , const TQString & reason )
{
setOnlineStatus ( OnlineStatusManager : : self ( ) - > onlineStatus ( protocol ( ) , away ? OnlineStatusManager : : Away : OnlineStatusManager : : Online ) , reason ) ;
}
void Account : : setCustomIcon ( const TQString & i )
{
d - > customIcon = i ;
if ( ! i . isEmpty ( ) )
d - > configGroup - > writeEntry ( " Icon " , i ) ;
else
d - > configGroup - > deleteEntry ( " Icon " ) ;
emit colorChanged ( color ( ) ) ;
}
TQString Account : : customIcon ( ) const
{
return d - > customIcon ;
}
void Account : : virtual_hook ( uint /*id*/ , void * /*data*/ )
{
}
}
//END namespace Kopete
# include "kopeteaccount.moc"