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 *************************************************************************
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 "kopetemetacontact.h"
22 #include "kopetemetacontact_p.h"
25 #include <kabc/addressbook.h>
26 #include <kabc/addressee.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"
46 MetaContact::MetaContact()
47 : ContactListElement( ContactList::self() )
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()
75 void MetaContact::addContact( Contact
*c
)
77 if( d
->contacts
.contains( c
) )
79 kWarning(14010) << "Ignoring attempt to add duplicate contact " << c
->contactId() << "!";
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
* ) ) );
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 ";
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
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
);
184 // we didn't fallback to SourceCustom above so lets use the next
186 setDisplayNameSourceContact( d
->contacts
.first() );
190 if ( wasTrackingPhoto
)
192 // Oh! this contact was the source for the metacontact's photo
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
204 // we didn't fallback to SourceCustom above so lets use the next
206 setPhotoSourceContact( d
->contacts
.first() );
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
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() )
250 if(c
->account()->accountId() == accountId
)
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
)
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();
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" ) );
322 Contact
*MetaContact::startChat()
324 Contact
*c
= preferredContact();
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" ) );
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
367 if( c
->isReachable() )
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.
380 { //this is the first contact.
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() )
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
399 Contact
*MetaContact::execute()
401 Contact
*c
= preferredContact();
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" ) );
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 )
434 QString
MetaContact::statusIcon() const
438 case OnlineStatus::Online
:
439 if( useCustomIcon() )
440 return icon( ContactListElement::Online
);
442 return QString::fromUtf8( "user-online" );
443 case OnlineStatus::Away
:
444 if( useCustomIcon() )
445 return icon( ContactListElement::Away
);
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" );
455 return QString::fromUtf8( "user-offline" );
457 case OnlineStatus::Offline
:
459 if( useCustomIcon() )
460 return icon( ContactListElement::Offline
);
462 return QString::fromUtf8( "user-offline" );
466 QString
MetaContact::statusString() const
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
:
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() )
498 bool MetaContact::isReachable() const
503 QListIterator
<Contact
*> it( d
->contacts
);
504 while ( it
.hasNext() )
506 Contact
*c
= it
.next();
507 if ( c
->account()->isConnected() && c
->isReachable() )
513 //Determine if we are capable of accepting file transfers
514 bool MetaContact::canAcceptFiles() const
519 QListIterator
<Contact
*> it( d
->contacts
);
520 while ( it
.hasNext() )
522 if( it
.next()->canAcceptFiles() )
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() )
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() )
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
)
571 d
->displayName
= name
;
575 //check if there is another contact with the same display name.
576 //if this is the case, merge them
578 foreach(MetaContact
*m
, ContactList::self()->metaContacts())
580 if( m
!= this && m
->customDisplayName() == name
)
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;
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());
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";
653 return theAddressee
.formattedName();
656 // no kabc association, return null image
660 QString
nameFromContact( Kopete::Contact
*c
) /*const*/
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
)
681 d
->customPicture
.setPicture(url
.path());
683 if ( photoSource() == SourceCustom
)
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*/
719 if ( contact
->hasProperty( Kopete::Global::Properties::self()->photo().key() ) )
720 photoProp
= contact
->property( Kopete::Global::Properties::self()->photo().key() ).value();
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();
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";
746 KABC::Picture pic
= theAddressee
.photo();
747 if ( pic
.data().isNull() && pic
.url().isEmpty() )
748 pic
= theAddressee
.logo();
756 return QPixmap( pic
.url() ).toImage();
760 // no kabc association, return null image
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)
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
)
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());
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
872 if ( !to
|| groups().contains( to
) )
874 // We're removing, not moving, because 'to' is illegal
875 removeFromGroup( from
);
879 if ( isTemporary() && to
->type() != Group::Temporary
)
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
) )
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
) )
923 if ( d
->temporary
&& to
->type() != Group::Temporary
)
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
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
)
966 QMap
<QString
, QString
> map
= pluginData( p
);
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";
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();
1013 addToGroup (temporaryGroup
);
1014 QListIterator
<Group
*> it(d
->groups
);
1015 while ( it
.hasNext() )
1017 Group
*g
= it
.next();
1018 if(g
!= temporaryGroup
)
1023 moveToGroup(temporaryGroup
, group
? group
: Group::topLevel());
1026 QString
MetaContact::metaContactId() const
1028 if(d
->metaContactId
.isEmpty())
1030 if(d
->contacts
.isEmpty())
1032 Contact
*c
=d
->contacts
.first();
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
)
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
;
1057 slotUpdateAddressBookPicture();
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() )
1079 switch( photoSource() )
1083 Contact
*source
= photoSourceContact();
1085 newValue
= source
->property( Kopete::Global::Properties::self()->photo() ).value();
1090 if( !d
->customPicture
.isNull() )
1091 newValue
= d
->customPicture
.path();
1094 // Don't sync the photo with KABC if the source is KABC !
1099 if ( !d
->metaContactId
.isEmpty() && !newValue
.isNull())
1101 KABC::Addressee theAddressee
= KABCPersistence::self()->addressBook()->findByUid( metaContactId() );
1103 if ( !theAddressee
.isEmpty() )
1106 if(newValue
.canConvert( QVariant::Image
))
1107 img
=newValue
.value
<QImage
>();
1108 else if(newValue
.canConvert( QVariant::Pixmap
))
1109 img
=newValue
.value
<QPixmap
>().toImage();
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());
1122 theAddressee
.setPhoto(fallBackImage
);
1125 theAddressee
.setPhoto(img
);
1127 KABCPersistence::self()->addressBook()->insertAddressee(theAddressee
);
1128 KABCPersistence::self()->writeAddressBook( theAddressee
.resource() );
1135 QList
<Contact
*> MetaContact::contacts() const
1142 } //END namespace Kopete
1144 #include "kopetemetacontact.moc"
1146 // vim: set noet ts=4 sts=4 sw=4: