You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/libkopete/kopetecontactlist.cpp

1113 lines
38 KiB

/*
kopetecontactlist.cpp - Kopete's Contact List backend
Copyright (c) 2005 by Michael Larouche <michael.larouche@kdemail.net>
Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
Copyright (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
*************************************************************************
* *
* 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 <tqdir.h>
#include <tqregexp.h>
#include <tqtimer.h>
#include <tdeapplication.h>
#include <kabc/stdaddressbook.h>
#include <kdebug.h>
#include <ksavefile.h>
#include <kstandarddirs.h>
#include <kopeteconfig.h>
#include <kglobal.h>
#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<MetaContact> contacts;
TQPtrList<Group> groups;
TQPtrList<MetaContact> selectedMetaContacts;
TQPtrList<Group> 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<MetaContact> ContactList::metaContacts() const
{
return d->contacts;
}
TQPtrList<Group> ContactList::groups() const
{
return d->groups;
}
MetaContact *ContactList::metaContact( const TQString &metaContactId ) const
{
TQPtrListIterator<MetaContact> 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<MetaContact> 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<Account> acts=AccountManager::self()->accounts();
TQPtrListIterator<Account> 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<MetaContact> ContactList::selectedMetaContacts() const
{
return d->selectedMetaContacts;
}
TQPtrList<Group> 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<Contact> 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<MetaContact> metaContacts , TQPtrList<Group> 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
KABC::Addressee a = KABC::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;
/* The mutex is usefull to don't have such as stack overflow
Kopete::ContactList::slotPhotoChanged -> Kopete::ContactList::globalIdentityChanged
MSNAccount::slotGlobalIdentityChanged -> Kopete::Contact::propertyChanged
Kopete::MetaContact::slotPropertyChanged -> Kopete::MetaContact::photoChanged -> Kopete::ContactList::slotPhotoChanged
*/
}
///////////////////////////////////////////////////////////////////////////////////////////////
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<TQString, TQDomElement> 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". MSN, 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("MSNProtocol") )
{
fieldCount = 3;
addressBookLabel = TQString::fromLatin1("msn");
}
else 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("ICTQProtocol") || 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( "MSNProtocol" ) || id == TQString::fromLatin1( "OscarProtocol" ) ||
id == TQString::fromLatin1( "AIMProtocol" ) || id == TQString::fromLatin1( "IRCProtocol" ) ||
id == TQString::fromLatin1( "ICTQProtocol" ) || 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("ICTQProtocol") )
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("ICTQProtocol") || 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("MSNProtocol") )
{
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 + 2 ] ) );
}
else 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;
}
// MSN, AIM, IRC, Oscar and SMS didn't store address book fields up
// to now, so create one
if( id != TQString::fromLatin1("ICTQProtocol") && 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<Kopete::MetaContact> it( d->contacts );
for( ; it.current(); ++it )
{
contacts.append( it.current()->displayName() );
}
return contacts;
}
TQStringList ContactList::contactStatuses() const
{
TQStringList meta_contacts;
TQPtrListIterator<Kopete::MetaContact> 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<Kopete::MetaContact> it( d->contacts );
for( ; it.current(); ++it )
{
if ( it.current()->isReachable() )
contacts.append( it.current()->displayName() );
}
return contacts;
}
TQPtrList<Contact> ContactList::onlineContacts() const
{
TQPtrList<Kopete::Contact> result;
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
for( ; it.current(); ++it )
{
if ( it.current()->isOnline() )
{
TQPtrList<Kopete::Contact> contacts = it.current()->contacts();
TQPtrListIterator<Kopete::Contact> cit( contacts );
for( ; cit.current(); ++cit )
{
if ( cit.current()->isOnline() )
result.append( cit.current() );
}
}
}
return result;
}
TQPtrList<Kopete::MetaContact> Kopete::ContactList::onlineMetaContacts() const
{
TQPtrList<Kopete::MetaContact> result;
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
for( ; it.current(); ++it )
{
if ( it.current()->isOnline() )
result.append( it.current() );
}
return result;
}
TQPtrList<Kopete::MetaContact> Kopete::ContactList::onlineMetaContacts( const TQString &protocolId ) const
{
TQPtrList<Kopete::MetaContact> result;
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
for( ; it.current(); ++it )
{
// FIXME: This loop is not very efficient :(
if ( it.current()->isOnline() )
{
TQPtrList<Kopete::Contact> contacts = it.current()->contacts();
TQPtrListIterator<Kopete::Contact> cit( contacts );
for( ; cit.current(); ++cit )
{
if( cit.current()->isOnline() && cit.current()->protocol()->pluginId() == protocolId )
result.append( it.current() );
}
}
}
return result;
}
TQPtrList<Kopete::Contact> Kopete::ContactList::onlineContacts( const TQString &protocolId ) const
{
TQPtrList<Kopete::Contact> result;
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
for( ; it.current(); ++it )
{
// FIXME: This loop is not very efficient :(
if ( it.current()->isOnline() )
{
TQPtrList<Kopete::Contact> contacts = it.current()->contacts();
TQPtrListIterator<Kopete::Contact> 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<Kopete::MetaContact> 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<Kopete::Contact> mContacts = c->contacts();
kdDebug(14010) << mContacts.count() << endl;
TQPtrListIterator<Kopete::Contact> 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 kabc 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: