Show invite menu in wlm chat window immediately
[kdenetwork.git] / kopete / libkopete / kopeteaccount.cpp
blobf1266b0634e66f9b5a06b0eb42e7381fabbcbc7c
1 /*
2 kopeteaccount.cpp - Kopete Account
4 Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@kde.org>
5 Copyright (c) 2003-2004 by Martijn Klingens <klingens@kde.org>
6 Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
7 Copyright (c) 2007 Will Stephenson <wstephenson@kde.org>
9 Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
11 *************************************************************************
12 * *
13 * This library is free software; you can redistribute it and/or *
14 * modify it under the terms of the GNU Lesser General Public *
15 * License as published by the Free Software Foundation; either *
16 * version 2 of the License, or (at your option) any later version. *
17 * *
18 *************************************************************************
21 #include <qapplication.h>
22 #include <QTimer>
23 #include <QPixmap>
24 #include <QIcon>
26 #include <kconfig.h>
27 #include <kdebug.h>
28 #include <kdialog.h>
29 #include <kdeversion.h>
30 #include <klocale.h>
31 #include <kiconloader.h>
32 #include <kiconeffect.h>
33 #include <kicon.h>
34 #include <kaction.h>
35 #include <kmenu.h>
36 #include <kmessagebox.h>
37 #include <kcomponentdata.h>
38 #include <kactionmenu.h>
39 #include <kconfiggroup.h>
41 #include "kopeteaccount.h"
42 #include "kopeteidentity.h"
43 #include "kopeteidentitymanager.h"
44 #include "kabcpersistence.h"
45 #include "kopetecontactlist.h"
46 #include "kopeteaccountmanager.h"
47 #include "kopetecontact.h"
48 #include "kopetemetacontact.h"
49 #include "kopeteprotocol.h"
50 #include "kopetepluginmanager.h"
51 #include "kopetegroup.h"
52 #include "kopetebehaviorsettings.h"
53 #include "kopeteutils.h"
54 #include "kopeteuiglobal.h"
55 #include "kopeteblacklister.h"
56 #include "kopeteonlinestatusmanager.h"
57 #include "editaccountwidget.h"
59 namespace Kopete
63 class Account::Private
65 public:
66 Private( Protocol *protocol, const QString &accountId )
67 : protocol( protocol ), id( accountId )
68 , excludeconnect( true ), priority( 0 )
69 , connectionTry(0), identity( 0 ), myself( 0 )
70 , suppressStatusTimer( 0 ), suppressStatusNotification( false )
71 , blackList( new Kopete::BlackLister( protocol->pluginId(), accountId ) )
72 { }
75 ~Private() { delete blackList; }
77 Protocol *protocol;
78 QString id;
79 QString accountLabel;
80 bool excludeconnect;
81 uint priority;
82 QHash<QString, Contact*> contacts;
83 QColor color;
84 uint connectionTry;
85 Identity *identity;
86 Contact *myself;
87 QTimer suppressStatusTimer;
88 bool suppressStatusNotification;
89 Kopete::BlackLister *blackList;
90 KConfigGroup *configGroup;
91 QString customIcon;
92 Kopete::OnlineStatus restoreStatus;
93 Kopete::StatusMessage restoreMessage;
96 Account::Account( Protocol *parent, const QString &accountId )
97 : QObject( parent ), d( new Private( parent, accountId ) )
99 d->configGroup=new KConfigGroup(KGlobal::config(), QString::fromLatin1( "Account_%1_%2" ).arg( d->protocol->pluginId(), d->id ));
101 d->excludeconnect = d->configGroup->readEntry( "ExcludeConnect", false );
102 d->color = d->configGroup->readEntry( "Color" , QColor() );
103 d->customIcon = d->configGroup->readEntry( "Icon", QString() );
104 d->priority = d->configGroup->readEntry( "Priority", 0 );
106 d->restoreStatus = Kopete::OnlineStatus::Online;
107 d->restoreMessage = Kopete::StatusMessage();
109 QObject::connect( &d->suppressStatusTimer, SIGNAL( timeout() ),
110 this, SLOT( slotStopSuppression() ) );
113 Account::~Account()
115 d->contacts.remove( d->myself->contactId() );
116 // Delete all registered child contacts first
117 foreach (Contact* c, d->contacts) QObject::disconnect(c, SIGNAL( contactDestroyed( Kopete::Contact * ) ), this, 0);
118 qDeleteAll(d->contacts);
119 d->contacts.clear();
121 kDebug( 14010 ) << " account '" << d->id << "' about to emit accountDestroyed ";
122 emit accountDestroyed(this);
124 delete d->myself;
125 delete d->configGroup;
126 delete d;
129 void Account::reconnect()
131 kDebug( 14010 ) << "account " << d->id << " restoreStatus " << d->restoreStatus.status()
132 << " restoreTitle " << d->restoreMessage.title()
133 << " restoreMessage " << d->restoreMessage.message();
134 setOnlineStatus( d->restoreStatus, d->restoreMessage );
137 void Account::disconnected( DisconnectReason reason )
139 kDebug( 14010 ) << reason;
140 //reconnect if needed
141 if(reason == BadPassword )
143 QTimer::singleShot(0, this, SLOT(reconnect()));
145 else if ( Kopete::BehaviorSettings::self()->reconnectOnDisconnect() == true && reason > Manual )
147 d->connectionTry++;
148 //use a timer to allow the plugins to clean up after return
149 if(d->connectionTry < 3)
150 QTimer::singleShot(10000, this, SLOT(reconnect())); // wait 10 seconds before reconnect
152 if(reason== OtherClient)
154 Kopete::Utils::notifyConnectionLost(this, i18n("You have been disconnected"), i18n( "You have connected from another client or computer to the account '%1'" , 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."));
158 Protocol *Account::protocol() const
160 return d->protocol;
163 QString Account::accountId() const
165 return d->id;
168 const QColor Account::color() const
170 return d->color;
173 void Account::setColor( const QColor &color )
175 d->color = color;
176 if ( d->color.isValid() )
177 d->configGroup->writeEntry( "Color", d->color );
178 else
179 d->configGroup->deleteEntry( "Color" );
180 emit colorChanged( color );
183 void Account::setPriority( uint priority )
185 d->priority = priority;
186 d->configGroup->writeEntry( "Priority", d->priority );
189 uint Account::priority() const
191 return d->priority;
195 QPixmap Account::accountIcon(const int size) const
197 QString icon= d->customIcon.isEmpty() ? d->protocol->pluginIcon() : d->customIcon;
199 // FIXME: this code is duplicated with OnlineStatus, can we merge it somehow?
200 QPixmap base = KIconLoader::global()->loadIcon(
201 icon, KIconLoader::Small, size );
203 if ( d->color.isValid() )
205 KIconEffect effect;
206 base = effect.apply( base, KIconEffect::Colorize, 1, d->color, 0);
209 if ( size > 0 && base.width() != size )
211 base.scaled( size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
214 return base;
217 KConfigGroup* Kopete::Account::configGroup() const
219 return d->configGroup;
222 void Account::setAccountLabel( const QString &label )
224 d->accountLabel = label;
227 QString Account::accountLabel() const
229 if( d->accountLabel.isNull() )
230 return d->id;
231 return d->accountLabel;
234 void Account::setExcludeConnect( bool b )
236 d->excludeconnect = b;
237 d->configGroup->writeEntry( "ExcludeConnect", d->excludeconnect );
240 bool Account::excludeConnect() const
242 return d->excludeconnect;
245 void Account::registerContact( Contact *c )
247 if ( d->contacts.value( c->contactId(), 0 ) )
249 kWarning(14010) << "Contact already exists!!! accountId: " << c->account() << " contactId: " << c->contactId();
250 return;
253 d->contacts.insert( c->contactId(), c );
254 QObject::connect( c, SIGNAL( contactDestroyed( Kopete::Contact * ) ),
255 SLOT( contactDestroyed( Kopete::Contact * ) ) );
258 void Account::contactDestroyed( Contact *c )
260 d->contacts.remove( c->contactId() );
264 const QHash<QString, Contact*>& Account::contacts()
266 return d->contacts;
270 Kopete::MetaContact* Account::addContact( const QString &contactId, const QString &displayName , Group *group, AddMode mode )
273 if ( contactId == d->myself->contactId() )
275 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error,
276 i18n("You are not allowed to add yourself to the contact list. The addition of \"%1\" to account \"%2\" will not take place.", contactId, accountId()), i18n("Error Creating Contact")
278 return false;
281 bool isTemporary = mode == Temporary;
283 Contact *c = d->contacts[ contactId ];
285 if(!group)
286 group=Group::topLevel();
288 if ( c && c->metaContact() )
290 if ( c->metaContact()->isTemporary() && !isTemporary )
292 kDebug( 14010 ) << " You are trying to add an existing temporary contact. Just add it on the list";
294 c->metaContact()->setTemporary(false, group );
295 ContactList::self()->addMetaContact(c->metaContact());
297 else
299 // should we here add the contact to the parentContact if any?
300 kDebug( 14010 ) << "Contact already exists";
302 return c->metaContact();
305 MetaContact *parentContact = new MetaContact();
306 if(!displayName.isEmpty())
307 parentContact->setDisplayName( displayName );
309 //Set it as a temporary contact if requested
310 if ( isTemporary )
311 parentContact->setTemporary( true );
312 else
313 parentContact->addToGroup( group );
315 if ( c )
317 c->setMetaContact( parentContact );
318 if ( mode == ChangeKABC )
320 kDebug( 14010 ) << " changing KABC";
321 KABCPersistence::self()->write( parentContact );
324 else
326 if ( !createContact( contactId, parentContact ) )
328 delete parentContact;
329 return 0L;
333 ContactList::self()->addMetaContact( parentContact );
334 return parentContact;
337 bool Account::addContact(const QString &contactId , MetaContact *parent, AddMode mode )
339 if ( contactId == myself()->contactId() )
341 KMessageBox::error( Kopete::UI::Global::mainWidget(),
342 i18n("You are not allowed to add yourself to the contact list. The addition of \"%1\" to account \"%2\" will not take place.", contactId, accountId()), i18n("Error Creating Contact")
344 return 0L;
347 bool isTemporary= parent->isTemporary();
348 Contact *c = d->contacts[ contactId ];
349 if ( c && c->metaContact() )
351 if ( c->metaContact()->isTemporary() && !isTemporary )
353 kDebug( 14010 ) <<
354 "Account::addContact: You are trying to add an existing temporary contact. Just add it on the list" << endl;
356 //setMetaContact ill take care about the deletion of the old contact
357 c->setMetaContact(parent);
358 return true;
360 else
362 // should we here add the contact to the parentContact if any?
363 kDebug( 14010 ) << "Account::addContact: Contact already exists";
365 return false; //(the contact is not in the correct metacontact, so false)
368 bool success = createContact(contactId, parent);
370 if ( success && mode == ChangeKABC )
372 kDebug( 14010 ) << " changing KABC";
373 KABCPersistence::self()->write( parent );
376 return success;
379 void Account::fillActionMenu( KActionMenu *actionMenu )
381 //default implementation
382 // KActionMenu *menu = new KActionMenu( QIcon(myself()->onlineStatus().iconFor( this )), accountId(), 0, 0);
383 #ifdef __GNUC__
384 #warning No icon shown, we should go away from QPixmap genered icons with overlays.
385 #endif
386 QString nick;
387 if (identity()->hasProperty( Kopete::Global::Properties::self()->nickName().key() ))
388 nick = identity()->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
389 else
390 nick = myself()->nickName();
392 // Always add title at the beginning of actionMenu
393 QAction *before = actionMenu->menu()->actions().value( 0, 0 );
394 actionMenu->menu()->addTitle( myself()->onlineStatus().iconFor( myself() ),
395 nick.isNull() ? accountLabel() : i18n( "%2 <%1>", accountLabel(), nick ),
396 before
399 actionMenu->menu()->addSeparator();
401 KAction *propertiesAction = new KAction( i18n("Properties"), actionMenu );
402 QObject::connect( propertiesAction, SIGNAL(triggered(bool)), this, SLOT( editAccount() ) );
403 actionMenu->addAction( propertiesAction );
406 bool Account::hasCustomStatusMenu() const
408 return false;
411 bool Account::isConnected() const
413 return myself() && myself()->isOnline();
416 bool Account::isAway() const
418 return d->myself && ( d->myself->onlineStatus().status() == Kopete::OnlineStatus::Away );
420 Identity * Account::identity() const
422 return d->identity;
425 bool Account::setIdentity( Identity *ident )
427 if ( d->identity == ident )
429 return false;
432 if (d->identity)
434 d->identity->removeAccount( this );
437 ident->addAccount( this );
438 d->identity = ident;
439 d->configGroup->writeEntry("Identity", ident->id());
440 return true;
443 Contact * Account::myself() const
445 return d->myself;
448 void Account::setMyself( Contact *myself )
450 //FIXME does it make sens to change the myself contact to another ? - Olivier 2005-11-21
452 bool wasConnected = isConnected();
454 if ( d->myself )
456 QObject::disconnect( d->myself, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
457 this, SLOT( slotOnlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
458 QObject::disconnect( d->myself, SIGNAL( propertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ),
459 this, SLOT( slotContactPropertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ) );
462 d->myself = myself;
464 // d->contacts.remove( myself->contactId() );
466 QObject::connect( d->myself, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
467 this, SLOT( slotOnlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
468 QObject::connect( d->myself, SIGNAL( propertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ),
469 this, SLOT( slotContactPropertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ) );
471 if ( isConnected() != wasConnected )
472 emit isConnectedChanged();
475 void Account::slotOnlineStatusChanged( Contact * /* contact */,
476 const OnlineStatus &newStatus, const OnlineStatus &oldStatus )
478 bool wasOffline = !oldStatus.isDefinitelyOnline();
479 bool isOffline = !newStatus.isDefinitelyOnline();
481 if ( wasOffline || newStatus.status() == OnlineStatus::Offline )
483 // Wait for five seconds until we treat status notifications for contacts
484 // as unrelated to our own status change.
485 // Five seconds may seem like a long time, but just after your own
486 // connection it's basically neglectible, and depending on your own
487 // contact list's size, the protocol you are using, your internet
488 // connection's speed and your computer's speed you *will* need it.
489 d->suppressStatusNotification = true;
490 d->suppressStatusTimer.setSingleShot( true );
491 d->suppressStatusTimer.start( 5000 );
492 //the timer is also used to reset the d->connectionTry
495 if ( !isOffline )
497 d->restoreStatus = newStatus;
498 d->restoreMessage.setTitle( myself()->property( Kopete::Global::Properties::self()->statusTitle() ).value().toString() );
499 d->restoreMessage.setMessage( myself()->property( Kopete::Global::Properties::self()->statusMessage() ).value().toString() );
502 /* kDebug(14010) << "account " << d->id << " changed status. was "
503 << Kopete::OnlineStatus::statusTypeToString(oldStatus.status()) << ", is "
504 << Kopete::OnlineStatus::statusTypeToString(newStatus.status()) << endl;*/
505 if ( wasOffline != isOffline )
506 emit isConnectedChanged();
509 void Account::setAllContactsStatus( const Kopete::OnlineStatus &status )
511 d->suppressStatusNotification = true;
512 d->suppressStatusTimer.setSingleShot( true );
513 d->suppressStatusTimer.start( 5000 );
515 QHashIterator<QString, Contact*> it(d->contacts);
516 for ( ; it.hasNext(); ) {
517 it.next();
519 Contact *c = it.value();
520 if ( c && c != d->myself )
521 c->setOnlineStatus( status );
525 void Account::slotContactPropertyChanged( PropertyContainer * /* contact */,
526 const QString &key, const QVariant &old, const QVariant &newVal )
528 if ( key == Kopete::Global::Properties::self()->statusTitle().key() && old != newVal && isConnected() )
529 d->restoreMessage.setTitle( newVal.toString() );
530 else if ( key == Kopete::Global::Properties::self()->statusMessage().key() && old != newVal && isConnected() )
531 d->restoreMessage.setMessage( newVal.toString() );
534 void Account::slotStopSuppression()
536 d->suppressStatusNotification = false;
537 if(isConnected())
538 d->connectionTry=0;
541 bool Account::suppressStatusNotification() const
543 return d->suppressStatusNotification;
546 bool Account::removeAccount()
548 //default implementation
549 return true;
553 BlackLister* Account::blackLister()
555 return d->blackList;
558 void Account::block( const QString &contactId )
560 d->blackList->addContact( contactId );
563 void Account::unblock( const QString &contactId )
565 d->blackList->removeContact( contactId );
568 bool Account::isBlocked( const QString &contactId )
570 return d->blackList->isBlocked( contactId );
573 void Account::editAccount(QWidget *parent)
575 KDialog *editDialog = new KDialog( parent );
576 editDialog->setCaption( i18n( "Edit Account" ) );
577 editDialog->setButtons( KDialog::Ok | KDialog::Apply | KDialog::Cancel );
579 KopeteEditAccountWidget *m_accountWidget = protocol()->createEditAccountWidget( this, editDialog );
580 if ( !m_accountWidget )
581 return;
583 // FIXME: Why the #### is EditAccountWidget not a QWidget?!? This sideways casting
584 // is braindead and error-prone. Looking at MSN the only reason I can see is
585 // because it allows direct subclassing of designer widgets. But what is
586 // wrong with embedding the designer widget in an empty QWidget instead?
587 // Also, if this REALLY has to be a pure class and not a widget, then the
588 // class should at least be renamed to EditAccountIface instead - Martijn
589 QWidget *w = dynamic_cast<QWidget *>( m_accountWidget );
590 if ( !w )
591 return;
593 editDialog->setMainWidget( w );
594 if ( editDialog->exec() == QDialog::Accepted )
596 if( m_accountWidget->validateData() )
597 m_accountWidget->apply();
600 editDialog->deleteLater();
603 void Account::setCustomIcon( const QString & i)
605 d->customIcon = i;
606 if(!i.isEmpty())
607 d->configGroup->writeEntry( "Icon", i );
608 else
609 d->configGroup->deleteEntry( "Icon" );
610 emit colorChanged( color() );
613 QString Account::customIcon() const
615 return d->customIcon;
618 } // END namespace Kopete
620 #include "kopeteaccount.moc"