make webinterface translatable. there are around 20 short strings, all with context...
[kdenetwork.git] / kopete / libkopete / kopetemetacontact.cpp
bloba0d20d9cd9428c716b03b1390be341ca10bf8917
1 /*
2 kopetemetacontact.cpp - Kopete Meta Contact
4 Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
5 Copyright (c) 2002-2005 by Olivier Goffart <ogoffart@kde.org>
6 Copyright (c) 2002-2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
7 Copyright (c) 2005 by Michaƫl Larouche <larouche@kde.org>
9 Kopete (c) 2002-2006 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 "kopetemetacontact.h"
22 #include "kopetemetacontact_p.h"
25 #include <kabc/addressbook.h>
26 #include <kabc/addressee.h>
28 #include <kdebug.h>
29 #include <klocale.h>
30 #include <kmessagebox.h>
31 #include <kdeversion.h>
33 #include "kabcpersistence.h"
34 #include "kopetecontactlist.h"
35 #include "kopetecontact.h"
36 #include "kopeteaccountmanager.h"
37 #include "kopeteprotocol.h"
38 #include "kopeteaccount.h"
39 #include "kopetepluginmanager.h"
40 #include "kopetegroup.h"
41 #include "kopeteglobal.h"
42 #include "kopeteuiglobal.h"
44 namespace Kopete {
46 MetaContact::MetaContact()
47 : ContactListElement( ContactList::self() )
49 d = new Private;
51 connect( this, SIGNAL( pluginDataChanged() ), SIGNAL( persistentDataChanged() ) );
52 connect( this, SIGNAL( iconChanged( Kopete::ContactListElement::IconState, const QString & ) ), SIGNAL( persistentDataChanged() ) );
53 connect( this, SIGNAL( useCustomIconChanged( bool ) ), SIGNAL( persistentDataChanged() ) );
54 connect( this, SIGNAL( displayNameChanged( const QString &, const QString & ) ), SIGNAL( persistentDataChanged() ) );
55 connect( this, SIGNAL( movedToGroup( Kopete::MetaContact *, Kopete::Group *, Kopete::Group * ) ), SIGNAL( persistentDataChanged() ) );
56 connect( this, SIGNAL( removedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ), SIGNAL( persistentDataChanged() ) );
57 connect( this, SIGNAL( addedToGroup( Kopete::MetaContact *, Kopete::Group * ) ), SIGNAL( persistentDataChanged() ) );
58 connect( this, SIGNAL( contactAdded( Kopete::Contact * ) ), SIGNAL( persistentDataChanged() ) );
59 connect( this, SIGNAL( contactRemoved( Kopete::Contact * ) ), SIGNAL( persistentDataChanged() ) );
61 // Update the KABC picture when the KDE Address book change.
62 connect(KABCPersistence::self()->addressBook(), SIGNAL(addressBookChanged(AddressBook *)), this, SLOT(slotUpdateAddressBookPicture()));
64 // make sure MetaContact is at least in one group
65 addToGroup( Group::topLevel() );
66 // I'm not sure this is correct -Olivier
67 // we probably should do the check in groups() instead
70 MetaContact::~MetaContact()
72 delete d;
75 void MetaContact::addContact( Contact *c )
77 if( d->contacts.contains( c ) )
79 kWarning(14010) << "Ignoring attempt to add duplicate contact " << c->contactId() << "!";
81 else
83 const QString oldDisplayName = displayName();
85 d->contacts.append( c );
87 connect( c, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
88 SLOT( slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
90 connect( c, SIGNAL( propertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ),
91 this, SLOT( slotPropertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ) ) ;
93 connect( c, SIGNAL( contactDestroyed( Kopete::Contact * ) ),
94 this, SLOT( slotContactDestroyed( Kopete::Contact * ) ) );
96 connect( c, SIGNAL( idleStateChanged( Kopete::Contact * ) ),
97 this, SIGNAL( contactIdleStateChanged( Kopete::Contact * ) ) );
99 emit contactAdded(c);
101 updateOnlineStatus();
103 // if this is the first contact, probbaly was created by a protocol
104 // so it has empty custom properties, then set sources to the contact
105 if ( d->contacts.count() == 1 )
107 const QString newDisplayName = displayName();
108 if ( oldDisplayName != newDisplayName )
110 emit displayNameChanged( oldDisplayName , displayName() );
112 if ( picture().isNull() )
114 setPhotoSourceContact(c);
115 setPhotoSource(SourceContact);
121 void MetaContact::updateOnlineStatus()
123 Kopete::OnlineStatus::StatusType newStatus = Kopete::OnlineStatus::Unknown;
124 Kopete::OnlineStatus mostSignificantStatus;
126 QListIterator<Contact *> it(d->contacts);
127 while ( it.hasNext() )
129 Contact *c = it.next();
130 // find most significant status
131 if ( c->onlineStatus() > mostSignificantStatus )
132 mostSignificantStatus = c->onlineStatus();
135 newStatus = mostSignificantStatus.status();
137 if( newStatus != d->onlineStatus )
139 d->onlineStatus = newStatus;
140 emit onlineStatusChanged( this, d->onlineStatus );
145 void MetaContact::removeContact(Contact *c, bool deleted)
147 if( !d->contacts.contains( c ) )
149 kDebug(14010) << " Contact is not in this metaContact ";
151 else
153 // must check before removing, or will always be false
154 bool wasTrackingName = ( !displayNameSourceContact() && (displayNameSource() == SourceContact) );
155 bool wasTrackingPhoto = ( !photoSourceContact() && (photoSource() == SourceContact) );
156 // save for later use
157 QString currDisplayName = displayName();
159 d->contacts.removeAll( c );
161 // if the contact was a source of property data, clean
162 if (displayNameSourceContact() == c)
163 setDisplayNameSourceContact(0L);
164 if (photoSourceContact() == c)
165 setPhotoSourceContact(0L);
168 if ( wasTrackingName )
170 // Oh! this contact was the source for the metacontact's name
171 // lets do something
172 // is this the only contact?
173 if ( d->contacts.isEmpty() )
175 // fallback to a custom name as we don't have
176 // more contacts to chose as source.
177 setDisplayNameSource(SourceCustom);
178 // perhaps the custom display name was empty
179 // no problems baby, I saved the old one.
180 setDisplayName(currDisplayName);
182 else
184 // we didn't fallback to SourceCustom above so lets use the next
185 // contact as source
186 setDisplayNameSourceContact( d->contacts.first() );
190 if ( wasTrackingPhoto )
192 // Oh! this contact was the source for the metacontact's photo
193 // lets do something
194 // is this the only contact?
195 if ( d->contacts.isEmpty() )
197 // fallback to a custom photo as we don't have
198 // more contacts to chose as source.
199 setPhotoSource(SourceCustom);
200 // FIXME set the custom photo
202 else
204 // we didn't fallback to SourceCustom above so lets use the next
205 // contact as source
206 setPhotoSourceContact( d->contacts.first() );
210 if(!deleted)
211 { //If this function is tell by slotContactRemoved, c is maybe just a QObject
212 disconnect( c, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
213 this, SLOT( slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
214 disconnect( c, SIGNAL( propertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ),
215 this, SLOT( slotPropertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ) ) ;
216 disconnect( c, SIGNAL( contactDestroyed( Kopete::Contact * ) ),
217 this, SLOT( slotContactDestroyed( Kopete::Contact * ) ) );
218 disconnect( c, SIGNAL( idleStateChanged( Kopete::Contact * ) ),
219 this, SIGNAL( contactIdleStateChanged( Kopete::Contact *) ) );
221 kDebug( 14010 ) << "Contact disconnected";
223 KABCPersistence::self()->write( this );
226 // Reparent the contact
227 c->setParent( 0 );
229 emit contactRemoved( c );
231 updateOnlineStatus();
234 Contact *MetaContact::findContact( const QString &protocolId, const QString &accountId, const QString &contactId )
236 //kDebug( 14010 ) << "Num contacts: " << d->contacts.count();
237 QListIterator<Contact *> it( d->contacts );
238 while ( it.hasNext() )
240 Contact *c = it.next();
241 //kDebug( 14010 ) << "Trying " << it.current()->contactId() << ", proto "
242 //<< it.current()->protocol()->pluginId() << ", account " << it.current()->accountId() << endl;
243 if( ( c->contactId() == contactId ) && ( c->protocol()->pluginId() == protocolId || protocolId.isNull() ) )
245 if ( accountId.isNull() )
246 return c;
248 if(c->account())
250 if(c->account()->accountId() == accountId)
251 return c;
256 // Contact not found
257 return 0L;
260 void MetaContact::setDisplayNameSource(PropertySource source)
262 QString oldName = displayName();
263 d->displayNameSource = source;
264 QString newName = displayName();
265 if ( oldName != newName)
266 emit displayNameChanged( oldName, newName );
269 void MetaContact::setDisplayNameSource( const QString &nameSourcePID, const QString &nameSourceAID, const QString &nameSourceCID )
271 d->nameSourcePID = nameSourcePID;
272 d->nameSourceAID = nameSourceAID;
273 d->nameSourceCID = nameSourceCID;
276 MetaContact::PropertySource MetaContact::displayNameSource() const
278 return d->displayNameSource;
281 void MetaContact::setPhotoSource(PropertySource source)
283 PropertySource oldSource = photoSource();
284 d->photoSource = source;
285 if ( source != oldSource )
287 emit photoChanged();
291 void MetaContact::setPhotoSource( const QString &photoSourcePID, const QString &photoSourceAID, const QString &photoSourceCID )
293 d->photoSourcePID = photoSourcePID;
294 d->photoSourceAID = photoSourceAID;
295 d->photoSourceCID = photoSourceCID;
298 MetaContact::PropertySource MetaContact::photoSource() const
300 return d->photoSource;
304 Contact *MetaContact::sendMessage()
306 Contact *c = preferredContact();
308 if( !c )
310 KMessageBox::queuedMessageBox( UI::Global::mainWidget(), KMessageBox::Sorry,
311 i18n( "This user is not reachable at the moment. Please make sure you are connected and using a protocol that supports offline sending, or wait "
312 "until this user comes online." ), i18n( "User is Not Reachable" ) );
314 else
316 c->sendMessage();
317 return c;
319 return 0L;
322 Contact *MetaContact::startChat()
324 Contact *c = preferredContact();
326 if( !c )
328 KMessageBox::queuedMessageBox( UI::Global::mainWidget(), KMessageBox::Sorry,
329 i18n( "This user is not reachable at the moment. Please make sure you are connected and using a protocol that supports offline sending, or wait "
330 "until this user comes online." ), i18n( "User is Not Reachable" ) );
332 else
334 c->startChat();
335 return c;
337 return 0L;
340 Contact *MetaContact::preferredContact()
343 This function will determine what contact will be used to reach the contact.
345 The preferred contact is choose with the following criterias: (in that order)
346 1) If a contact was an open chatwindow already, we will use that one.
347 2) The contact with the better online status is used. But if that
348 contact is not reachable, we prefer return no contact.
349 3) If all the criterias aboxe still gives ex-eaquo, we use the preffered
350 account as selected in the account preferances (with the arrows)
353 Contact *contact = 0;
354 bool hasOpenView=false; //has the selected contact already an open chatwindow
355 QListIterator<Contact *> it(d->contacts);
356 while ( it.hasNext() )
358 Contact *c=it.next();
360 //Does the contact an open chatwindow?
361 if( c->manager( Contact::CannotCreate ) )
362 { //no need to check the view. having a manager is enough
363 if( !hasOpenView )
365 contact=c;
366 hasOpenView=true;
367 if( c->isReachable() )
368 continue;
369 } //else, several contact might have an open view, uses following criterias
371 else if( hasOpenView && contact->isReachable() )
372 continue; //This contact has not open view, but the selected contact has, and is reachable
374 // FIXME: The isConnected call should be handled in Contact::isReachable
375 // after KDE 3.2 - Martijn
376 if ( !c->account() || !c->account()->isConnected() || !c->isReachable() )
377 continue; //if this contact is not reachable, we ignore it.
379 if ( !contact )
380 { //this is the first contact.
381 contact= c;
382 continue;
385 if( c->onlineStatus().status() > contact->onlineStatus().status() )
386 contact=c; //this contact has a better status
387 else if ( c->onlineStatus().status() == contact->onlineStatus().status() )
389 if( c->account()->priority() > contact->account()->priority() )
390 contact=c;
391 else if( c->account()->priority() == contact->account()->priority()
392 && c->onlineStatus().weight() > contact->onlineStatus().weight() )
393 contact = c; //the weight is not supposed to follow the same scale for each protocol
396 return contact;
399 Contact *MetaContact::execute()
401 Contact *c = preferredContact();
403 if( !c )
405 KMessageBox::queuedMessageBox( UI::Global::mainWidget(), KMessageBox::Sorry,
406 i18n( "This user is not reachable at the moment. Please make sure you are connected and using a protocol that supports offline sending, or wait "
407 "until this user comes online." ), i18n( "User is Not Reachable" ) );
409 else
411 c->execute();
412 return c;
415 return 0L;
418 unsigned long int MetaContact::idleTime() const
420 unsigned long int time = 0;
421 QListIterator<Contact *> it( d->contacts );
422 while ( it.hasNext() )
424 Contact *c = it.next();
425 unsigned long int i = c->idleTime();
426 if( c->isOnline() && i < time || time == 0 )
428 time = i;
431 return time;
434 QString MetaContact::statusIcon() const
436 switch( status() )
438 case OnlineStatus::Online:
439 if( useCustomIcon() )
440 return icon( ContactListElement::Online );
441 else
442 return QString::fromUtf8( "user-online" );
443 case OnlineStatus::Away:
444 if( useCustomIcon() )
445 return icon( ContactListElement::Away );
446 else
447 return QString::fromUtf8( "user-away" );
449 case OnlineStatus::Unknown:
450 if( useCustomIcon() )
451 return icon( ContactListElement::Unknown );
452 if ( d->contacts.isEmpty() )
453 return QString::fromUtf8( "metacontact_unknown" );
454 else
455 return QString::fromUtf8( "user-offline" );
457 case OnlineStatus::Offline:
458 default:
459 if( useCustomIcon() )
460 return icon( ContactListElement::Offline );
461 else
462 return QString::fromUtf8( "user-offline" );
466 QString MetaContact::statusString() const
468 switch( status() )
470 case OnlineStatus::Online:
471 return i18n( "Online" );
472 case OnlineStatus::Away:
473 return i18n( "Away" );
474 case OnlineStatus::Offline:
475 return i18n( "Offline" );
476 case OnlineStatus::Unknown:
477 default:
478 return i18n( "Status not available" );
482 OnlineStatus::StatusType MetaContact::status() const
484 return d->onlineStatus;
487 bool MetaContact::isOnline() const
489 QListIterator<Contact *> it( d->contacts );
490 while ( it.hasNext() )
492 if( it.next()->isOnline() )
493 return true;
495 return false;
498 bool MetaContact::isReachable() const
500 if ( isOnline() )
501 return true;
503 QListIterator<Contact *> it( d->contacts );
504 while ( it.hasNext() )
506 Contact *c = it.next();
507 if ( c->account()->isConnected() && c->isReachable() )
508 return true;
510 return false;
513 //Determine if we are capable of accepting file transfers
514 bool MetaContact::canAcceptFiles() const
516 if( !isOnline() )
517 return false;
519 QListIterator<Contact *> it( d->contacts );
520 while ( it.hasNext() )
522 if( it.next()->canAcceptFiles() )
523 return true;
525 return false;
528 //Slot for sending files
529 void MetaContact::sendFile( const KUrl &sourceURL, const QString &altFileName, unsigned long fileSize )
531 //If we can't send any files then exit
532 if( d->contacts.isEmpty() || !canAcceptFiles() )
533 return;
535 //Find the highest ranked protocol that can accept files
536 Contact *contact = d->contacts.first();
537 QListIterator<Contact *> it( d->contacts );
538 while ( it.hasNext() )
540 Contact *curr = it.next();
541 if( (curr)->onlineStatus() > contact->onlineStatus() && (curr)->canAcceptFiles() )
542 contact = curr;
545 //Call the sendFile slot of this protocol
546 contact->sendFile( sourceURL, altFileName, fileSize );
549 void MetaContact::emitAboutToSave()
551 emit aboutToSave( this );
554 void MetaContact::slotContactStatusChanged( Contact * c, const OnlineStatus &status, const OnlineStatus &/*oldstatus*/ )
556 updateOnlineStatus();
557 emit contactStatusChanged( c, status );
560 void MetaContact::setDisplayName( const QString &name )
562 /*kDebug( 14010 ) << "Change displayName from " << d->displayName <<
563 " to " << name << ", d->trackChildNameChanges=" << d->trackChildNameChanges << endl;
564 kDebug(14010) << kBacktrace(6);*/
566 if( name == d->displayName )
567 return;
569 if ( loading() )
571 d->displayName = name;
573 else
575 //check if there is another contact with the same display name.
576 //if this is the case, merge them
577 if(!name.isEmpty())
578 foreach(MetaContact *m, ContactList::self()->metaContacts())
580 if( m != this && m->customDisplayName() == name)
582 //merge
583 while(!m->d->contacts.isEmpty())
585 m->d->contacts.first()->setMetaContact(this);
587 //the contact will be automatically removed when the last contact is removed
588 //that's why we merge othe other into this one and not the opposite;
589 break;
593 const QString old = d->displayName;
594 d->displayName = name;
596 emit displayNameChanged( old , name );
597 QListIterator<Kopete::Contact *> it( d->contacts );
598 while ( it.hasNext() )
599 ( it.next() )->sync(Contact::DisplayNameChanged);
604 QString MetaContact::customDisplayName() const
606 return d->displayName;
609 QString MetaContact::displayName() const
611 PropertySource source = displayNameSource();
612 if ( source == SourceKABC )
614 // kabc source, try to get from addressbook
615 // if the metacontact has a kabc association
616 if ( !metaContactId().isEmpty() )
617 return nameFromKABC(metaContactId());
619 else if ( source == SourceContact || d->displayName.isEmpty())
621 if ( d->displayNameSourceContact==0 )
623 if( d->contacts.count() >= 1 )
624 {// don't call setDisplayNameSource , or there will probably be an infinite loop
625 d->displayNameSourceContact=d->contacts.first();
626 // kDebug( 14010 ) << " setting displayname source for " << metaContactId();
629 if ( displayNameSourceContact() != 0L )
631 return nameFromContact(displayNameSourceContact());
633 else
635 // kDebug( 14010 ) << " source == SourceContact , but there is no displayNameSourceContact for contact " << metaContactId();
638 return d->displayName;
641 QString nameFromKABC( const QString &id ) /*const*/
643 KABC::AddressBook* ab = KABCPersistence::self()->addressBook();
644 if ( ! id.isEmpty() && !id.contains(':') )
646 KABC::Addressee theAddressee = ab->findByUid(id);
647 if ( theAddressee.isEmpty() )
649 kDebug( 14010 ) << "no KABC::Addressee found for ( " << id << " ) " << " in current address book";
651 else
653 return theAddressee.formattedName();
656 // no kabc association, return null image
657 return QString();
660 QString nameFromContact( Kopete::Contact *c) /*const*/
662 if ( !c )
663 return QString();
665 QString contactName;
666 if ( c->hasProperty( Kopete::Global::Properties::self()->nickName().key() ) )
667 contactName = c->property( Global::Properties::self()->nickName()).value().toString();
669 //the replace is there to workaround the Bug 95444
670 return contactName.isEmpty() ? c->contactId() : contactName.replace('\n',QString::fromUtf8(""));
673 KUrl MetaContact::customPhoto() const
675 return KUrl(d->customPicture.path());
678 void MetaContact::setPhoto( const KUrl &url )
680 d->photoUrl = url;
681 d->customPicture.setPicture(url.path());
683 if ( photoSource() == SourceCustom )
685 emit photoChanged();
689 QImage MetaContact::photo() const
691 return picture().image();
694 Picture &MetaContact::picture() const
696 if ( photoSource() == SourceKABC )
698 return d->kabcPicture;
700 else if ( photoSource() == SourceContact )
702 return d->contactPicture;
705 return d->customPicture;
708 QImage MetaContact::photoFromCustom() const
710 return d->customPicture.image();
713 QImage photoFromContact( Kopete::Contact *contact) /*const*/
715 if ( contact == 0L )
716 return QImage();
718 QVariant photoProp;
719 if ( contact->hasProperty( Kopete::Global::Properties::self()->photo().key() ) )
720 photoProp = contact->property( Kopete::Global::Properties::self()->photo().key() ).value();
722 QImage img;
723 if(photoProp.canConvert( QVariant::Image ))
724 img= photoProp.value<QImage>();
725 else if(photoProp.canConvert( QVariant::Pixmap ))
726 img=photoProp.value<QPixmap>().toImage();
727 else if(!photoProp.toString().isEmpty())
729 img=QPixmap( photoProp.toString() ).toImage();
731 return img;
734 QImage photoFromKABC( const QString &id ) /*const*/
736 KABC::AddressBook* ab = KABCPersistence::self()->addressBook();
737 if ( ! id.isEmpty() && !id.contains(':') )
739 KABC::Addressee theAddressee = ab->findByUid(id);
740 if ( theAddressee.isEmpty() )
742 kDebug( 14010 ) << "no KABC::Addressee found for ( " << id << " ) " << " in current address book";
744 else
746 KABC::Picture pic = theAddressee.photo();
747 if ( pic.data().isNull() && pic.url().isEmpty() )
748 pic = theAddressee.logo();
750 if ( pic.isIntern())
752 return pic.data();
754 else
756 return QPixmap( pic.url() ).toImage();
760 // no kabc association, return null image
761 return QImage();
764 Contact *MetaContact::displayNameSourceContact() const
766 return d->displayNameSourceContact;
769 Contact *MetaContact::photoSourceContact() const
771 return d->photoSourceContact;
774 void MetaContact::setDisplayNameSourceContact( Contact *contact )
776 Contact *old = d->displayNameSourceContact;
777 d->displayNameSourceContact = contact;
778 if ( displayNameSource() == SourceContact )
780 emit displayNameChanged( nameFromContact(old), nameFromContact(contact));
784 void MetaContact::setPhotoSourceContact( Contact *contact )
786 d->photoSourceContact = contact;
788 // Create a cache for the contact photo.
789 if(d->photoSourceContact != 0L)
791 QVariant photoProp;
792 if ( contact->hasProperty( Kopete::Global::Properties::self()->photo().key() ) )
794 photoProp = contact->property( Kopete::Global::Properties::self()->photo().key() ).value();
796 if(photoProp.canConvert( QVariant::Image ))
798 d->contactPicture.setPicture( photoProp.value<QImage>() );
800 else if(photoProp.canConvert( QVariant::Pixmap ))
802 d->contactPicture.setPicture( photoProp.value<QPixmap>().toImage() );
804 else if(!photoProp.toString().isEmpty())
806 d->contactPicture.setPicture(photoProp.toString());
811 if ( photoSource() == SourceContact )
813 emit photoChanged();
817 void MetaContact::slotPropertyChanged( PropertyContainer* _subcontact, const QString &key,
818 const QVariant &oldValue, const QVariant &newValue )
820 Contact *subcontact=static_cast<Contact*>(_subcontact);
821 if ( displayNameSource() == SourceContact ||
822 (d->displayName.isEmpty() && displayNameSource() == SourceCustom) )
824 if( key == Global::Properties::self()->nickName().key() )
826 if (displayNameSourceContact() == subcontact)
828 emit displayNameChanged( oldValue.toString(), newValue.toString());
830 else
832 // HACK the displayName that changed is not from the contact we are tracking, but
833 // as the current one is null, lets use this new one
834 if (displayName().isEmpty())
835 setDisplayNameSourceContact(subcontact);
840 if (photoSource() == SourceContact)
842 if ( key == Global::Properties::self()->photo().key() )
844 if (photoSourceContact() != subcontact)
846 // HACK the displayName that changed is not from the contact we are tracking, but
847 // as the current one is null, lets use this new one
848 if (picture().isNull())
849 setPhotoSourceContact(subcontact);
852 else if(photoSourceContact() == subcontact)
854 if(d->photoSyncedWithKABC)
855 setPhotoSyncedWithKABC(true);
857 setPhotoSourceContact(subcontact);
863 void MetaContact::moveToGroup( Group *from, Group *to )
865 if ( !from || !groups().contains( from ) )
867 // We're adding, not moving, because 'from' is illegal
868 addToGroup( to );
869 return;
872 if ( !to || groups().contains( to ) )
874 // We're removing, not moving, because 'to' is illegal
875 removeFromGroup( from );
876 return;
879 if ( isTemporary() && to->type() != Group::Temporary )
880 return;
883 //kDebug( 14010 ) << from->displayName() << " => " << to->displayName();
885 d->groups.removeAll( from );
886 d->groups.append( to );
888 QListIterator<Contact *> it(d->contacts);
889 while( it.hasNext() )
890 it.next()->sync(Contact::MovedBetweenGroup);
892 emit movedToGroup( this, from, to );
895 void MetaContact::removeFromGroup( Group *group )
897 if ( !group || !groups().contains( group ) || ( isTemporary() && group->type() == Group::Temporary ) )
899 return;
902 d->groups.removeAll( group );
904 // make sure MetaContact is at least in one group
905 if ( d->groups.isEmpty() )
907 d->groups.append( Group::topLevel() );
908 emit addedToGroup( this, Group::topLevel() );
911 QListIterator<Contact *> it(d->contacts);
912 while( it.hasNext() )
913 it.next()->sync(Contact::MovedBetweenGroup);
915 emit removedFromGroup( this, group );
918 void MetaContact::addToGroup( Group *to )
920 if ( !to || groups().contains( to ) )
921 return;
923 if ( d->temporary && to->type() != Group::Temporary )
924 return;
926 if ( d->groups.contains( Group::topLevel() ) )
928 d->groups.removeAll( Group::topLevel() );
929 emit removedFromGroup( this, Group::topLevel() );
932 d->groups.append( to );
934 QListIterator<Contact *> it(d->contacts);
935 while( it.hasNext() )
936 it.next()->sync(Contact::MovedBetweenGroup);
938 emit addedToGroup( this, to );
941 QList<Group *> MetaContact::groups() const
943 return d->groups;
946 void MetaContact::slotContactDestroyed( Contact *contact )
948 removeContact(contact,true);
951 QString MetaContact::addressBookField( Kopete::Plugin * /* p */, const QString &app, const QString & key ) const
953 return d->addressBook[ app ][ key ];
956 void Kopete::MetaContact::setAddressBookField( Kopete::Plugin * /* p */, const QString &app, const QString &key, const QString &value )
958 d->addressBook[ app ][ key ] = value;
961 void MetaContact::slotPluginLoaded( Plugin *p )
963 if( !p )
964 return;
966 QMap<QString, QString> map= pluginData( p );
967 if(!map.isEmpty())
969 p->deserialize(this,map);
973 void MetaContact::slotAllPluginsLoaded()
975 // Now that the plugins and subcontacts are loaded, set the source contact.
976 setDisplayNameSourceContact( findContact( d->nameSourcePID, d->nameSourceAID, d->nameSourceCID) );
977 setPhotoSourceContact( findContact( d->photoSourcePID, d->photoSourceAID, d->photoSourceCID) );
980 void MetaContact::slotUpdateAddressBookPicture()
982 KABC::AddressBook* ab = KABCPersistence::self()->addressBook();
983 QString id = metaContactId();
984 if ( !id.isEmpty() && !id.contains(':') )
986 KABC::Addressee theAddressee = ab->findByUid(id);
987 if ( theAddressee.isEmpty() )
989 kDebug( 14010 ) << "no KABC::Addressee found for ( " << id << " ) " << " in current address book";
991 else
993 KABC::Picture pic = theAddressee.photo();
994 if ( pic.data().isNull() && pic.url().isEmpty() )
995 pic = theAddressee.logo();
997 d->kabcPicture.setPicture(pic);
1002 bool MetaContact::isTemporary() const
1004 return d->temporary;
1007 void MetaContact::setTemporary( bool isTemporary, Group *group )
1009 d->temporary = isTemporary;
1010 Group *temporaryGroup = Group::temporary();
1011 if ( d->temporary )
1013 addToGroup (temporaryGroup);
1014 QListIterator<Group *> it(d->groups);
1015 while ( it.hasNext() )
1017 Group *g = it.next();
1018 if(g != temporaryGroup)
1019 removeFromGroup(g);
1022 else
1023 moveToGroup(temporaryGroup, group ? group : Group::topLevel());
1026 QString MetaContact::metaContactId() const
1028 if(d->metaContactId.isEmpty())
1030 if(d->contacts.isEmpty())
1031 return QString();
1032 Contact *c=d->contacts.first();
1033 if(!c)
1034 return QString();
1035 return c->protocol()->pluginId()+QString::fromUtf8(":")+c->account()->accountId()+QString::fromUtf8(":") + c->contactId() ;
1037 return d->metaContactId;
1040 void MetaContact::setMetaContactId( const QString& newMetaContactId )
1042 if(newMetaContactId == d->metaContactId)
1043 return;
1045 // 1) Check the Id is not already used by another contact
1046 // 2) cause a kabc write ( only in response to metacontactLVIProps calling this, or will
1047 // write be called twice when creating a brand new MC? )
1048 // 3) What about changing from one valid kabc to another, are kabc fields removed?
1049 // 4) May be called with Null to remove an invalid kabc uid by KMC::toKABC()
1050 // 5) Is called when reading the saved contact list
1052 // Don't remove IM addresses from kabc if we are changing contacts;
1053 // other programs may have written that data and depend on it
1054 d->metaContactId = newMetaContactId;
1055 if ( loading() )
1057 slotUpdateAddressBookPicture();
1059 else
1061 KABCPersistence::self()->write( this );
1062 emit onlineStatusChanged( this, d->onlineStatus );
1063 emit persistentDataChanged();
1067 bool MetaContact::isPhotoSyncedWithKABC() const
1069 return d->photoSyncedWithKABC;
1072 void MetaContact::setPhotoSyncedWithKABC(bool b)
1074 d->photoSyncedWithKABC=b;
1075 if( b && !loading() )
1077 QVariant newValue;
1079 switch( photoSource() )
1081 case SourceContact:
1083 Contact *source = photoSourceContact();
1084 if(source != 0L)
1085 newValue = source->property( Kopete::Global::Properties::self()->photo() ).value();
1086 break;
1088 case SourceCustom:
1090 if( !d->customPicture.isNull() )
1091 newValue = d->customPicture.path();
1092 break;
1094 // Don't sync the photo with KABC if the source is KABC !
1095 default:
1096 return;
1099 if ( !d->metaContactId.isEmpty() && !newValue.isNull())
1101 KABC::Addressee theAddressee = KABCPersistence::self()->addressBook()->findByUid( metaContactId() );
1103 if ( !theAddressee.isEmpty() )
1105 QImage img;
1106 if(newValue.canConvert( QVariant::Image ))
1107 img=newValue.value<QImage>();
1108 else if(newValue.canConvert( QVariant::Pixmap ))
1109 img=newValue.value<QPixmap>().toImage();
1111 if(img.isNull())
1113 // Some protocols like MSN save the photo as a url in
1114 // contact properties, we should not use this url
1115 // to sync with kabc but try first to embed the
1116 // photo data in the kabc addressee, because it could
1117 // be remote resource and the local url makes no sense
1118 QImage fallBackImage = QImage(newValue.toString());
1119 if(fallBackImage.isNull())
1120 theAddressee.setPhoto(newValue.toString());
1121 else
1122 theAddressee.setPhoto(fallBackImage);
1124 else
1125 theAddressee.setPhoto(img);
1127 KABCPersistence::self()->addressBook()->insertAddressee(theAddressee);
1128 KABCPersistence::self()->writeAddressBook( theAddressee.resource() );
1135 QList<Contact *> MetaContact::contacts() const
1137 return d->contacts;
1142 } //END namespace Kopete
1144 #include "kopetemetacontact.moc"
1146 // vim: set noet ts=4 sts=4 sw=4: