diff options
Diffstat (limited to 'kopete/libkopete/kopeteaccount.cpp')
-rw-r--r-- | kopete/libkopete/kopeteaccount.cpp | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/kopete/libkopete/kopeteaccount.cpp b/kopete/libkopete/kopeteaccount.cpp new file mode 100644 index 00000000..52bb26bc --- /dev/null +++ b/kopete/libkopete/kopeteaccount.cpp @@ -0,0 +1,581 @@ +/* + 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 <qapplication.h> +#include <qtimer.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 QString &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; + QString id; + QString accountLabel; + bool excludeconnect; + uint priority; + QDict<Contact> contacts; + QColor color; + Contact *myself; + QTimer suppressStatusTimer; + bool suppressStatusNotification; + Kopete::BlackLister *blackList; + KConfigGroup *configGroup; + uint connectionTry; + QString customIcon; + Kopete::OnlineStatus restoreStatus; + QString restoreMessage; +}; + +Account::Account( Protocol *parent, const QString &accountId, const char *name ) + : QObject( parent, name ), d( new Private( parent, accountId ) ) +{ + d->configGroup=new KConfigGroup(KGlobal::config(), QString::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", QString() ); + d->priority = d->configGroup->readNumEntry( "Priority", 0 ); + + d->restoreStatus = Kopete::OnlineStatus::Online; + d->restoreMessage = ""; + + QObject::connect( &d->suppressStatusTimer, SIGNAL( timeout() ), + this, SLOT( slotStopSuppression() ) ); +} + +Account::~Account() +{ + d->contacts.remove( d->myself->contactId() ); + + // Delete all registered child contacts first + while ( !d->contacts.isEmpty() ) + delete *QDictIterator<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 ) + { + QTimer::singleShot(0, this, 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) + QTimer::singleShot(10000, this, 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; +} + +QString Account::accountId() const +{ + return d->id; +} + +const QColor Account::color() const +{ + return d->color; +} + +void Account::setColor( const QColor &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; +} + + +QPixmap Account::accountIcon(const int size) const +{ + QString icon= d->customIcon.isEmpty() ? d->protocol->pluginIcon() : d->customIcon; + + // FIXME: this code is duplicated with OnlineStatus, can we merge it somehow? + QPixmap 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 = QPixmap( base.convertToImage().smoothScale( size, size ) ); + } + + return base; +} + +KConfigGroup* Kopete::Account::configGroup() const +{ + return d->configGroup; +} + +void Account::setAccountLabel( const QString &label ) +{ + d->accountLabel = label; +} + +QString 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 ); + QObject::connect( c, SIGNAL( contactDestroyed( Kopete::Contact * ) ), + SLOT( contactDestroyed( Kopete::Contact * ) ) ); +} + +void Account::contactDestroyed( Contact *c ) +{ + d->contacts.remove( c->contactId() ); +} + + +const QDict<Contact>& Account::contacts() +{ + return d->contacts; +} + + +Kopete::MetaContact* Account::addContact( const QString &contactId, const QString &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 QString &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 ); + QString 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, 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 ) + { + QObject::disconnect( d->myself, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ), + this, SLOT( slotOnlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) ); + QObject::disconnect( d->myself, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ), + this, SLOT( slotContactPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) ); + } + + d->myself = myself; + +// d->contacts.remove( myself->contactId() ); + + QObject::connect( d->myself, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ), + this, SLOT( slotOnlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) ); + QObject::connect( d->myself, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ), + this, SLOT( slotContactPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) ); + + 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 ( QDictIterator<Contact> it( d->contacts ); it.current(); ++it ) + if ( it.current() != d->myself ) + it.current()->setOnlineStatus( status ); +} + +void Account::slotContactPropertyChanged( Contact * /* contact */, + const QString &key, const QVariant &old, const QVariant &newVal ) +{ + if ( key == QString::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 QString &contactId ) +{ + d->blackList->addContact( contactId ); +} + +void Account::unblock( const QString &contactId ) +{ + d->blackList->removeContact( contactId ); +} + +bool Account::isBlocked( const QString &contactId ) +{ + return d->blackList->isBlocked( contactId ); +} + +void Account::editAccount(QWidget *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 QWidget?!? 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 QWidget 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 + QWidget *w = dynamic_cast<QWidget *>( m_accountWidget ); + if ( !w ) + return; + + editDialog->setMainWidget( w ); + if ( editDialog->exec() == QDialog::Accepted ) + { + if( m_accountWidget->validateData() ) + m_accountWidget->apply(); + } + + editDialog->deleteLater(); +} + +void Account::setPluginData( Plugin* /*plugin*/, const QString &key, const QString &value ) +{ + configGroup()->writeEntry(key,value); +} + +QString Account::pluginData( Plugin* /*plugin*/, const QString &key ) const +{ + return configGroup()->readEntry(key); +} + +void Account::setAway(bool away, const QString& reason) +{ + setOnlineStatus( OnlineStatusManager::self()->onlineStatus(protocol() , away ? OnlineStatusManager::Away : OnlineStatusManager::Online) , reason ); +} + +void Account::setCustomIcon( const QString & i) +{ + d->customIcon = i; + if(!i.isEmpty()) + d->configGroup->writeEntry( "Icon", i ); + else + d->configGroup->deleteEntry( "Icon" ); + emit colorChanged( color() ); +} + +QString Account::customIcon() const +{ + return d->customIcon; +} + +void Account::virtual_hook( uint /*id*/, void* /*data*/) +{ +} + + + +} + + //END namespace Kopete + +#include "kopeteaccount.moc" |