/* kopetecontactlist.cpp - Kopete's Contact List backend Copyright (c) 2005 by Michael Larouche Copyright (c) 2002-2003 by Martijn Klingens Copyright (c) 2002-2004 by Olivier Goffart Copyright (c) 2002 by Duncan Mac-Vicar Prett Copyright (c) 2002-2004 by the Kopete developers ************************************************************************* * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * ************************************************************************* */ #include "kopetecontactlist.h" #include #include #include #include #include #include #include #include #include #include #include "kopetemetacontact.h" #include "kopetecontact.h" #include "kopetechatsession.h" //#include "kopetemessage.h" #include "kopetepluginmanager.h" #include "kopeteprotocol.h" #include "kopeteaccount.h" #include "kopeteaccountmanager.h" #include "kopetegroup.h" #include "kopetepicture.h" namespace Kopete { class ContactList::Private {public: /** Flag: do not save the contactlist until she is completely loaded */ bool loaded ; TQPtrList contacts; TQPtrList groups; TQPtrList selectedMetaContacts; TQPtrList selectedGroups; TQTimer *saveTimer; MetaContact *myself; /** Flag: does the user uses the global identity */ bool useGlobalIdentity; /** * Current contact list version * 10 ( i.e. '10' is version '1.0' ) */ static const uint ContactListVersion = 10; }; ContactList *ContactList::s_self = 0L; ContactList *ContactList::self() { if( !s_self ) s_self = new ContactList; return s_self; } ContactList::ContactList() : TQObject( kapp, "KopeteContactList" ) { d=new Private; //the myself metacontact can't be created now, because it will use //ContactList::self() as parent which will call this constructor -> infinite loop d->myself=0L; //no contactlist loaded yet, don't save them d->loaded=false; // automatically save on changes to the list d->saveTimer = new TQTimer( this, "saveTimer" ); connect( d->saveTimer, TQT_SIGNAL( timeout() ), TQT_SLOT ( save() ) ); connect( this, TQT_SIGNAL( metaContactAdded( Kopete::MetaContact * ) ), TQT_SLOT( slotSaveLater() ) ); connect( this, TQT_SIGNAL( metaContactRemoved( Kopete::MetaContact * ) ), TQT_SLOT( slotSaveLater() ) ); connect( this, TQT_SIGNAL( groupAdded( Kopete::Group * ) ), TQT_SLOT( slotSaveLater() ) ); connect( this, TQT_SIGNAL( groupRemoved( Kopete::Group * ) ), TQT_SLOT( slotSaveLater() ) ); connect( this, TQT_SIGNAL( groupRenamed( Kopete::Group *, const TQString & ) ), TQT_SLOT( slotSaveLater() ) ); } ContactList::~ContactList() { delete d->myself; delete d; } TQPtrList ContactList::metaContacts() const { return d->contacts; } TQPtrList ContactList::groups() const { return d->groups; } MetaContact *ContactList::metaContact( const TQString &metaContactId ) const { TQPtrListIterator it( d->contacts ); for( ; it.current(); ++it ) { if( it.current()->metaContactId() == metaContactId ) return it.current(); } return 0L; } Group * ContactList::group(unsigned int groupId) const { Group *groupIterator; for ( groupIterator = d->groups.first(); groupIterator; groupIterator = d->groups.next() ) { if( groupIterator->groupId()==groupId ) return groupIterator; } return 0L; } Contact *ContactList::findContact( const TQString &protocolId, const TQString &accountId, const TQString &contactId ) const { //Browsing metacontacts is too slow, better to uses the Dict of the account. Account *i=AccountManager::self()->findAccount(protocolId,accountId); if(!i) { kdDebug( 14010 ) << k_funcinfo << "Account not found" << endl; return 0L; } return i->contacts()[contactId]; } MetaContact *ContactList::findMetaContactByDisplayName( const TQString &displayName ) const { TQPtrListIterator it( d->contacts ); for( ; it.current(); ++it ) { // kdDebug(14010) << "Display Name: " << it.current()->displayName() << "\n"; if( it.current()->displayName() == displayName ) { return it.current(); } } return 0L; } MetaContact* ContactList::findMetaContactByContactId( const TQString &contactId ) const { TQPtrList acts=AccountManager::self()->accounts(); TQPtrListIterator it( acts ); for ( ; it.current(); ++it ) { Contact *c=(*it)->contacts()[contactId]; if(c && c->metaContact()) return c->metaContact(); } return 0L; } Group * ContactList::findGroup(const TQString& displayName, int type) { if( type == Group::Temporary ) return Group::temporary(); Group *groupIterator; for ( groupIterator = d->groups.first(); groupIterator; groupIterator = d->groups.next() ) { if( groupIterator->type() == type && groupIterator->displayName() == displayName ) return groupIterator; } Group *newGroup = new Group( displayName, (Group::GroupType)type ); addGroup( newGroup ); return newGroup; } TQPtrList ContactList::selectedMetaContacts() const { return d->selectedMetaContacts; } TQPtrList ContactList::selectedGroups() const { return d->selectedGroups; } void ContactList::addMetaContact( MetaContact *mc ) { if ( d->contacts.contains( mc ) ) return; d->contacts.append( mc ); emit metaContactAdded( mc ); connect( mc, TQT_SIGNAL( persistentDataChanged( ) ), TQT_SLOT( slotSaveLater() ) ); connect( mc, TQT_SIGNAL( addedToGroup( Kopete::MetaContact *, Kopete::Group * ) ), TQT_SIGNAL( metaContactAddedToGroup( Kopete::MetaContact *, Kopete::Group * ) ) ); connect( mc, TQT_SIGNAL( removedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ), TQT_SIGNAL( metaContactRemovedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ) ); } void ContactList::removeMetaContact(MetaContact *m) { if ( !d->contacts.contains(m) ) { kdDebug(14010) << k_funcinfo << "Trying to remove a not listed MetaContact." << endl; return; } if ( d->selectedMetaContacts.contains( m ) ) { d->selectedMetaContacts.remove( m ); setSelectedItems( d->selectedMetaContacts, d->selectedGroups ); } //removes subcontact from server here and now. TQPtrList cts=m->contacts(); for( Contact *c = cts.first(); c; c = cts.next() ) { c->deleteContact(); } d->contacts.remove( m ); emit metaContactRemoved( m ); m->deleteLater(); } void ContactList::addGroup( Group * g ) { if(!d->groups.contains(g) ) { d->groups.append( g ); emit groupAdded( g ); connect( g , TQT_SIGNAL ( displayNameChanged(Kopete::Group* , const TQString & )) , this , TQT_SIGNAL ( groupRenamed(Kopete::Group* , const TQString & )) ) ; } } void ContactList::removeGroup( Group *g ) { if ( d->selectedGroups.contains( g ) ) { d->selectedGroups.remove( g ); setSelectedItems( d->selectedMetaContacts, d->selectedGroups ); } d->groups.remove( g ); emit groupRemoved( g ); g->deleteLater(); } void ContactList::setSelectedItems(TQPtrList metaContacts , TQPtrList groups) { kdDebug( 14010 ) << k_funcinfo << metaContacts.count() << " metacontacts, " << groups.count() << " groups selected" << endl; d->selectedMetaContacts=metaContacts; d->selectedGroups=groups; emit metaContactSelected( groups.isEmpty() && metaContacts.count()==1 ); emit selectionChanged(); } MetaContact* ContactList::myself() { if(!d->myself) d->myself=new MetaContact(); return d->myself; } void ContactList::loadGlobalIdentity() { // Apply the global identity if(Kopete::Config::enableGlobalIdentity()) { // Disconnect to make sure it will not cause duplicate calls. disconnect(myself(), TQT_SIGNAL(displayNameChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotDisplayNameChanged())); disconnect(myself(), TQT_SIGNAL(photoChanged()), this, TQT_SLOT(slotPhotoChanged())); connect(myself(), TQT_SIGNAL(displayNameChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotDisplayNameChanged())); connect(myself(), TQT_SIGNAL(photoChanged()), this, TQT_SLOT(slotPhotoChanged())); // Ensure that the myself metaContactId is always the KABC whoAmI TDEABC::Addressee a = TDEABC::StdAddressBook::self()->whoAmI(); if(!a.isEmpty() && a.uid() != myself()->metaContactId()) { myself()->setMetaContactId(a.uid()); } // Apply the global identity // Maybe one of the myself contact from a account has a different displayName/photo at startup. slotDisplayNameChanged(); slotPhotoChanged(); } else { disconnect(myself(), TQT_SIGNAL(displayNameChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotDisplayNameChanged())); disconnect(myself(), TQT_SIGNAL(photoChanged()), this, TQT_SLOT(slotPhotoChanged())); } } void ContactList::slotDisplayNameChanged() { static bool mutex=false; if(mutex) { kdDebug (14010) << k_funcinfo << " mutex blocked" << endl ; return; } mutex=true; kdDebug( 14010 ) << k_funcinfo << myself()->displayName() << endl; emit globalIdentityChanged(Kopete::Global::Properties::self()->nickName().key(), myself()->displayName()); mutex=false; } void ContactList::slotPhotoChanged() { static bool mutex=false; if(mutex) { kdDebug (14010) << k_funcinfo << " mutex blocked" << endl ; return; } mutex=true; kdDebug( 14010 ) << k_funcinfo << myself()->picture().path() << endl; emit globalIdentityChanged(Kopete::Global::Properties::self()->photo().key(), myself()->picture().path()); mutex=false; } /////////////////////////////////////////////////////////////////////////////////////////////// void ContactList::load() { loadXML(); // Apply the global identity when all the protocols plugins are loaded. connect(PluginManager::self(), TQT_SIGNAL(allPluginsLoaded()), this, TQT_SLOT(loadGlobalIdentity())); } void ContactList::loadXML() { // don't save when we're in the middle of this... d->loaded = false; TQString filename = locateLocal( "appdata", TQString::fromLatin1( "contactlist.xml" ) ); if( filename.isEmpty() ) { d->loaded=true; return ; } TQDomDocument contactList( TQString::fromLatin1( "kopete-contact-list" ) ); TQFile contactListFile( filename ); contactListFile.open( IO_ReadOnly ); contactList.setContent( &contactListFile ); TQDomElement list = contactList.documentElement(); TQString versionString = list.attribute( TQString::fromLatin1( "version" ), TQString() ); uint version = 0; if( TQRegExp( TQString::fromLatin1( "[0-9]+\\.[0-9]" ) ).exactMatch( versionString ) ) version = versionString.replace( TQString::fromLatin1( "." ), TQString() ).toUInt(); if( version < Private::ContactListVersion ) { // The version string is invalid, or we're using an older version. // Convert first and reparse the file afterwards kdDebug( 14010 ) << k_funcinfo << "Contact list version " << version << " is older than current version " << Private::ContactListVersion << ". Converting first." << endl; contactListFile.close(); convertContactList( filename, version, Private::ContactListVersion ); contactList = TQDomDocument ( TQString::fromLatin1( "kopete-contact-list" ) ); contactListFile.open( IO_ReadOnly ); contactList.setContent( &contactListFile ); list = contactList.documentElement(); } addGroup( Kopete::Group::topLevel() ); TQDomElement element = list.firstChild().toElement(); while( !element.isNull() ) { if( element.tagName() == TQString::fromLatin1("meta-contact") ) { //TODO: id isn't used //TQString id = element.attribute( "id", TQString() ); Kopete::MetaContact *metaContact = new Kopete::MetaContact(); if ( !metaContact->fromXML( element ) ) { delete metaContact; metaContact = 0; } else { Kopete::ContactList::self()->addMetaContact( metaContact ); } } else if( element.tagName() == TQString::fromLatin1("kopete-group") ) { Kopete::Group *group = new Kopete::Group(); if( !group->fromXML( element ) ) { delete group; group = 0; } else { Kopete::ContactList::self()->addGroup( group ); } } // Only load myself metacontact information when Global Identity is enabled. else if( element.tagName() == TQString::fromLatin1("myself-meta-contact") && Kopete::Config::enableGlobalIdentity() ) { if( !myself()->fromXML( element ) ) { delete d->myself; d->myself = 0; } } else { kdWarning(14010) << "Kopete::ContactList::loadXML: " << "Unknown element '" << element.tagName() << "' in contact list!" << endl; } element = element.nextSibling().toElement(); } contactListFile.close(); d->loaded=true; } void ContactList::convertContactList( const TQString &fileName, uint /* fromVersion */, uint /* toVersion */ ) { // For now, ignore fromVersion and toVersion. These are meant for future // changes to allow incremental (multi-pass) conversion so we don't have // to rewrite the whole conversion code for each change. TQDomDocument contactList( TQString::fromLatin1( "messaging-contact-list" ) ); TQFile contactListFile( fileName ); contactListFile.open( IO_ReadOnly ); contactList.setContent( &contactListFile ); TQDomElement oldList = contactList.documentElement(); TQDomDocument newList( TQString::fromLatin1( "kopete-contact-list" ) ); newList.appendChild( newList.createProcessingInstruction( TQString::fromLatin1( "xml" ), TQString::fromLatin1( "version=\"1.0\"" ) ) ); TQDomElement newRoot = newList.createElement( TQString::fromLatin1( "kopete-contact-list" ) ); newList.appendChild( newRoot ); newRoot.setAttribute( TQString::fromLatin1( "version" ), TQString::fromLatin1( "1.0" ) ); TQDomNode oldNode = oldList.firstChild(); while( !oldNode.isNull() ) { TQDomElement oldElement = oldNode.toElement(); if( !oldElement.isNull() ) { if( oldElement.tagName() == TQString::fromLatin1("meta-contact") ) { // Ignore ID, it is not used in the current list anyway TQDomElement newMetaContact = newList.createElement( TQString::fromLatin1( "meta-contact" ) ); newRoot.appendChild( newMetaContact ); // Plugin data is stored completely different, and requires // some bookkeeping to convert properly TQMap pluginData; TQStringList icqData; TQStringList gaduData; // ICQ and Gadu can only be converted properly if the address book fields // are already parsed. Therefore, scan for those first and add the rest // afterwards TQDomNode oldContactNode = oldNode.firstChild(); while( !oldContactNode.isNull() ) { TQDomElement oldContactElement = oldContactNode.toElement(); if( !oldContactElement.isNull() && oldContactElement.tagName() == TQString::fromLatin1("address-book-field") ) { // Convert address book fields. // Jabber will be called "xmpp", Aim/Toc and Aim/Oscar both will // be called "aim". AIM, IRC, Oscar and SMS don't use address // book fields yet; Gadu and ICQ can be converted as-is. // As Yahoo is unfinished we won't try to convert at all. TQString id = oldContactElement.attribute( TQString::fromLatin1( "id" ), TQString() ); TQString data = oldContactElement.text(); TQString app, key, val; TQString separator = TQString::fromLatin1( "," ); if( id == TQString::fromLatin1( "messaging/gadu" ) ) separator = TQString::fromLatin1( "\n" ); else if( id == TQString::fromLatin1( "messaging/icq" ) ) separator = TQString::fromLatin1( ";" ); else if( id == TQString::fromLatin1( "messaging/jabber" ) ) id = TQString::fromLatin1( "messaging/xmpp" ); if( id == TQString::fromLatin1( "messaging/gadu" ) || id == TQString::fromLatin1( "messaging/icq" ) || id == TQString::fromLatin1( "messaging/winpopup" ) || id == TQString::fromLatin1( "messaging/xmpp" ) ) { app = id; key = TQString::fromLatin1( "All" ); val = data.replace( separator, TQChar( 0xE000 ) ); } if( !app.isEmpty() ) { TQDomElement addressBookField = newList.createElement( TQString::fromLatin1( "address-book-field" ) ); newMetaContact.appendChild( addressBookField ); addressBookField.setAttribute( TQString::fromLatin1( "app" ), app ); addressBookField.setAttribute( TQString::fromLatin1( "key" ), key ); addressBookField.appendChild( newList.createTextNode( val ) ); // ICQ didn't store the contactId locally, only in the address // book fields, so we need to be able to access it later if( id == TQString::fromLatin1( "messaging/icq" ) ) icqData = TQStringList::split( TQChar( 0xE000 ), val ); else if( id == TQString::fromLatin1("messaging/gadu") ) gaduData = TQStringList::split( TQChar( 0xE000 ), val ); } } oldContactNode = oldContactNode.nextSibling(); } // Now, convert the other elements oldContactNode = oldNode.firstChild(); while( !oldContactNode.isNull() ) { TQDomElement oldContactElement = oldContactNode.toElement(); if( !oldContactElement.isNull() ) { if( oldContactElement.tagName() == TQString::fromLatin1("display-name") ) { TQDomElement displayName = newList.createElement( TQString::fromLatin1( "display-name" ) ); displayName.appendChild( newList.createTextNode( oldContactElement.text() ) ); newMetaContact.appendChild( displayName ); } else if( oldContactElement.tagName() == TQString::fromLatin1("groups") ) { TQDomElement groups = newList.createElement( TQString::fromLatin1( "groups" ) ); newMetaContact.appendChild( groups ); TQDomNode oldGroup = oldContactElement.firstChild(); while( !oldGroup.isNull() ) { TQDomElement oldGroupElement = oldGroup.toElement(); if ( oldGroupElement.tagName() == TQString::fromLatin1("group") ) { TQDomElement group = newList.createElement( TQString::fromLatin1( "group" ) ); group.appendChild( newList.createTextNode( oldGroupElement.text() ) ); groups.appendChild( group ); } else if ( oldGroupElement.tagName() == TQString::fromLatin1("top-level") ) { TQDomElement group = newList.createElement( TQString::fromLatin1( "top-level" ) ); groups.appendChild( group ); } oldGroup = oldGroup.nextSibling(); } } else if( oldContactElement.tagName() == TQString::fromLatin1( "plugin-data" ) ) { // Convert the plugin data TQString id = oldContactElement.attribute( TQString::fromLatin1( "plugin-id" ), TQString() ); TQString data = oldContactElement.text(); bool convertOldAim = false; uint fieldCount = 1; TQString addressBookLabel; if( id == TQString::fromLatin1("IRCProtocol") ) { fieldCount = 3; addressBookLabel = TQString::fromLatin1("irc"); } else if( id == TQString::fromLatin1("OscarProtocol") ) { fieldCount = 2; addressBookLabel = TQString::fromLatin1("aim"); } else if( id == TQString::fromLatin1("AIMProtocol") ) { id = TQString::fromLatin1("OscarProtocol"); convertOldAim = true; addressBookLabel = TQString::fromLatin1("aim"); } else if( id == TQString::fromLatin1("ICQProtocol") || id == TQString::fromLatin1("WPProtocol") || id == TQString::fromLatin1("GaduProtocol") ) { fieldCount = 1; } else if( id == TQString::fromLatin1("JabberProtocol") ) { fieldCount = 4; } else if( id == TQString::fromLatin1("SMSProtocol") ) { // SMS used a variable serializing using a dot as delimiter. // The minimal count is three though (id, name, delimiter). fieldCount = 2; addressBookLabel = TQString::fromLatin1("sms"); } if( pluginData[ id ].isNull() ) { pluginData[ id ] = newList.createElement( TQString::fromLatin1( "plugin-data" ) ); pluginData[ id ].setAttribute( TQString::fromLatin1( "plugin-id" ), id ); newMetaContact.appendChild( pluginData[ id ] ); } // Do the actual conversion if ( id == TQString::fromLatin1( "OscarProtocol" ) || id == TQString::fromLatin1( "AIMProtocol" ) || id == TQString::fromLatin1( "IRCProtocol" ) || id == TQString::fromLatin1( "ICQProtocol" ) || id == TQString::fromLatin1( "JabberProtocol" ) || id == TQString::fromLatin1( "SMSProtocol" ) || id == TQString::fromLatin1( "WPProtocol" ) || id == TQString::fromLatin1( "GaduProtocol" ) ) { TQStringList strList = TQStringList::split( TQString::fromLatin1( "||" ), data ); // Unescape '||' for( TQStringList::iterator it = strList.begin(); it != strList.end(); ++it ) { ( *it ).replace( TQString::fromLatin1( "\\|;" ), TQString::fromLatin1( "|" ) ). replace( TQString::fromLatin1( "\\\\" ), TQString::fromLatin1( "\\" ) ); } uint idx = 0; while( idx < strList.size() ) { TQDomElement dataField; dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) ); pluginData[ id ].appendChild( dataField ); dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "contactId" ) ); if( id == TQString::fromLatin1("ICQProtocol") ) dataField.appendChild( newList.createTextNode( icqData[ idx ] ) ); else if( id == TQString::fromLatin1("GaduProtocol") ) dataField.appendChild( newList.createTextNode( gaduData[ idx ] ) ); else if( id == TQString::fromLatin1("JabberProtocol") ) dataField.appendChild( newList.createTextNode( strList[ idx + 1 ] ) ); else dataField.appendChild( newList.createTextNode( strList[ idx ] ) ); dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) ); pluginData[ id ].appendChild( dataField ); dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "displayName" ) ); if( convertOldAim || id == TQString::fromLatin1("ICQProtocol") || id == TQString::fromLatin1("WPProtocol") || id == TQString::fromLatin1("GaduProtocol") ) dataField.appendChild( newList.createTextNode( strList[ idx ] ) ); else if( id == TQString::fromLatin1("JabberProtocol") ) dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) ); else dataField.appendChild( newList.createTextNode( strList[ idx + 1 ] ) ); if( id == TQString::fromLatin1("IRCProtocol") ) { dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) ); pluginData[ id ].appendChild( dataField ); dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "serverName" ) ); dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) ); } else if( id == TQString::fromLatin1("JabberProtocol") ) { dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) ); pluginData[ id ].appendChild( dataField ); dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "accountId" ) ); dataField.appendChild( newList.createTextNode( strList[ idx ] ) ); dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) ); pluginData[ id ].appendChild( dataField ); dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "groups" ) ); dataField.appendChild( newList.createTextNode( strList[ idx + 3 ] ) ); } else if( id == TQString::fromLatin1( "SMSProtocol" ) && ( idx + 2 < strList.size() ) && strList[ idx + 2 ] != TQString::fromLatin1( "." ) ) { dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) ); pluginData[ id ].appendChild( dataField ); dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "serviceName" ) ); dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) ); dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) ); pluginData[ id ].appendChild( dataField ); dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "servicePrefs" ) ); dataField.appendChild( newList.createTextNode( strList[ idx + 3 ] ) ); // Add extra fields idx += 2; } // AIM, IRC, Oscar and SMS didn't store address book fields up // to now, so create one if( id != TQString::fromLatin1("ICQProtocol") && id != TQString::fromLatin1("JabberProtocol") && id != TQString::fromLatin1("WPProtocol") && id != TQString::fromLatin1("GaduProtocol") ) { TQDomElement addressBookField = newList.createElement( TQString::fromLatin1( "address-book-field" ) ); newMetaContact.appendChild( addressBookField ); addressBookField.setAttribute( TQString::fromLatin1( "app" ), TQString::fromLatin1( "messaging/" ) + addressBookLabel ); addressBookField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "All" ) ); addressBookField.appendChild( newList.createTextNode( strList[ idx ] ) ); } idx += fieldCount; } } else if( id == TQString::fromLatin1("ContactNotesPlugin") || id == TQString::fromLatin1("CryptographyPlugin") || id == TQString::fromLatin1("TranslatorPlugin") ) { TQDomElement dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) ); pluginData[ id ].appendChild( dataField ); if( id == TQString::fromLatin1("ContactNotesPlugin") ) dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "notes" ) ); else if( id == TQString::fromLatin1("CryptographyPlugin") ) dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "gpgKey" ) ); else if( id == TQString::fromLatin1("TranslatorPlugin") ) dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "languageKey" ) ); dataField.appendChild( newList.createTextNode( data ) ); } } } oldContactNode = oldContactNode.nextSibling(); } } else if( oldElement.tagName() == TQString::fromLatin1("kopete-group") ) { TQDomElement newGroup = newList.createElement( TQString::fromLatin1( "kopete-group" ) ); newRoot.appendChild( newGroup ); TQDomNode oldGroupNode = oldNode.firstChild(); while( !oldGroupNode.isNull() ) { TQDomElement oldGroupElement = oldGroupNode.toElement(); if( oldGroupElement.tagName() == TQString::fromLatin1("display-name") ) { TQDomElement displayName = newList.createElement( TQString::fromLatin1( "display-name" ) ); displayName.appendChild( newList.createTextNode( oldGroupElement.text() ) ); newGroup.appendChild( displayName ); } if( oldGroupElement.tagName() == TQString::fromLatin1("type") ) { if( oldGroupElement.text() == TQString::fromLatin1("Temporary") ) newGroup.setAttribute( TQString::fromLatin1( "type" ), TQString::fromLatin1( "temporary" ) ); else if( oldGroupElement.text() == TQString::fromLatin1( "TopLevel" ) ) newGroup.setAttribute( TQString::fromLatin1( "type" ), TQString::fromLatin1( "top-level" ) ); else newGroup.setAttribute( TQString::fromLatin1( "type" ), TQString::fromLatin1( "standard" ) ); } if( oldGroupElement.tagName() == TQString::fromLatin1("view") ) { if( oldGroupElement.text() == TQString::fromLatin1("collapsed") ) newGroup.setAttribute( TQString::fromLatin1( "view" ), TQString::fromLatin1( "collapsed" ) ); else newGroup.setAttribute( TQString::fromLatin1( "view" ), TQString::fromLatin1( "expanded" ) ); } else if( oldGroupElement.tagName() == TQString::fromLatin1("plugin-data") ) { // Per-group plugin data // FIXME: This needs updating too, ideally, convert this in a later // contactlist.xml version TQDomElement groupPluginData = newList.createElement( TQString::fromLatin1( "plugin-data" ) ); newGroup.appendChild( groupPluginData ); groupPluginData.setAttribute( TQString::fromLatin1( "plugin-id" ), oldGroupElement.attribute( TQString::fromLatin1( "plugin-id" ), TQString() ) ); groupPluginData.appendChild( newList.createTextNode( oldGroupElement.text() ) ); } oldGroupNode = oldGroupNode.nextSibling(); } } else { kdWarning( 14010 ) << k_funcinfo << "Unknown element '" << oldElement.tagName() << "' in contact list!" << endl; } } oldNode = oldNode.nextSibling(); } // Close the file, and save the new file contactListFile.close(); TQDir().rename( fileName, fileName + TQString::fromLatin1( ".bak" ) ); // kdDebug( 14010 ) << k_funcinfo << "XML output:\n" << newList.toString( 2 ) << endl; contactListFile.open( IO_WriteOnly ); TQTextStream stream( &contactListFile ); stream.setEncoding( TQTextStream::UnicodeUTF8 ); stream << newList.toString( 2 ); contactListFile.flush(); contactListFile.close(); } void Kopete::ContactList::save() { saveXML(); } void Kopete::ContactList::saveXML() { if(!d->loaded) { kdDebug(14010) << "Kopete::ContactList::saveXML: contactlist not loaded, abort saving" << endl; return; } TQString contactListFileName = locateLocal( "appdata", TQString::fromLatin1( "contactlist.xml" ) ); KSaveFile contactListFile( contactListFileName ); if( contactListFile.status() == 0 ) { TQTextStream *stream = contactListFile.textStream(); stream->setEncoding( TQTextStream::UnicodeUTF8 ); toXML().save( *stream, 4 ); if ( contactListFile.close() ) { // cancel any scheduled saves d->saveTimer->stop(); return; } else { kdDebug(14010) << "Kopete::ContactList::saveXML: failed to write contactlist, error code is: " << contactListFile.status() << endl; } } else { kdWarning(14010) << "Kopete::ContactList::saveXML: Couldn't open contact list file " << contactListFileName << ". Contact list not saved." << endl; } // if we got here, saving the contact list failed. retry every minute until it works. d->saveTimer->start( 60000, true /* single-shot: will get restarted by us next time if it's still failing */ ); } const TQDomDocument ContactList::toXML() { TQDomDocument doc; doc.appendChild( doc.createElement( TQString::fromLatin1("kopete-contact-list") ) ); doc.documentElement().setAttribute( TQString::fromLatin1("version"), TQString::fromLatin1("1.0")); // Save group information. ie: Open/Closed, pehaps later icons? Who knows. for( Kopete::Group *g = d->groups.first(); g; g = d->groups.next() ) doc.documentElement().appendChild( doc.importNode( g->toXML(), true ) ); // Save metacontact information. for( Kopete::MetaContact *m = d->contacts.first(); m; m = d->contacts.next() ) if( !m->isTemporary() ) doc.documentElement().appendChild( doc.importNode( m->toXML(), true ) ); // Save myself metacontact information if( Kopete::Config::enableGlobalIdentity() ) { TQDomElement myselfElement = myself()->toXML(true); // Save minimal information. myselfElement.setTagName( TQString::fromLatin1("myself-meta-contact") ); doc.documentElement().appendChild( doc.importNode( myselfElement, true ) ); } return doc; } TQStringList ContactList::contacts() const { TQStringList contacts; TQPtrListIterator it( d->contacts ); for( ; it.current(); ++it ) { contacts.append( it.current()->displayName() ); } return contacts; } TQStringList ContactList::contactStatuses() const { TQStringList meta_contacts; TQPtrListIterator it( d->contacts ); for( ; it.current(); ++it ) { meta_contacts.append( TQString::fromLatin1( "%1 (%2)" ). arg( it.current()->displayName(), it.current()->statusString() )); } return meta_contacts; } TQStringList ContactList::reachableContacts() const { TQStringList contacts; TQPtrListIterator it( d->contacts ); for( ; it.current(); ++it ) { if ( it.current()->isReachable() ) contacts.append( it.current()->displayName() ); } return contacts; } TQPtrList ContactList::onlineContacts() const { TQPtrList result; TQPtrListIterator it( d->contacts ); for( ; it.current(); ++it ) { if ( it.current()->isOnline() ) { TQPtrList contacts = it.current()->contacts(); TQPtrListIterator cit( contacts ); for( ; cit.current(); ++cit ) { if ( cit.current()->isOnline() ) result.append( cit.current() ); } } } return result; } TQPtrList Kopete::ContactList::onlineMetaContacts() const { TQPtrList result; TQPtrListIterator it( d->contacts ); for( ; it.current(); ++it ) { if ( it.current()->isOnline() ) result.append( it.current() ); } return result; } TQPtrList Kopete::ContactList::onlineMetaContacts( const TQString &protocolId ) const { TQPtrList result; TQPtrListIterator it( d->contacts ); for( ; it.current(); ++it ) { // FIXME: This loop is not very efficient :( if ( it.current()->isOnline() ) { TQPtrList contacts = it.current()->contacts(); TQPtrListIterator cit( contacts ); for( ; cit.current(); ++cit ) { if( cit.current()->isOnline() && cit.current()->protocol()->pluginId() == protocolId ) result.append( it.current() ); } } } return result; } TQPtrList Kopete::ContactList::onlineContacts( const TQString &protocolId ) const { TQPtrList result; TQPtrListIterator it( d->contacts ); for( ; it.current(); ++it ) { // FIXME: This loop is not very efficient :( if ( it.current()->isOnline() ) { TQPtrList contacts = it.current()->contacts(); TQPtrListIterator cit( contacts ); for( ; cit.current(); ++cit ) { if( cit.current()->isOnline() && cit.current()->protocol()->pluginId() == protocolId ) result.append( cit.current() ); } } } return result; } TQStringList Kopete::ContactList::fileTransferContacts() const { TQStringList contacts; TQPtrListIterator it( d->contacts ); for( ; it.current(); ++it ) { if ( it.current()->canAcceptFiles() ) contacts.append( it.current()->displayName() ); } return contacts; } void Kopete::ContactList::sendFile( const TQString &displayName, const KURL &sourceURL, const TQString &altFileName, const long unsigned int fileSize) { // kdDebug(14010) << "Send To Display Name: " << displayName << "\n"; Kopete::MetaContact *c = findMetaContactByDisplayName( displayName ); if( c ) c->sendFile( sourceURL, altFileName, fileSize ); } void Kopete::ContactList::messageContact( const TQString &contactId, const TQString &messageText ) { Kopete::MetaContact *mc = findMetaContactByContactId( contactId ); if (!mc) return; Kopete::Contact *c = mc->execute(); //We need to know which contact was chosen as the preferred in order to message it if (!c) return; Kopete::Message msg(c->account()->myself(), c, messageText, Kopete::Message::Outbound); c->manager(Contact::CanCreate)->sendMessage(msg); } TQStringList Kopete::ContactList::contactFileProtocols(const TQString &displayName) { // kdDebug(14010) << "Get contacts for: " << displayName << "\n"; TQStringList protocols; Kopete::MetaContact *c = findMetaContactByDisplayName( displayName ); if( c ) { TQPtrList mContacts = c->contacts(); kdDebug(14010) << mContacts.count() << endl; TQPtrListIterator jt( mContacts ); for ( ; jt.current(); ++jt ) { kdDebug(14010) << "1" << jt.current()->protocol()->pluginId() << endl; if( jt.current()->canAcceptFiles() ) { kdDebug(14010) << jt.current()->protocol()->pluginId() << endl; protocols.append ( jt.current()->protocol()->pluginId() ); } } return protocols; } return TQStringList(); } void ContactList::slotSaveLater() { // if we already have a save scheduled, it will be cancelled. either way, // start a timer to save the contact list a bit later. d->saveTimer->start( 17100 /* 17,1 seconds */, true /* single-shot */ ); } void ContactList::slotKABCChanged() { // TODO: react to changes in KABC, replacing this function, post 3.4 (Will) // call syncWithKABC on each metacontact to check if its associated tdeabc entry has changed. /* for ( MetaContact * mc = d->contacts.first(); mc; mc = d->contacts.next() ) mc->syncWithKABC();*/ } } //END namespace Kopete #include "kopetecontactlist.moc" // vim: set noet ts=4 sts=4 sw=4: