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 *************************************************************************
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. *
18 *************************************************************************
21 #include <qapplication.h>
29 #include <kdeversion.h>
31 #include <kiconloader.h>
32 #include <kiconeffect.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"
63 class Account::Private
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
) )
75 ~Private() { delete blackList
; }
82 QHash
<QString
, Contact
*> contacts
;
87 QTimer suppressStatusTimer
;
88 bool suppressStatusNotification
;
89 Kopete::BlackLister
*blackList
;
90 KConfigGroup
*configGroup
;
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() ) );
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
);
121 kDebug( 14010 ) << " account '" << d
->id
<< "' about to emit accountDestroyed ";
122 emit
accountDestroyed(this);
125 delete d
->configGroup
;
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
)
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
163 QString
Account::accountId() const
168 const QColor
Account::color() const
173 void Account::setColor( const QColor
&color
)
176 if ( d
->color
.isValid() )
177 d
->configGroup
->writeEntry( "Color", d
->color
);
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
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() )
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
);
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() )
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();
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()
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")
281 bool isTemporary
= mode
== Temporary
;
283 Contact
*c
= d
->contacts
[ contactId
];
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());
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
311 parentContact
->setTemporary( true );
313 parentContact
->addToGroup( group
);
317 c
->setMetaContact( parentContact
);
318 if ( mode
== ChangeKABC
)
320 kDebug( 14010 ) << " changing KABC";
321 KABCPersistence::self()->write( parentContact
);
326 if ( !createContact( contactId
, parentContact
) )
328 delete parentContact
;
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")
347 bool isTemporary
= parent
->isTemporary();
348 Contact
*c
= d
->contacts
[ contactId
];
349 if ( c
&& c
->metaContact() )
351 if ( c
->metaContact()->isTemporary() && !isTemporary
)
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
);
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
);
379 void Account::fillActionMenu( KActionMenu
*actionMenu
)
381 //default implementation
382 // KActionMenu *menu = new KActionMenu( QIcon(myself()->onlineStatus().iconFor( this )), accountId(), 0, 0);
384 #warning No icon shown, we should go away from QPixmap genered icons with overlays.
387 if (identity()->hasProperty( Kopete::Global::Properties::self()->nickName().key() ))
388 nick
= identity()->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
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
),
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
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
425 bool Account::setIdentity( Identity
*ident
)
427 if ( d
->identity
== ident
)
434 d
->identity
->removeAccount( this );
437 ident
->addAccount( this );
439 d
->configGroup
->writeEntry("Identity", ident
->id());
443 Contact
* Account::myself() const
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();
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
& ) ) );
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
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(); ) {
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;
541 bool Account::suppressStatusNotification() const
543 return d
->suppressStatusNotification
;
546 bool Account::removeAccount()
548 //default implementation
553 BlackLister
* Account::blackLister()
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
)
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
);
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
)
607 d
->configGroup
->writeEntry( "Icon", i
);
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"