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.
tdenetwork/kopete/libkopete/kopeteaccount.cpp

582 lines
17 KiB

/*
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 <kdeversion.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"