/* kopeteaccount.cpp - Kopete Account Copyright (c) 2003-2005 by Olivier Goffart Copyright (c) 2003-2004 by Martijn Klingens Copyright (c) 2004 by Richard Smith 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 #include #include #include #include #include #include #include #include #include #include "tdeabcpersistence.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 contacts; TQColor color; Contact *myself; TQTimer suppressStatusTimer; bool suppressStatusNotification; Kopete::BlackLister *blackList; TDEConfigGroup *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 TDEConfigGroup(TDEGlobal::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( 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 = TDEGlobal::instance()->iconLoader()->loadIcon( icon, TDEIcon::Small, size ); if ( d->color.isValid() ) { TDEIconEffect effect; base = effect.apply( base, TDEIconEffect::Colorize, 1, d->color, 0); } if ( size > 0 && base.width() != size ) { base = TQPixmap( base.convertToImage().smoothScale( size, size ) ); } return base; } TDEConfigGroup* 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& 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; } TDEActionMenu * Account::actionMenu() { //default implementation TDEActionMenu *menu = new TDEActionMenu( 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 TDEAction ( 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 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( 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"