diff options
Diffstat (limited to 'kopete/kopete/contactlist')
25 files changed, 7203 insertions, 0 deletions
diff --git a/kopete/kopete/contactlist/Makefile.am b/kopete/kopete/contactlist/Makefile.am new file mode 100644 index 00000000..d9544d5d --- /dev/null +++ b/kopete/kopete/contactlist/Makefile.am @@ -0,0 +1,28 @@ +METASOURCES = AUTO + +AM_CPPFLAGS = $(KOPETE_INCLUDES) \ + -I$(top_srcdir)/kopete/libkopete/private \ + -I$(top_srcdir)/kopete/libkopete/ui \ + -I$(top_srcdir)/kopete/kopete \ + -I$(top_srcdir)/kopete/kopete/chatwindow \ + -I$(top_srcdir)/kopete/kopete/addcontactwizard \ + -I$(top_builddir)/kopete/kopete/addcontactwizard \ + $(all_includes) + +noinst_LTLIBRARIES = libkopetecontactlist.la + +libkopetecontactlist_la_SOURCES = kopetemetacontactlvi.cpp \ + kopetestatusgroupviewitem.cpp kopetegroupviewitem.cpp kopetecontactlistview.cpp \ + kopetegvipropswidget.ui kopetemetalvipropswidget.ui kopetelviprops.cpp \ + kopeteaddrbookexport.cpp kopeteaddrbookexportui.ui customnotifications.ui \ + customnotificationprops.cpp kopetegrouplistaction.cpp kabcexport.cpp \ + kabcexport_base.ui + +libkopetecontactlist_la_LDFLAGS = $(all_libraries) +libkopetecontactlist_la_LIBADD = -lkabc ../../libkopete/libkopete.la ../addcontactwizard/libkopeteaddcontactwizard.la $(LIB_KDEUI) $(LIB_XRENDER) + +noinst_HEADERS = kopeteaddrbookexport.h customnotificationprops.h kabcexport.h + +KDE_OPTIONS = nofinal + +# vim: set noet: diff --git a/kopete/kopete/contactlist/configure.in.in b/kopete/kopete/contactlist/configure.in.in new file mode 100644 index 00000000..900224ea --- /dev/null +++ b/kopete/kopete/contactlist/configure.in.in @@ -0,0 +1,15 @@ + +dnl ----------------------------------------------------- +dnl XRender check - stolen from kdelibs/kdefx +dnl ----------------------------------------------------- +LIB_XRENDER= +if test "$kde_use_qt_emb" = "no" && test "$kde_use_qt_mac" = "no"; then + KDE_CHECK_HEADER(X11/extensions/Xrender.h, [xrender_h=yes], [xrender_h=no]) + if test "$xrender_h" = yes; then + KDE_CHECK_LIB(Xrender, XRenderComposite, [ + LIB_XRENDER=-lXrender + AC_DEFINE_UNQUOTED(HAVE_XRENDER, 1, [Defined if your system has XRender support]) + ], [], -lXext -lX11 $X_EXTRA_LIBS) + fi +fi +AC_SUBST(LIB_XRENDER) diff --git a/kopete/kopete/contactlist/customnotificationprops.cpp b/kopete/kopete/contactlist/customnotificationprops.cpp new file mode 100644 index 00000000..87833fa7 --- /dev/null +++ b/kopete/kopete/contactlist/customnotificationprops.cpp @@ -0,0 +1,168 @@ +/* + customnotificationprops.cpp + + Kopete Contactlist Custom Notifications GUI for Groups and MetaContacts + + Contains UI controller logic for managing custom notifications + + Copyright (c) 2004 Will Stephenson <lists@stevello.free-online.co.uk> + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qlineedit.h> + +#include <kdebug.h> +#include <kconfig.h> +#include <kurlrequester.h> + +#include "customnotifications.h" +#include "kopeteeventpresentation.h" +#include "kopetenotifyevent.h" +#include "kopetenotifydataobject.h" + +#include "customnotificationprops.h" + +CustomNotificationProps::CustomNotificationProps( QWidget *parent, Kopete::NotifyDataObject* item, const char * name ) +: QObject( parent, name ) +{ + m_notifyWidget = new CustomNotificationWidget( parent, "notificationWidget" ); + + m_item = item; + QString path = "kopete/eventsrc"; + KConfig eventsfile( path, true, false, "data" ); + m_eventList = eventsfile.groupList(); + QStringList contactSpecificEvents; // we are only interested in events that relate to contacts + QStringList::Iterator it = m_eventList.begin(); + QStringList::Iterator end = m_eventList.end(); + for ( ; it != end; ++it ) + { + if ( !(*it).startsWith( QString::fromLatin1( "kopete_contact_" ) ) ) + continue; + contactSpecificEvents.append( *it ); + QMap<QString, QString> entries = eventsfile.entryMap( *it ); + eventsfile.setGroup( *it ); + QString comment = eventsfile.readEntry( "Comment", QString::fromLatin1( "Found nothing!" ) ); + m_notifyWidget->cmbEvents->insertItem( comment ); + } + m_eventList = contactSpecificEvents; + slotEventsComboChanged( m_notifyWidget->cmbEvents->currentItem() ); + // we have to do this after adding items + connect( m_notifyWidget->cmbEvents, SIGNAL( activated( int ) ), this, SLOT( slotEventsComboChanged( int ) ) ); +} + +void CustomNotificationProps::slotEventsComboChanged( int itemNo ) +{ + // if the combo has changed, store the previous state of the widgets + // record the selected item so we can save it when the widget changes next + storeCurrentCustoms(); + m_event = m_eventList[ itemNo ]; + // update the widgets for the selected item + // get the corresponding Kopete::NotifyEvent + Kopete::NotifyEvent *evt = m_item->notifyEvent( m_event ); + // set the widgets accordingly + resetEventWidgets(); + if ( evt ) + { + // sound presentation + Kopete::EventPresentation *pres = evt->presentation( Kopete::EventPresentation::Sound ); + if ( pres ) + { + m_notifyWidget->chkCustomSound->setChecked( pres->enabled() ); + m_notifyWidget->customSound->setURL( pres->content() ); + m_notifyWidget->chkSoundSS->setChecked( pres->singleShot() ); + } + // message presentation + pres = evt->presentation( Kopete::EventPresentation::Message ); + if ( pres ) + { + m_notifyWidget->chkCustomMsg->setChecked( pres->enabled() ); + m_notifyWidget->customMsg->setText( pres->content() ); + m_notifyWidget->chkMsgSS->setChecked( pres->singleShot() ); + } + // chat presentation + pres = evt->presentation( Kopete::EventPresentation::Chat ); + if ( pres ) + { + m_notifyWidget->chkCustomChat->setChecked( pres->enabled() ); + m_notifyWidget->chkChatSS->setChecked( pres->singleShot() ); + } + m_notifyWidget->chkSuppressCommon->setChecked( evt->suppressCommon() ); + } + //dumpData(); +} + + +void CustomNotificationProps::dumpData() +{ + Kopete::NotifyEvent *evt = m_item->notifyEvent( m_event ); + if ( evt ) + kdDebug( 14000 ) << k_funcinfo << evt->toString() << endl; + else + kdDebug( 14000 ) << k_funcinfo << " no event exists." << endl; +} + +void CustomNotificationProps::resetEventWidgets() +{ + m_notifyWidget->chkCustomSound->setChecked( false ); + m_notifyWidget->customSound->clear(); + m_notifyWidget->chkSoundSS->setChecked( true ); + m_notifyWidget->chkCustomMsg->setChecked( false ); + m_notifyWidget->customMsg->clear(); + m_notifyWidget->chkMsgSS->setChecked( true ); + m_notifyWidget->chkCustomChat->setChecked( false ); + m_notifyWidget->chkChatSS->setChecked( true ); + m_notifyWidget->chkSuppressCommon->setChecked( false ); +} + +void CustomNotificationProps::storeCurrentCustoms() +{ + if ( !m_event.isNull() ) + { + Kopete::NotifyEvent *evt = m_item->notifyEvent( m_event ); + if ( !evt ) + { + evt = new Kopete::NotifyEvent( ); + // store the changed event + m_item->setNotifyEvent( m_event, evt ); + } + evt->setSuppressCommon( m_notifyWidget->chkSuppressCommon->isChecked() ); + // set different presentations + Kopete::EventPresentation *eventNotify = 0; + eventNotify = new Kopete::EventPresentation( Kopete::EventPresentation::Sound, + m_notifyWidget->customSound->url(), + m_notifyWidget->chkSoundSS->isChecked(), + m_notifyWidget->chkCustomSound->isChecked() ); + evt->setPresentation( Kopete::EventPresentation::Sound, eventNotify ); + // set message attributes + eventNotify = new Kopete::EventPresentation( Kopete::EventPresentation::Message, + m_notifyWidget->customMsg->text(), + m_notifyWidget->chkMsgSS->isChecked(), + m_notifyWidget->chkCustomMsg->isChecked() ); + evt->setPresentation( Kopete::EventPresentation::Message, eventNotify ); + // set chat attributes + eventNotify = new Kopete::EventPresentation( Kopete::EventPresentation::Chat, + QString::null, + m_notifyWidget->chkChatSS->isChecked(), + m_notifyWidget->chkCustomChat->isChecked() ); + evt->setPresentation( Kopete::EventPresentation::Chat, eventNotify ); + evt->setSuppressCommon( m_notifyWidget->chkSuppressCommon->isChecked() ); + } +} + +CustomNotificationWidget* CustomNotificationProps::widget() +{ + return m_notifyWidget; +} + +#include "customnotificationprops.moc" diff --git a/kopete/kopete/contactlist/customnotificationprops.h b/kopete/kopete/contactlist/customnotificationprops.h new file mode 100644 index 00000000..5d6c1dea --- /dev/null +++ b/kopete/kopete/contactlist/customnotificationprops.h @@ -0,0 +1,51 @@ +/* + customnotificationprops.h + + Kopete Contactlist Custom Notifications GUI for Groups and MetaContacts + + Copyright (c) 2004 Will Stephenson <lists@stevello.free-online.co.uk> + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KOPETE_CUSTOM_NOTIFICATION_PROPS_H +#define KOPETE_CUSTOM_NOTIFICATION_PROPS_H + +class CustomNotificationWidget; +class QBoxLayout; + +namespace Kopete +{ +class NotifyDataObject; +} + +class CustomNotificationProps : public QObject +{ + Q_OBJECT +public: + CustomNotificationProps( QWidget *parent, Kopete::NotifyDataObject* item, const char * name = 0 ); + ~CustomNotificationProps() {} + void dumpData(); + void resetEventWidgets(); + void storeCurrentCustoms(); + CustomNotificationWidget* widget(); + +protected slots: + void slotEventsComboChanged( int itemNo ); + +private: + CustomNotificationWidget* m_notifyWidget; + Kopete::NotifyDataObject * m_item; + QStringList m_eventList; + QString m_event; +}; + +#endif diff --git a/kopete/kopete/contactlist/customnotifications.ui b/kopete/kopete/contactlist/customnotifications.ui new file mode 100644 index 00000000..86224af2 --- /dev/null +++ b/kopete/kopete/contactlist/customnotifications.ui @@ -0,0 +1,238 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>CustomNotificationWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>CustomNotificationWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>389</width> + <height>220</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout13</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>On &event:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>cmbEvents</cstring> + </property> + </widget> + <widget class="QComboBox"> + <property name="name"> + <cstring>cmbEvents</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Choose the event that should have a custom notification</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout12</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KURLRequester" row="0" column="1"> + <property name="name"> + <cstring>customSound</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Select the sound to play</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>chkCustomSound</cstring> + </property> + <property name="text"> + <string>&Play a sound:</string> + </property> + <property name="toolTip" stdset="0"> + <string>Play a sound when this event occurs for this contact</string> + </property> + </widget> + <spacer row="2" column="1"> + <property name="name"> + <cstring>spacer10</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>140</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>chkCustomChat</cstring> + </property> + <property name="text"> + <string>Start a cha&t</string> + </property> + <property name="toolTip" stdset="0"> + <string>Open a chat window with this contact when this event occurs for this contact</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>chkCustomMsg</cstring> + </property> + <property name="text"> + <string>&Display a message:</string> + </property> + <property name="toolTip" stdset="0"> + <string>Display a message on your screen when this event occurs for this contact</string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>customMsg</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Enter the message to display</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="2"> + <property name="name"> + <cstring>chkMsgSS</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>D&isplay once</string> + </property> + <property name="toolTip" stdset="0"> + <string>Only display a message the next time the event occurs</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="2"> + <property name="name"> + <cstring>chkSoundSS</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>P&lay once</string> + </property> + <property name="toolTip" stdset="0"> + <string>Only play a sound the next time the event occurs</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="2"> + <property name="name"> + <cstring>chkChatSS</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>T&rigger once</string> + </property> + <property name="toolTip" stdset="0"> + <string>Only start a chat the next time the event occurs</string> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>chkSuppressCommon</cstring> + </property> + <property name="text"> + <string>S&uppress standard notifications</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Check to prevent notifications common to all contacts from happening for this contact</string> + </property> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>chkCustomSound</sender> + <signal>toggled(bool)</signal> + <receiver>customSound</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>chkCustomSound</sender> + <signal>toggled(bool)</signal> + <receiver>chkSoundSS</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>chkCustomMsg</sender> + <signal>toggled(bool)</signal> + <receiver>customMsg</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>chkCustomMsg</sender> + <signal>toggled(bool)</signal> + <receiver>chkMsgSS</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>chkCustomChat</sender> + <signal>toggled(bool)</signal> + <receiver>chkChatSS</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>cmbEvents</tabstop> + <tabstop>chkCustomSound</tabstop> + <tabstop>customSound</tabstop> + <tabstop>chkSoundSS</tabstop> + <tabstop>chkCustomMsg</tabstop> + <tabstop>customMsg</tabstop> + <tabstop>chkMsgSS</tabstop> + <tabstop>chkCustomChat</tabstop> + <tabstop>chkChatSS</tabstop> + <tabstop>chkSuppressCommon</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/kopete/kopete/contactlist/kabcexport.cpp b/kopete/kopete/contactlist/kabcexport.cpp new file mode 100644 index 00000000..73f67344 --- /dev/null +++ b/kopete/kopete/contactlist/kabcexport.cpp @@ -0,0 +1,249 @@ +/* + kabcexport.cpp - Export Contacts to Address Book Wizard for Kopete + + Copyright (c) 2005 by Will Stephenson <will@stevello.free-online.co.uk> + Resource selector taken from KRES::SelectDialog + Copyright (c) 2002 Tobias Koenig <tokoe@kde.org> + Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org> + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qpushbutton.h> +#include <qlistbox.h> +#include <qlistview.h> +#include <qptrlist.h> +#include <qmap.h> + +#include <klocale.h> +#include <kmessagebox.h> +#include <kabc/addressee.h> +#include <kabc/addressbook.h> +#include <kabc/phonenumber.h> +#include <kabc/picture.h> +#include <kabc/resource.h> +#include <kabc/stdaddressbook.h> + +#include <kabcpersistence.h> +#include <kopetecontact.h> +#include <kopetecontactlist.h> +#include <kopetecontactproperty.h> +#include <kopeteglobal.h> +#include <kopetemetacontact.h> + +#include "kabcpersistence.h" + +#include "kabcexport.h" + +class ContactLVI : public QCheckListItem +{ + public: + ContactLVI ( Kopete::MetaContact * mc, QListView * parent, const QString & text, Type tt = RadioButtonController ) : QCheckListItem( parent, text, tt ), mc( mc ) + { } + Kopete::MetaContact * mc; + QString uid; +}; + +// ctor populates the resource list and contact list, and enables the next button on the first page +KabcExportWizard::KabcExportWizard( QWidget *parent, const char *name ) + : KabcExportWizard_Base( parent, name ) +{ + connect( m_addrBooks, SIGNAL( selectionChanged( QListBoxItem * ) ), SLOT( slotResourceSelectionChanged( QListBoxItem * ) ) ); + + connect( m_btnSelectAll, SIGNAL( clicked() ), SLOT( slotSelectAll() ) ); + connect( m_btnDeselectAll, SIGNAL( clicked() ), SLOT( slotDeselectAll() ) ); + + // fill resource selector + m_addressBook = Kopete::KABCPersistence::self()->addressBook(); + + QPtrList<KABC::Resource> kabcResources = m_addressBook->resources(); + + QPtrListIterator<KABC::Resource> resIt( kabcResources ); + KABC::Resource *resource; + + uint counter = 0; + while ( ( resource = resIt.current() ) != 0 ) + { + ++resIt; + if ( !resource->readOnly() ) + { + m_resourceMap.insert( counter, resource ); + m_addrBooks->insertItem( resource->resourceName() ); + counter++; + } + } + setNextEnabled( QWizard::page( 0 ), false ); + setFinishEnabled( QWizard::page( 1 ), true ); + // if there were no writable address books, tell the user + if ( counter == 0 ) + { + m_addrBooks->insertItem( i18n( "No writeable addressbook resource found." ) ); + m_addrBooks->insertItem( i18n( "Add or enable one using the KDE Control Center." ) ); + m_addrBooks->setEnabled( false ); + } + + if ( m_addrBooks->count() == 1 ) + m_addrBooks->setSelected( 0, true ); + + // fill contact list + QPtrList<Kopete::MetaContact> contacts = Kopete::ContactList::self()->metaContacts(); + QPtrListIterator<Kopete::MetaContact> it( contacts ); + counter = 0; + QString alreadyIn = i18n( " (already in address book)" ); + for (; it.current(); ++it) + { + m_contactMap.insert( counter, it.current() ); + QCheckListItem * lvi = new ContactLVI( it.current(), m_contactList, + it.current()->displayName(), QCheckListItem::CheckBox ); + lvi->setOn( false ); + if ( it.current()->metaContactId().contains(':') ) + { + lvi->setOn( true ); + lvi->setEnabled( true ); + } + else + lvi->setText( 0, lvi->text( 0 ) + alreadyIn ); + } +} + +KabcExportWizard::~KabcExportWizard() +{ + +} + +void KabcExportWizard::slotDeselectAll() +{ + QListViewItemIterator it( m_contactList ); + while ( it.current() ) + { + ContactLVI *item = static_cast<ContactLVI *>( it.current() ); + item->setOn( false ); + ++it; + } +} + +void KabcExportWizard::slotSelectAll() +{ + QListViewItemIterator it( m_contactList ); + while ( it.current() ) + { + ContactLVI *item = static_cast<ContactLVI *>( it.current() ); + ++it; + if ( !item->isEnabled() ) + continue; + item->setOn( true ); + } +} + +void KabcExportWizard::slotResourceSelectionChanged( QListBoxItem * lbi ) +{ + setNextEnabled( QWizard::page( 0 ), lbi->isSelected() ); +} + +// accept runs the export algorithm +void KabcExportWizard::accept() +{ + // first add an addressee to the selected resource + // then set the metacontactId of each MC to that of the new addressee + KABC::Resource * selectedResource = + m_resourceMap[ ( m_addrBooks->index( m_addrBooks->selectedItem() ) ) ]; + // for each item checked + { + QListViewItemIterator it( m_contactList ); + while ( it.current() ) + { + ContactLVI *item = static_cast<ContactLVI *>( it.current() ); + // if it is checked and enabled + if ( item->isEnabled() && item->isOn() ) + { + KABC::Addressee addr; + addr = m_addressBook->findByUid( item->mc->metaContactId() ); + if ( addr.isEmpty() ) // unassociated contact + { + kdDebug( 14000 ) << "creating addressee " << item->mc->displayName() << " in address book " << selectedResource->resourceName() << endl; + // create a new addressee in the selected resource + addr.setResource( selectedResource ); + + // set name + QPtrList<Kopete::Contact> contacts = item->mc->contacts(); + if ( contacts.count() == 1 ) + { + Kopete::ContactProperty prop; + prop = contacts.first()->property( + Kopete::Global::Properties::self()->fullName() ); + if ( prop.isNull() ) + addr.setNameFromString( item->mc->displayName() ); + else + addr.setNameFromString( prop.value().toString() ); + } + else + addr.setNameFromString( item->mc->displayName() ); + + // set details + exportDetails( item->mc, addr ); + m_addressBook->insertAddressee( addr ); + // set the metacontact's id to that of the new addressee + // - this causes the addressbook to be written by libkopete + item->mc->setMetaContactId( addr.uid() ); + } + else + { + exportDetails( item->mc, addr ); + m_addressBook->insertAddressee( addr ); + } + } + ++it; + } + } + // request a write in case we only changed details on existing linked addressee + Kopete::KABCPersistence::self()->writeAddressBook( selectedResource ); + QDialog::accept(); +} + +void KabcExportWizard::exportDetails( Kopete::MetaContact * mc, KABC::Addressee & addr ) +{ + // for each contact + QPtrList<Kopete::Contact> contacts = mc->contacts(); + QPtrListIterator<Kopete::Contact> cit( contacts ); + for( ; cit.current(); ++cit ) + { + Kopete::ContactProperty prop; + prop = (*cit)->property( Kopete::Global::Properties::self()->emailAddress() ); + if ( !prop.isNull() ) + { + addr.insertEmail( prop.value().toString() ); + } + prop = (*cit)->property( Kopete::Global::Properties::self()->privatePhone() ); + if ( !prop.isNull() ) + { + addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Home ) ); + } + prop = (*cit)->property( Kopete::Global::Properties::self()->workPhone() ); + if ( !prop.isNull() ) + { + addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Work ) ); + } + prop = (*cit)->property( Kopete::Global::Properties::self()->privateMobilePhone() ); + if ( !prop.isNull() ) + { + addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Cell ) ); + } + + } + // metacontact photo + QImage photo = mc->photo(); + if ( !photo.isNull() ) + addr.setPhoto( KABC::Picture( photo ) ); +} + +#include "kabcexport.moc" diff --git a/kopete/kopete/contactlist/kabcexport.h b/kopete/kopete/contactlist/kabcexport.h new file mode 100644 index 00000000..bad1d8e6 --- /dev/null +++ b/kopete/kopete/contactlist/kabcexport.h @@ -0,0 +1,56 @@ +/* + kabcexport.h - Export Contacts to Address Book Wizard for Kopete + + Copyright (c) 2005 by Will Stephenson <will@stevello.free-online.co.uk> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KABCEXPORTWIZARD_H +#define KABCEXPORTWIZARD_H + +#include "kabcexport_base.h" + +namespace KABC { + class AddressBook; + class Addressee; +} + +namespace Kopete { + class MetaContact; +} + +namespace KRES { + class Resource; +} + +class KabcExportWizard : public KabcExportWizard_Base +{ +Q_OBJECT + public: + KabcExportWizard( QWidget *parent = 0, const char *name = 0 ); + ~KabcExportWizard(); + public slots: + void accept(); + protected slots: + void slotDeselectAll(); + void slotSelectAll(); + void slotResourceSelectionChanged( QListBoxItem * lbi ); + protected: + void exportDetails( Kopete::MetaContact * mc, KABC::Addressee & addr ); + private: + KABC::AddressBook* m_addressBook; + QMap<int, KABC::Resource*> m_resourceMap; + QMap<int, Kopete::MetaContact*> m_contactMap; +}; + +#endif diff --git a/kopete/kopete/contactlist/kabcexport_base.ui b/kopete/kopete/contactlist/kabcexport_base.ui new file mode 100644 index 00000000..80ace5c6 --- /dev/null +++ b/kopete/kopete/contactlist/kabcexport_base.ui @@ -0,0 +1,183 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KabcExportWizard_Base</class> +<widget class="QWizard"> + <property name="name"> + <cstring>KabcExportWizard_Base</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>423</width> + <height>398</height> + </rect> + </property> + <property name="caption"> + <string>Export Contacts</string> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>page</cstring> + </property> + <attribute name="title"> + <string>Export Contacts to Address Book</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>This wizard helps you export instant messaging contacts to the KDE address book.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>&Select Address Book</string> + </property> + <widget class="QListBox"> + <property name="name"> + <cstring>m_addrBooks</cstring> + </property> + <property name="geometry"> + <rect> + <x>11</x> + <y>31</y> + <width>230</width> + <height>140</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </widget> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WizardPage</cstring> + </property> + <attribute name="title"> + <string>Select Contacts to Export</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Selected contacts will be added to the KDE address book.</string> + </property> + </widget> + <widget class="QListView"> + <column> + <property name="text"> + <string>Contact</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_contactList</cstring> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>AllColumns</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_btnSelectAll</cstring> + </property> + <property name="text"> + <string>Select &All</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_btnDeselectAll</cstring> + </property> + <property name="text"> + <string>&Deselect All</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer11</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>51</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> + </widget> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/kopete/contactlist/kopeteaddrbookexport.cpp b/kopete/kopete/contactlist/kopeteaddrbookexport.cpp new file mode 100644 index 00000000..d752f71e --- /dev/null +++ b/kopete/kopete/contactlist/kopeteaddrbookexport.cpp @@ -0,0 +1,298 @@ +/* + kopeteaddrbookexport.cpp - Kopete Online Status + + Logic for exporting data acquired from messaging systems to the + KDE address book + + Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <kabc/phonenumber.h> +#include <qcombobox.h> +#include <qlabel.h> + +#include <kdialogbase.h> +#include <kiconloader.h> +#include <klistbox.h> +#include <klocale.h> + +#include "kopeteaccount.h" +#include "kopeteglobal.h" +#include "kopetemetacontact.h" +#include "kopetecontact.h" + +#include "kopeteaddrbookexport.h" +#include "kopeteaddrbookexportui.h" + +KopeteAddressBookExport::KopeteAddressBookExport( QWidget *parent, Kopete::MetaContact *mc ) : QObject( parent ) +{ + // instantiate dialog and populate widgets + mParent = parent; + mAddressBook = KABC::StdAddressBook::self(); + mMetaContact = mc; +} + +KopeteAddressBookExport::~KopeteAddressBookExport() +{ + +} + +void KopeteAddressBookExport::initLabels() +{ + if ( !mAddressee.isEmpty() ) + { + mUI->mLblFirstName->setText( mAddressee.givenNameLabel() ); + mUI->mLblLastName->setText( mAddressee.familyNameLabel() ); + mUI->mLblEmail->setText( mAddressee.emailLabel() ); + mUI->mLblUrl->setText( mAddressee.urlLabel() ); + mUI->mLblHomePhone->setText( mAddressee.homePhoneLabel() ); + mUI->mLblWorkPhone->setText( mAddressee.businessPhoneLabel() ); + mUI->mLblMobilePhone->setText( mAddressee.mobilePhoneLabel() ); + } +} + +void KopeteAddressBookExport::fetchKABCData() +{ + if ( !mAddressee.isEmpty() ) + { + mAddrBookIcon = SmallIcon( "kaddressbook" ); + + // given name + QString given = mAddressee.givenName(); + if ( !given.isEmpty() ) + mUI->mFirstName->insertItem( mAddrBookIcon, given ); + else + mUI->mFirstName->insertItem( mAddrBookIcon, i18n("<Not Set>") ); + + // family name + QString family = mAddressee.familyName(); + if ( !family.isEmpty() ) + mUI->mLastName->insertItem( mAddrBookIcon, family ); + else + mUI->mLastName->insertItem( mAddrBookIcon, i18n("<Not Set>") ); + + // url + QString url = mAddressee.url().url(); + if ( !url.isEmpty() ) + mUI->mUrl->insertItem( mAddrBookIcon, url ); + else + mUI->mUrl->insertItem( mAddrBookIcon, i18n("<Not Set>") ); + + // emails + QStringList emails = mAddressee.emails(); + numEmails = emails.count(); + for ( QStringList::Iterator it = emails.begin(); it != emails.end(); ++it ) + mUI->mEmails->insertItem( mAddrBookIcon, *it ); + if ( numEmails == 0 ) + { + mUI->mEmails->insertItem( mAddrBookIcon, i18n("<Not Set>") ); + numEmails = 1; + } + + // phone numbers + fetchPhoneNumbers( mUI->mHomePhones, KABC::PhoneNumber::Home, numHomePhones ); + fetchPhoneNumbers( mUI->mWorkPhones, KABC::PhoneNumber::Work, numWorkPhones ); + fetchPhoneNumbers( mUI->mMobilePhones, KABC::PhoneNumber::Cell, numMobilePhones ); + } +} + +void KopeteAddressBookExport::fetchPhoneNumbers( KListBox * listBox, int type, uint& counter ) +{ + KABC::PhoneNumber::List phones = mAddressee.phoneNumbers( type ); + counter = phones.count(); + KABC::PhoneNumber::List::Iterator it; + for ( it = phones.begin(); it != phones.end(); ++it ) + listBox->insertItem( mAddrBookIcon, (*it).number() ); + if ( counter == 0 ) + { + listBox->insertItem( mAddrBookIcon, i18n("<Not Set>") ); + counter = 1; + } +} + +void KopeteAddressBookExport::fetchIMData() +{ + QPtrList<Kopete::Contact> contacts = mMetaContact->contacts(); + QPtrListIterator<Kopete::Contact> cit( contacts ); + for( ; cit.current(); ++cit ) + { + // for each contact, get the property content + Kopete::Contact* c = cit.current(); + QPixmap contactIcon = c->account()->accountIcon( 16 ); + // given name + populateIM( c, contactIcon, mUI->mFirstName, Kopete::Global::Properties::self()->firstName() ); + // family name + populateIM( c, contactIcon, mUI->mLastName, Kopete::Global::Properties::self()->lastName() ); + // url + // TODO: make URL/homepage a global template, currently only in IRC channel contact + // emails + populateIM( c, contactIcon, mUI->mEmails, Kopete::Global::Properties::self()->emailAddress() ); + // home phone + populateIM( c, contactIcon, mUI->mHomePhones, Kopete::Global::Properties::self()->privatePhone() ); + // work phone + populateIM( c, contactIcon, mUI->mWorkPhones, Kopete::Global::Properties::self()->workPhone() ); + // mobile phone + populateIM( c, contactIcon, mUI->mMobilePhones, Kopete::Global::Properties::self()->privateMobilePhone() ); + } +} + +void KopeteAddressBookExport::populateIM( const Kopete::Contact *contact, const QPixmap &icon, QComboBox *combo, const Kopete::ContactPropertyTmpl &property ) +{ + Kopete::ContactProperty prop = contact->property( property ); + if ( !prop.isNull() ) + { + combo->insertItem( icon, prop.value().toString() ); + } +} + +void KopeteAddressBookExport::populateIM( const Kopete::Contact *contact, const QPixmap &icon, KListBox *listBox, const Kopete::ContactPropertyTmpl &property ) +{ + Kopete::ContactProperty prop = contact->property( property ); + if ( !prop.isNull() ) + { + listBox->insertItem( icon, prop.value().toString() ); + } +} + +int KopeteAddressBookExport::showDialog() +{ + mAddressee = mAddressBook->findByUid( mMetaContact->metaContactId() ); + if ( !mAddressee.isEmpty() ) + { + numEmails = 0; + numHomePhones = 0; + numWorkPhones = 0; + numMobilePhones = 0; + mDialog = new KDialogBase( mParent, "addressbookexportdialog", true, i18n("Export to Address Book"), KDialogBase::Ok|KDialogBase::Cancel ); + mUI = new AddressBookExportUI( mDialog ); + mDialog->setMainWidget( mUI ); + mDialog->setButtonOK( KGuiItem( i18n( "Export" ), + QString::null, i18n( "Set address book fields using the selected data from Kopete" ) ) ); + + initLabels(); + // fetch existing data from kabc + fetchKABCData(); + // fetch data from contacts + fetchIMData(); + + return mDialog->exec(); + } + else + return QDialog::Rejected; +} + +void KopeteAddressBookExport::exportData() +{ + // write the data from the widget to KABC + // update the Addressee + // first name + bool dirty = false; + if ( newValue( mUI->mFirstName ) ) + { + dirty = true; + mAddressee.setGivenName( mUI->mFirstName->currentText() ); + } + // last name + if ( newValue( mUI->mLastName ) ) + { + dirty = true; + mAddressee.setFamilyName( mUI->mLastName->currentText() ); + } + // url + if ( newValue( mUI->mUrl ) ) + { + dirty = true; + mAddressee.setUrl( KURL( mUI->mUrl->currentText() ) ); + } + + QStringList newVals; + // email + newVals = newValues( mUI->mEmails, numEmails ); + for ( QStringList::Iterator it = newVals.begin(); it != newVals.end(); ++it ) + { + dirty = true; + mAddressee.insertEmail( *it ); + } + // home phone + newVals = newValues( mUI->mHomePhones, numHomePhones ); + for ( QStringList::Iterator it = newVals.begin(); it != newVals.end(); ++it ) + { + dirty = true; + mAddressee.insertPhoneNumber( KABC::PhoneNumber( *it, KABC::PhoneNumber::Home ) ); + } + // work phone + newVals = newValues( mUI->mWorkPhones, numWorkPhones ); + for ( QStringList::Iterator it = newVals.begin(); it != newVals.end(); ++it ) + { + dirty = true; + mAddressee.insertPhoneNumber( KABC::PhoneNumber( *it, KABC::PhoneNumber::Work ) ); + } + // mobile + newVals = newValues( mUI->mMobilePhones, numMobilePhones ); + for ( QStringList::Iterator it = newVals.begin(); it != newVals.end(); ++it ) + { + dirty = true; + mAddressee.insertPhoneNumber( KABC::PhoneNumber( *it, KABC::PhoneNumber::Cell ) ); + } + + if ( dirty ) + { + // write the changed addressbook + mAddressBook->insertAddressee( mAddressee ); + + KABC::Ticket *ticket = mAddressBook->requestSaveTicket(); + if ( !ticket ) + kdWarning( 14000 ) << k_funcinfo << "WARNING: Resource is locked by other application!" << endl; + else + { + if ( !mAddressBook->save( ticket ) ) + { + kdWarning( 14000 ) << k_funcinfo << "ERROR: Saving failed!" << endl; + mAddressBook->releaseSaveTicket( ticket ); + } + } + kdDebug( 14000 ) << k_funcinfo << "Finished writing KABC" << endl; + } +} + +bool KopeteAddressBookExport::newValue( QComboBox *combo ) +{ + // all data in position 0 is from KABC, so if position 0 is selected, + // or if the selection is the same as the data at 0, return false + return !( combo->currentItem() == 0 || + ( combo->text( combo->currentItem() ) == combo->text( 0 ) ) ); +} + +QStringList KopeteAddressBookExport::newValues( KListBox *listBox, uint counter ) +{ + QStringList newValues; + // need to iterate all items except those from KABC and check if selected and not same as the first + // counter is the number of KABC items, and hence the index of the first non KABC item + for ( uint i = counter; i < listBox->count(); ++i ) + { + if ( listBox->isSelected( i ) ) + { + // check whether it matches any KABC item + bool duplicate = false; + for ( uint j = 0; j < counter; ++j ) + { + if ( listBox->text( i ) == listBox->text( j ) ) + duplicate = true; + } + if ( !duplicate ) + newValues.append( listBox->text( i ) ); + } + } + return newValues; +} diff --git a/kopete/kopete/contactlist/kopeteaddrbookexport.h b/kopete/kopete/contactlist/kopeteaddrbookexport.h new file mode 100644 index 00000000..b4437c4e --- /dev/null +++ b/kopete/kopete/contactlist/kopeteaddrbookexport.h @@ -0,0 +1,102 @@ +/* + kopeteaddrbookexport.h - Kopete Online Status + + Logic for exporting data acquired from messaging systems to the + KDE address book + + Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KOPETEADDRBOOKEXPORT_H +#define KOPETEADDRBOOKEXPORT_H + +#include <kabc/stdaddressbook.h> +#include <kabc/addressee.h> + +#include "kopetecontactproperty.h" + +class AddressBookExportUI; +class KDialogBase; +class KListBox; +class KComboBox; + +namespace Kopete +{ +class Contact; +class MetaContact; +} + +class KopeteAddressBookExport : public QObject +{ +public: + KopeteAddressBookExport( QWidget *parent, Kopete::MetaContact *mc ); + ~KopeteAddressBookExport(); + + /** + * Display the dialog + * @return a QDialog return code + */ + int showDialog(); + /** + * Export the data to KABC if changed, omitting any duplicates + */ + void exportData(); + +protected: + /** + * Initialise the GUI labels with labels from KABC + */ + void initLabels(); + /** + * Populate the GUI with data from KABC + */ + void fetchKABCData(); + /** + * Populate a listbox with a given type of phone number + */ + void fetchPhoneNumbers( KListBox * listBox, int type, uint& counter ); + /** + * Populate the GUI with data from IM systems + */ + void fetchIMData(); + /** + * Populate a combobox with a contact's IM data + */ + void populateIM( const Kopete::Contact *contact, const QPixmap &icon, + QComboBox *combo, const Kopete::ContactPropertyTmpl &property ); + /** + * Populate a listbox with a contact's IM data + */ + void populateIM( const Kopete::Contact *contact, const QPixmap &icon, + KListBox *combo, const Kopete::ContactPropertyTmpl &property ); + + /** Check the selected item is not the first (existing KABC) item, or the same as it */ + bool newValue( QComboBox *combo ); + QStringList newValues( KListBox *listBox, uint counter ); + + // the GUI + QWidget *mParent; + KDialogBase * mDialog; + QPixmap mAddrBookIcon; + AddressBookExportUI *mUI; + Kopete::MetaContact *mMetaContact; + KABC::AddressBook *mAddressBook; + KABC::Addressee mAddressee; + + // counters tracking the number of KABC values where multiple values are possible in a single key + uint numEmails, numHomePhones, numWorkPhones, numMobilePhones; + +}; + +#endif diff --git a/kopete/kopete/contactlist/kopeteaddrbookexportui.ui b/kopete/kopete/contactlist/kopeteaddrbookexportui.ui new file mode 100644 index 00000000..9613d44f --- /dev/null +++ b/kopete/kopete/contactlist/kopeteaddrbookexportui.ui @@ -0,0 +1,149 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>AddressBookExportUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>AddressBookExportUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>598</width> + <height>651</height> + </rect> + </property> + <property name="caption"> + <string>Merge with Address Book</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>mLblFirstName</cstring> + </property> + <property name="text"> + <string>First name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>comboBox1</cstring> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>mLblHomePhone</cstring> + </property> + <property name="text"> + <string>Home phone:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mPhones</cstring> + </property> + </widget> + <widget class="KListBox" row="5" column="1"> + <property name="name"> + <cstring>mWorkPhones</cstring> + </property> + <property name="selectionMode"> + <enum>Extended</enum> + </property> + </widget> + <widget class="KListBox" row="6" column="1"> + <property name="name"> + <cstring>mMobilePhones</cstring> + </property> + <property name="selectionMode"> + <enum>Extended</enum> + </property> + </widget> + <widget class="KListBox" row="4" column="1"> + <property name="name"> + <cstring>mHomePhones</cstring> + </property> + <property name="selectionMode"> + <enum>Extended</enum> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>mLblWorkPhone</cstring> + </property> + <property name="text"> + <string>Work phone:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mPhones_2</cstring> + </property> + </widget> + <widget class="QLabel" row="6" column="0"> + <property name="name"> + <cstring>mLblMobilePhone</cstring> + </property> + <property name="text"> + <string>Mobile phone:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mPhones_3</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>mLblUrl</cstring> + </property> + <property name="text"> + <string>URL:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>comboBox4</cstring> + </property> + </widget> + <widget class="QComboBox" row="3" column="1"> + <property name="name"> + <cstring>mUrl</cstring> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <property name="name"> + <cstring>mFirstName</cstring> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <property name="name"> + <cstring>mLastName</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>mLblLastName</cstring> + </property> + <property name="text"> + <string>Last name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>comboBox2</cstring> + </property> + </widget> + <widget class="KListBox" row="2" column="1"> + <property name="name"> + <cstring>mEmails</cstring> + </property> + <property name="selectionMode"> + <enum>Extended</enum> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>mLblEmail</cstring> + </property> + <property name="text"> + <string>Email:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mEmails</cstring> + </property> + </widget> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/kopete/contactlist/kopetecontactlistview.cpp b/kopete/kopete/contactlist/kopetecontactlistview.cpp new file mode 100644 index 00000000..b6b01a2f --- /dev/null +++ b/kopete/kopete/contactlist/kopetecontactlistview.cpp @@ -0,0 +1,2134 @@ +/* + kopetecontactlistview.cpp + + Kopete Contactlist GUI + + Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org> + Copyright (c) 2002 by Nick Betcher <nbetcher@usinternet.com> + Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net> + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart @kde.org> + Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "kopetecontactlistview.h" +#include "kopeteuiglobal.h" + +#include <qcursor.h> +#include <qdragobject.h> +#include <qheader.h> +#include <qstylesheet.h> +#include <qtimer.h> +#include <qtooltip.h> +#include <qguardedptr.h> + +#include <kaction.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmainwindow.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <kurldrag.h> +#include <kmultipledrag.h> +#include <kabc/stdaddressbook.h> +#include <kabc/vcardconverter.h> + +#include <kdeversion.h> +#include <kinputdialog.h> + +#include "addcontactwizard.h" +#include "addcontactpage.h" +#include "chatmessagepart.h" +#include "kopeteaccount.h" +#include "kopeteaccountmanager.h" +#include "kopetecontactlist.h" +#include "kopetemessageevent.h" +#include "kopetegroup.h" +#include "kopetegroupviewitem.h" +#include "kopetemetacontact.h" +#include "kopetemetacontactlvi.h" +#include "kopeteprefs.h" +#include "kopeteprotocol.h" +#include "kopetestatusgroupviewitem.h" +#include "kopetestdaction.h" +#include "kopetechatsessionmanager.h" +#include "kopetecontact.h" +#include "kopetechatsession.h" //needed to send the URL +#include "kopetemessage.h" //needed to send the URL +#include "kopeteglobal.h" +#include "kopetelviprops.h" +#include "kopetegrouplistaction.h" + +#include <memory> + +class ContactListViewStrategy; + +class KopeteContactListViewPrivate +{ +public: + std::auto_ptr<ContactListViewStrategy> viewStrategy; + + void updateViewStrategy( KListView *view ); +}; + +class ContactListViewStrategy +{ +public: + ContactListViewStrategy( KListView *view ) + : _listView( view ) + { + view->clear(); + } + virtual ~ContactListViewStrategy() {} + KListView *listView() { return _listView; } + void addCurrentItems() + { + // Add the already existing groups now + QPtrList<Kopete::Group> grps = Kopete::ContactList::self()->groups(); + for ( QPtrListIterator<Kopete::Group> groupIt( grps ); groupIt.current(); ++groupIt ) + addGroup( groupIt.current() ); + + // Add the already existing meta contacts now + QPtrList<Kopete::MetaContact> metaContacts = Kopete::ContactList::self()->metaContacts(); + for ( QPtrListIterator<Kopete::MetaContact> it( metaContacts ); it.current(); ++it ) + addMetaContact( it.current() ); + } + + virtual void addMetaContact( Kopete::MetaContact *mc ) = 0; + virtual void removeMetaContact( Kopete::MetaContact *mc ) = 0; + + virtual void addGroup( Kopete::Group * ) {} + + virtual void addMetaContactToGroup( Kopete::MetaContact *mc, Kopete::Group *gp ) = 0; + virtual void removeMetaContactFromGroup( Kopete::MetaContact *mc, Kopete::Group *gp ) = 0; + virtual void moveMetaContactBetweenGroups( Kopete::MetaContact *mc, Kopete::Group *from, Kopete::Group *to ) = 0; + + virtual void metaContactStatusChanged( Kopete::MetaContact *mc ) = 0; + +protected: + // work around QListView design stupidity. + // GroupViewItem will be QListView-derived, or QListViewItem-derived. + template<typename GroupViewItem> + void addMetaContactToGroupInner( Kopete::MetaContact *mc, GroupViewItem *gpi ) + { + // check if the contact isn't already in the group + for( QListViewItem *item = gpi->firstChild(); item; item = item->nextSibling() ) + if ( KopeteMetaContactLVI *mci = dynamic_cast<KopeteMetaContactLVI*>(item) ) + if ( mci->metaContact() == mc ) + return; + (void) new KopeteMetaContactLVI( mc, gpi ); + } + + template<typename GroupViewItem> + void removeMetaContactFromGroupInner( Kopete::MetaContact *mc, GroupViewItem *gpi ) + { + KopeteMetaContactLVI* mci; + QListViewItem* item = gpi->firstChild(); + while(item) { + mci = dynamic_cast<KopeteMetaContactLVI*>(item); + item = item->nextSibling(); + + if ( mci && mci->metaContact() == mc ) + delete mci; + } + } + +private: + KListView *_listView; +}; + +class ArrangeByGroupsViewStrategy : public ContactListViewStrategy +{ +public: + ArrangeByGroupsViewStrategy( KListView *view ) + : ContactListViewStrategy( view ) + { + addCurrentItems(); + } + + void addMetaContact( Kopete::MetaContact *mc ) + { + // create group items + Kopete::GroupList list = mc->groups(); + for ( Kopete::Group *gp = list.first(); gp; gp = list.next() ) + // will check to see if the contact is already in the group. + // this is inefficient but makes this function idempotent. + addMetaContactToGroup( mc, gp ); + } + void removeMetaContact( Kopete::MetaContact *mc ) + { + // usually, the list item will be deleted when the KMC is. however, we still + // need to make sure that the item count of the groups is correct. + // as a bonus, this allows us to remove a MC from the contact list without deleting it. + Kopete::GroupList list = mc->groups(); + for ( Kopete::Group *gp = list.first(); gp; gp = list.next() ) + removeMetaContactFromGroup( mc, gp ); + } + + void addGroup( Kopete::Group *group ) + { + (void) findOrCreateGroupItem( group ); + } + + void addMetaContactToGroup( Kopete::MetaContact *mc, Kopete::Group *gp ) + { + if ( KopeteGroupViewItem *gpi = findOrCreateGroupItem( gp ) ) + addMetaContactToGroupInner( mc, gpi ); + else + addMetaContactToGroupInner( mc, listView() ); + } + void removeMetaContactFromGroup( Kopete::MetaContact *mc, Kopete::Group *gp ) + { + if ( gp->type() == Kopete::Group::TopLevel ) + removeMetaContactFromGroupInner( mc, listView() ); + else if ( KopeteGroupViewItem *gpi = findGroupItem( gp ) ) + { + removeMetaContactFromGroupInner( mc, gpi ); + + // update the group's display of its number of children. + // TODO: make the KopeteGroupViewItem not need this, by overriding insertItem and takeItem + gpi->refreshDisplayName(); + + // remove the temporary group if it's empty + if ( gpi->childCount() == 0 ) + if ( gp->type() == Kopete::Group::Temporary ) + delete gpi; + } + } + void moveMetaContactBetweenGroups( Kopete::MetaContact *mc, Kopete::Group *from, Kopete::Group *to ) + { + // TODO: use takeItem and insertItem, and mci->movedGroup + addMetaContactToGroup( mc, to ); + removeMetaContactFromGroup( mc, from ); + } + void metaContactStatusChanged( Kopete::MetaContact * ) {} + +private: + KopeteGroupViewItem *findGroupItem( Kopete::Group *gp ) + { + if ( gp->type() == Kopete::Group::TopLevel ) return 0; + for( QListViewItem *item = listView()->firstChild(); item; item = item->nextSibling() ) + if ( KopeteGroupViewItem *gvi = dynamic_cast<KopeteGroupViewItem*>(item) ) + if ( gvi->group() == gp ) + return gvi; + return 0; + } + KopeteGroupViewItem *findOrCreateGroupItem( Kopete::Group *gp ) + { + if ( gp->type() == Kopete::Group::TopLevel ) return 0; + if ( KopeteGroupViewItem *item = findGroupItem(gp) ) + return item; + KopeteGroupViewItem *gpi = new KopeteGroupViewItem( gp, listView() ); + // TODO: store as plugin data the expandedness of a group + // currently this requires a 'plugin' for the main UI. + gpi->setOpen( gp->isExpanded() ); + return gpi; + } +}; + +class ArrangeByPresenceViewStrategy : public ContactListViewStrategy +{ +public: + ArrangeByPresenceViewStrategy( KListView *view ) + : ContactListViewStrategy( view ) + , m_onlineItem( new KopeteStatusGroupViewItem( Kopete::OnlineStatus::Online, listView() ) ) + , m_offlineItem( new KopeteStatusGroupViewItem( Kopete::OnlineStatus::Offline, listView() ) ) + { + m_onlineItem->setOpen( true ); + m_offlineItem->setOpen( true ); + addCurrentItems(); + } + + void removeMetaContact( Kopete::MetaContact *mc ) + { + // there's only three places we put metacontacts: online, offline and temporary. + removeMetaContactFromGroupInner( mc, m_onlineItem ); + removeMetaContactFromGroupInner( mc, m_offlineItem ); + if ( m_temporaryItem ) + removeMetaContactFromGroupInner( mc, (KopeteGroupViewItem*)m_temporaryItem ); + } + + void addMetaContact( Kopete::MetaContact *mc ) + { + updateMetaContact( mc ); + } + void addMetaContactToGroup( Kopete::MetaContact *mc, Kopete::Group * ) + { + updateMetaContact( mc ); + } + void removeMetaContactFromGroup( Kopete::MetaContact *mc, Kopete::Group * ) + { + updateMetaContact( mc ); + } + void moveMetaContactBetweenGroups( Kopete::MetaContact *mc, Kopete::Group *, Kopete::Group * ) + { + updateMetaContact( mc ); + } + void metaContactStatusChanged( Kopete::MetaContact *mc ) + { + updateMetaContact( mc ); + } +private: + void updateMetaContact( Kopete::MetaContact *mc ) + { + // split into a ...Inner function and this one to make the short-circuiting logic easier + updateMetaContactInner( mc ); + + // FIXME: these items should do this for themselves... + m_onlineItem->setText(0,i18n("Online contacts (%1)").arg(m_onlineItem->childCount())); + m_offlineItem->setText(0,i18n("Offline contacts (%1)").arg(m_offlineItem->childCount())); + } + void updateMetaContactInner( Kopete::MetaContact *mc ) + { + // this function basically *is* the arrange-by-presence strategy. + // given a metacontact, it removes it from any existing incorrect place and adds + // it to the correct place. usually it does this with takeItem and insertItem. + + // if the metacontact is temporary, it should be only in the temporary group + if ( mc->isTemporary() ) + { + removeMetaContactFromGroupInner( mc, m_onlineItem ); + removeMetaContactFromGroupInner( mc, m_offlineItem ); + + // create temporary item on demand + if ( !m_temporaryItem ) + { + m_temporaryItem = new KopeteGroupViewItem( Kopete::Group::temporary(), listView() ); + m_temporaryItem->setOpen( true ); + } + + addMetaContactToGroupInner( mc, (KopeteGroupViewItem*)m_temporaryItem ); + return; + } + + // if it's not temporary, it should not be in the temporary group + if ( m_temporaryItem ) + { + removeMetaContactFromGroupInner( mc, (KopeteGroupViewItem*)m_temporaryItem ); + + // remove temporary item if empty + if ( m_temporaryItem && m_temporaryItem->childCount() == 0 ) + { + delete (KopeteGroupViewItem*)m_temporaryItem; + } + } + + // check if the contact is already in the correct "group" + QListViewItem *currentGroup = mc->isOnline() ? m_onlineItem : m_offlineItem; + for( QListViewItem *lvi = currentGroup->firstChild(); lvi; lvi = lvi->nextSibling() ) + if ( KopeteMetaContactLVI *kc = dynamic_cast<KopeteMetaContactLVI*>( lvi ) ) + if ( kc->metaContact() == mc ) + return; + + // item not found in the right group; look for it in the other group + QListViewItem *oppositeGroup = mc->isOnline() ? m_offlineItem : m_onlineItem; + for( QListViewItem *lvi = oppositeGroup->firstChild(); lvi; lvi = lvi->nextSibling() ) + { + if ( KopeteMetaContactLVI *kc = dynamic_cast<KopeteMetaContactLVI*>( lvi ) ) + { + if ( kc->metaContact() == mc ) + { + // found: move it over to the right group + oppositeGroup->takeItem(kc); + currentGroup->insertItem(kc); + return; + } + } + } + + // item not found in either online neither offline groups: add it + (void) new KopeteMetaContactLVI( mc, currentGroup ); + } + + KopeteStatusGroupViewItem *m_onlineItem, *m_offlineItem; + QGuardedPtr<KopeteGroupViewItem> m_temporaryItem; +}; + +void KopeteContactListViewPrivate::updateViewStrategy( KListView *view ) +{ + // this is a bit nasty, but this function needs changing if we add + // more view strategies anyway, so it should be fine. + bool bSortByGroup = (bool)dynamic_cast<ArrangeByGroupsViewStrategy*>(viewStrategy.get()); + if ( !viewStrategy.get() || KopetePrefs::prefs()->sortByGroup() != bSortByGroup ) + { + // delete old strategy first... + viewStrategy.reset( 0 ); + // then create and store a new one + if ( KopetePrefs::prefs()->sortByGroup() ) + viewStrategy.reset( new ArrangeByGroupsViewStrategy(view) ); + else + viewStrategy.reset( new ArrangeByPresenceViewStrategy(view) ); + } +} + +// returns the next item in a depth-first descent of the list view. +// much like QLVI::itemBelow but does not depend on visibility of items, etc. +static QListViewItem *nextItem( QListViewItem *item ) +{ + if ( QListViewItem *it = item->firstChild() ) + return it; + while ( item && !item->nextSibling() ) + item = item->parent(); + if ( !item ) + return 0; + return item->nextSibling(); +} + + + +KopeteContactListView::KopeteContactListView( QWidget *parent, const char *name ) + : Kopete::UI::ListView::ListView( parent, name ) +{ + d = new KopeteContactListViewPrivate; + m_undo=0L; + m_redo=0L; + + mShowAsTree = KopetePrefs::prefs()->treeView(); + if ( mShowAsTree ) + { + setRootIsDecorated( true ); + } + else + { + setRootIsDecorated( false ); + setTreeStepSize( 0 ); + } + + d->updateViewStrategy( this ); + + setFullWidth( true ); + + connect( this, SIGNAL( contextMenu( KListView *, QListViewItem *, const QPoint & ) ), + SLOT( slotContextMenu( KListView *, QListViewItem *, const QPoint & ) ) ); + connect( this, SIGNAL( expanded( QListViewItem * ) ), + SLOT( slotExpanded( QListViewItem * ) ) ); + connect( this, SIGNAL( collapsed( QListViewItem * ) ), + SLOT( slotCollapsed( QListViewItem * ) ) ); + connect( this, SIGNAL( executed( QListViewItem *, const QPoint &, int ) ), + SLOT( slotExecuted( QListViewItem *, const QPoint &, int ) ) ); + connect( this, SIGNAL( selectionChanged() ), SLOT( slotViewSelectionChanged() ) ); + connect( this, SIGNAL( itemRenamed( QListViewItem * ) ), + SLOT( slotItemRenamed( QListViewItem * ) ) ); + + connect( KopetePrefs::prefs(), SIGNAL( saved() ), SLOT( slotSettingsChanged() ) ); + + connect( Kopete::ContactList::self(), SIGNAL( selectionChanged() ), + SLOT( slotListSelectionChanged() ) ); + connect( Kopete::ContactList::self(), + SIGNAL( metaContactAdded( Kopete::MetaContact * ) ), + SLOT( slotMetaContactAdded( Kopete::MetaContact * ) ) ); + connect( Kopete::ContactList::self(), + SIGNAL( metaContactRemoved( Kopete::MetaContact * ) ), + SLOT( slotMetaContactDeleted( Kopete::MetaContact * ) ) ); + connect( Kopete::ContactList::self(), SIGNAL( groupAdded( Kopete::Group * ) ), + SLOT( slotGroupAdded( Kopete::Group * ) ) ); + + connect( Kopete::ChatSessionManager::self(), SIGNAL( newEvent( Kopete::MessageEvent * ) ), + this, SLOT( slotNewMessageEvent( Kopete::MessageEvent * ) ) ); + + connect( this, SIGNAL( dropped( QDropEvent *, QListViewItem *, QListViewItem * ) ), + this, SLOT( slotDropped( QDropEvent *, QListViewItem *, QListViewItem * ) ) ); + + connect( &undoTimer, SIGNAL(timeout()) , this, SLOT (slotTimeout() ) ); + + addColumn( i18n( "Contacts" ), 0 ); //add an unique colums to add every contact + header()->hide(); // and hide the ugly header which show the single word "Contacts" + + setAutoOpen( true ); + setDragEnabled( true ); + setAcceptDrops( true ); + setItemsMovable( false ); + setDropVisualizer( false ); + setDropHighlighter( true ); + setSelectionMode( QListView::Extended ); + + // Load in the user's initial settings + slotSettingsChanged(); +} + +void KopeteContactListView::initActions( KActionCollection *ac ) +{ + actionUndo = KStdAction::undo( this , SLOT( slotUndo() ) , ac ); + actionRedo = KStdAction::redo( this , SLOT( slotRedo() ) , ac ); + actionUndo->setEnabled(false); + actionRedo->setEnabled(false); + + + new KAction( i18n( "Create New Group..." ), 0, 0, this, SLOT( addGroup() ), + ac, "AddGroup" ); + + actionSendMessage = KopeteStdAction::sendMessage( + this, SLOT( slotSendMessage() ), ac, "contactSendMessage" ); + actionStartChat = KopeteStdAction::chat( this, SLOT( slotStartChat() ), + ac, "contactStartChat" ); + + actionMove = new KopeteGroupListAction( i18n( "&Move To" ), QString::fromLatin1( "editcut" ), + 0, this, SLOT( slotMoveToGroup() ), ac, "contactMove" ); + actionCopy = new KopeteGroupListAction( i18n( "&Copy To" ), QString::fromLatin1( "editcopy" ), 0, + this, SLOT( slotCopyToGroup() ), ac, "contactCopy" ); + + actionRemove = KopeteStdAction::deleteContact( this, SLOT( slotRemove() ), + ac, "contactRemove" ); + actionSendEmail = new KAction( i18n( "Send Email..." ), QString::fromLatin1( "mail_generic" ), + 0, this, SLOT( slotSendEmail() ), ac, "contactSendEmail" ); + /* this actionRename is buggy, and useless with properties, removed in kopeteui.rc*/ + actionRename = new KAction( i18n( "Rename" ), "filesaveas", 0, + this, SLOT( slotRename() ), ac, "contactRename" ); + actionSendFile = KopeteStdAction::sendFile( this, SLOT( slotSendFile() ), + ac, "contactSendFile" ); + + actionAddContact = new KActionMenu( i18n( "&Add Contact" ), + QString::fromLatin1( "add_user" ), ac , "contactAddContact" ); + actionAddContact->popupMenu()->insertTitle( i18n("Select Account") ); + + actionAddTemporaryContact = new KAction( i18n( "Add to Your Contact List" ), "add_user", 0, + this, SLOT( slotAddTemporaryContact() ), ac, "contactAddTemporaryContact" ); + + connect( Kopete::ContactList::self(), SIGNAL( metaContactSelected( bool ) ), this, SLOT( slotMetaContactSelected( bool ) ) ); + + connect( Kopete::AccountManager::self(), SIGNAL(accountRegistered( Kopete::Account* )), SLOT(slotAddSubContactActionNewAccount(Kopete::Account*))); + connect( Kopete::AccountManager::self(), SIGNAL(accountUnregistered( const Kopete::Account* )), SLOT(slotAddSubContactActionAccountDeleted(const Kopete::Account *))); + + actionProperties = new KAction( i18n( "&Properties" ), "edit_user", Qt::Key_Alt + Qt::Key_Return, + this, SLOT( slotProperties() ), ac, "contactProperties" ); + + // Update enabled/disabled actions + slotViewSelectionChanged(); +} + +KopeteContactListView::~KopeteContactListView() +{ + delete d; +} + +void KopeteContactListView::slotAddSubContactActionNewAccount(Kopete::Account* account) +{ + KAction *action = new KAction( account->accountLabel(), account->accountIcon(), 0 , this, SLOT(slotAddContact()), account); + m_accountAddContactMap.insert( account, action); + actionAddContact->insert( action ); +} + +void KopeteContactListView::slotAddSubContactActionAccountDeleted(const Kopete::Account *account) +{ + kdDebug(14000) << k_funcinfo << endl; + if ( m_accountAddContactMap.contains( account ) ) + { + KAction *action = m_accountAddContactMap[account]; + m_accountAddContactMap.remove( account ); + actionAddContact->remove( action ); + } +} + +void KopeteContactListView::slotMetaContactAdded( Kopete::MetaContact *mc ) +{ + d->viewStrategy->addMetaContact( mc ); + + connect( mc, SIGNAL( addedToGroup( Kopete::MetaContact *, Kopete::Group * ) ), + SLOT( slotAddedToGroup( Kopete::MetaContact *, Kopete::Group * ) ) ); + connect( mc, SIGNAL( removedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ), + SLOT( slotRemovedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ) ); + connect( mc, SIGNAL( movedToGroup( Kopete::MetaContact *, Kopete::Group *, Kopete::Group * ) ), + SLOT( slotMovedToGroup( Kopete::MetaContact *, Kopete::Group *, Kopete::Group * ) ) ); + connect( mc, SIGNAL( onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType ) ), + SLOT( slotContactStatusChanged( Kopete::MetaContact * ) ) ); +} + +void KopeteContactListView::slotMetaContactDeleted( Kopete::MetaContact *mc ) +{ + d->viewStrategy->removeMetaContact( mc ); +} + +void KopeteContactListView::slotMetaContactSelected( bool sel ) +{ + bool set = sel; + + if( sel ) + { + Kopete::MetaContact *kmc = Kopete::ContactList::self()->selectedMetaContacts().first(); + set = sel && kmc->isReachable(); + actionAddTemporaryContact->setEnabled( sel && kmc->isTemporary() ); + } + else + { + actionAddTemporaryContact->setEnabled(false); + } + + actionSendMessage->setEnabled( set ); + actionStartChat->setEnabled( set ); + actionMove->setEnabled( sel ); // TODO: make available for several contacts + actionCopy->setEnabled( sel ); // TODO: make available for several contacts +} + +void KopeteContactListView::slotAddedToGroup( Kopete::MetaContact *mc, Kopete::Group *to ) +{ + d->viewStrategy->addMetaContactToGroup( mc, to ); +} + +void KopeteContactListView::slotRemovedFromGroup( Kopete::MetaContact *mc, Kopete::Group *from ) +{ + d->viewStrategy->removeMetaContactFromGroup( mc, from ); +} + +void KopeteContactListView::slotMovedToGroup( Kopete::MetaContact *mc, + Kopete::Group *from, Kopete::Group *to ) +{ + d->viewStrategy->moveMetaContactBetweenGroups( mc, from, to ); +} + +void KopeteContactListView::removeContact( Kopete::MetaContact *c ) +{ + d->viewStrategy->removeMetaContact( c ); +} + +void KopeteContactListView::addGroup() +{ + QString groupName = + KInputDialog::getText( i18n( "New Group" ), + i18n( "Please enter the name for the new group:" ) ); + + if ( !groupName.isEmpty() ) + addGroup( groupName ); +} + +void KopeteContactListView::addGroup( const QString &groupName ) +{ + d->viewStrategy->addGroup( Kopete::ContactList::self()->findGroup(groupName) ); +} + +void KopeteContactListView::slotGroupAdded( Kopete::Group *group ) +{ + d->viewStrategy->addGroup( group ); +} + +void KopeteContactListView::slotExpanded( QListViewItem *item ) +{ + KopeteGroupViewItem *groupLVI = dynamic_cast<KopeteGroupViewItem *>( item ); + if ( groupLVI ) + { + groupLVI->group()->setExpanded( true ); + groupLVI->updateIcon(); + } + + //workaround a bug in qt which make the items of a closed item not sorted. (qt 3.3.4 here) + delayedSort(); +} + +void KopeteContactListView::slotCollapsed( QListViewItem *item ) +{ + KopeteGroupViewItem *groupLVI = dynamic_cast<KopeteGroupViewItem*>( item ); + if ( groupLVI ) + { + groupLVI->group()->setExpanded( false ); + groupLVI->updateIcon(); + } +} + +void KopeteContactListView::slotContextMenu( KListView * /*listview*/, + QListViewItem *item, const QPoint &point ) +{ + // FIXME: this code should be moved to the various list view item classes. + KopeteMetaContactLVI *metaLVI = dynamic_cast<KopeteMetaContactLVI *>( item ); + KopeteGroupViewItem *groupvi = dynamic_cast<KopeteGroupViewItem *>( item ); + + if ( item && !item->isSelected() ) + { + clearSelection(); + item->setSelected( true ); + } + + if ( !item ) + { + clearSelection(); + //Clear selection doesn't update lists of selected contact if the item is onlt heilighted (see bug 106090) + Kopete::ContactList::self()->setSelectedItems( QPtrList<Kopete::MetaContact>() , QPtrList<Kopete::Group>() ); + } + + int nb = Kopete::ContactList::self()->selectedMetaContacts().count() + + Kopete::ContactList::self()->selectedGroups().count(); + + KMainWindow *window = dynamic_cast<KMainWindow *>(topLevelWidget()); + if ( !window ) + { + kdError( 14000 ) << k_funcinfo << "Main window not found, unable to display context-menu; " + << "Kopete::UI::Global::mainWidget() = " << Kopete::UI::Global::mainWidget() << endl; + return; + } + + if ( metaLVI && nb == 1 ) + { + int px = mapFromGlobal( point ).x() - ( header()->sectionPos( header()->mapToIndex( 0 ) ) + + treeStepSize() * ( item->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + itemMargin() ); + int py = mapFromGlobal( point ).y() - itemRect( item ).y() - (header()->isVisible() ? header()->height() : 0) ; + + //kdDebug( 14000 ) << k_funcinfo << "x: " << px << ", y: " << py << endl; + Kopete::Contact *c = metaLVI->contactForPoint( QPoint( px, py ) ) ; + if ( c ) + { + KPopupMenu *p = c->popupMenu(); + connect( p, SIGNAL( aboutToHide() ), p, SLOT( deleteLater() ) ); + p->popup( point ); + } + else + { + KPopupMenu *popup = dynamic_cast<KPopupMenu *>( + window->factory()->container( "contact_popup", window ) ); + if ( popup ) + { + QString title = i18n( "Translators: format: '<nickname> (<online status>)'", "%1 (%2)" ). + arg( metaLVI->metaContact()->displayName(), metaLVI->metaContact()->statusString() ); + + if ( title.length() > 43 ) + title = title.left( 40 ) + QString::fromLatin1( "..." ); + + if ( popup->title( 0 ).isNull() ) + popup->insertTitle ( title, 0, 0 ); + else + popup->changeTitle ( 0, title ); + + // Submenus for separate contact actions + bool sep = false; //FIXME: find if there is already a separator in the end - Olivier + QPtrList<Kopete::Contact> it = metaLVI->metaContact()->contacts(); + for( Kopete::Contact *c = it.first(); c; c = it.next() ) + { + if( sep ) + { + popup->insertSeparator(); + sep = false; + } + + KPopupMenu *contactMenu = it.current()->popupMenu(); + connect( popup, SIGNAL( aboutToHide() ), contactMenu, SLOT( deleteLater() ) ); + QString nick=c->property(Kopete::Global::Properties::self()->nickName()).value().toString(); + QString text= nick.isEmpty() ? c->contactId() : i18n( "Translators: format: '<displayName> (<id>)'", "%2 <%1>" ). arg( c->contactId(), nick ); + text=text.replace("&","&&"); // cf BUG 115449 + + if ( text.length() > 41 ) + text = text.left( 38 ) + QString::fromLatin1( "..." ); + + popup->insertItem( c->onlineStatus().iconFor( c, 16 ), text , contactMenu ); + } + + popup->popup( point ); + } + } + } + else if ( groupvi && nb == 1 ) + { + KPopupMenu *popup = dynamic_cast<KPopupMenu *>( + window->factory()->container( "group_popup", window ) ); + if ( popup ) + { + QString title = groupvi->group()->displayName(); + if ( title.length() > 32 ) + title = title.left( 30 ) + QString::fromLatin1( "..." ); + + if( popup->title( 0 ).isNull() ) + popup->insertTitle( title, 0, 0 ); + else + popup->changeTitle( 0, title ); + + popup->popup( point ); + } + } + else if ( nb >= 1 ) + { + KPopupMenu *popup = dynamic_cast<KPopupMenu *>( + window->factory()->container( "contactlistitems_popup", window ) ); + if ( popup ) + popup->popup( point ); + } + else + { + KPopupMenu *popup = dynamic_cast<KPopupMenu *>( + window->factory()->container( "contactlist_popup", window ) ); + if ( popup ) + { + if ( popup->title( 0 ).isNull() ) + popup->insertTitle( i18n( "Kopete" ), 0, 0 ); + + popup->popup( point ); + } + } +} + +void KopeteContactListView::slotShowAddContactDialog() +{ + ( new AddContactWizard( Kopete::UI::Global::mainWidget() ) )->show(); +} + +void KopeteContactListView::slotSettingsChanged( void ) +{ + mShowAsTree = KopetePrefs::prefs()->treeView(); + if ( mShowAsTree ) + { + setRootIsDecorated( true ); + setTreeStepSize( 20 ); + } + else + { + setRootIsDecorated( false ); + setTreeStepSize( 0 ); + } + + // maybe setEffects should read these from KopetePrefs itself? + Kopete::UI::ListView::Item::setEffects( KopetePrefs::prefs()->contactListAnimation(), + KopetePrefs::prefs()->contactListFading(), + KopetePrefs::prefs()->contactListFolding() ); + + d->updateViewStrategy( this ); + + slotUpdateAllGroupIcons(); + update(); +} + +void KopeteContactListView::slotUpdateAllGroupIcons() +{ + // FIXME: groups can (should?) do this for themselves + // HACK: assume all groups are top-level. works for now, until the fixme above is dealt with + for ( QListViewItem *it = firstChild(); it; it = it->nextSibling() ) + if ( KopeteGroupViewItem *gpi = dynamic_cast<KopeteGroupViewItem*>( it ) ) + gpi->updateIcon(); +} + +void KopeteContactListView::slotExecuted( QListViewItem *item, const QPoint &p, int /* col */ ) +{ + item->setSelected( false ); + KopeteMetaContactLVI *metaContactLVI = dynamic_cast<KopeteMetaContactLVI *>( item ); + + QPoint pos = viewport()->mapFromGlobal( p ); + Kopete::Contact *c = 0L; + if ( metaContactLVI ) + { + // Try if we are clicking a protocol icon. If so, open a direct + // connection for that protocol + QRect r = itemRect( item ); + QPoint relativePos( pos.x() - r.left() - ( treeStepSize() * + ( item->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + + itemMargin() ), pos.y() - r.top() ); + c = metaContactLVI->contactForPoint( relativePos ); + if( c ) + c->execute(); + else + metaContactLVI->execute(); + } +} + +void KopeteContactListView::slotContactStatusChanged( Kopete::MetaContact *mc ) +{ + d->viewStrategy->metaContactStatusChanged( mc ); +} + +void KopeteContactListView::slotDropped(QDropEvent *e, QListViewItem *, QListViewItem *after) +{ + if(!acceptDrag(e)) + return; + + KopeteMetaContactLVI *dest_metaLVI=dynamic_cast<KopeteMetaContactLVI*>(after); + KopeteGroupViewItem *dest_groupLVI=dynamic_cast<KopeteGroupViewItem*>(after); + + if( const_cast<const QWidget *>( e->source() ) == this ) + { + QPtrListIterator<KopeteMetaContactLVI> it( m_selectedContacts ); + + while ( it.current() ) + { + Kopete::Contact *source_contact=0L; + KopeteMetaContactLVI *source_metaLVI = it.current(); + ++it; + + if(source_metaLVI) + source_contact = source_metaLVI->contactForPoint( m_startDragPos ); + + if(source_metaLVI && dest_groupLVI) + { + if(source_metaLVI->group() == dest_groupLVI->group()) + return; + + if(source_metaLVI->metaContact()->isTemporary()) + { + addDraggedContactToGroup(source_metaLVI->metaContact(),dest_groupLVI->group()); + } + else + { + moveDraggedContactToGroup( source_metaLVI->metaContact(), + source_metaLVI->group(), dest_groupLVI->group() ); + } + } + else if(source_metaLVI && !dest_metaLVI && !dest_groupLVI) + { + if ( source_metaLVI->group()->type() == Kopete::Group::TopLevel ) + return; + + if(source_metaLVI->metaContact()->isTemporary()) + { + addDraggedContactToGroup(source_metaLVI->metaContact() , Kopete::Group::topLevel() ); + } + else + { + moveDraggedContactToGroup( source_metaLVI->metaContact(), + source_metaLVI->group(), Kopete::Group::topLevel() ); + } + } + else if(source_contact && dest_metaLVI) //we are moving a contact to another metacontact + { + if(source_metaLVI->metaContact()->isTemporary()) + { + addDraggedContactToMetaContact( source_contact,dest_metaLVI->metaContact() ); + } + else + { + UndoItem *u=new UndoItem; + u->type=UndoItem::MetaContactChange; + u->metacontact=source_metaLVI->metaContact(); + u->group=source_metaLVI->group(); + u->args << source_contact->protocol()->pluginId() << source_contact->account()->accountId() << source_contact->contactId(); + u->args << source_metaLVI->metaContact()->displayName(); + insertUndoItem(u); + + source_contact->setMetaContact(dest_metaLVI->metaContact()); + } + } + } + } + else if( e->provides("kopete/x-contact") ) + { + QString contactInfo = QString::fromUtf8( e->encodedData("kopete/x-contact") ); + QString protocolId = contactInfo.section( QChar( 0xE000 ), 0, 0 ); + QString accountId = contactInfo.section( QChar( 0xE000 ), 1, 1 ); + QString contactId = contactInfo.section( QChar( 0xE000 ), 2 ); + + addDraggedContactByInfo( protocolId, accountId, contactId, after ); + + } + else if( e->provides("text/uri-list") ) + { + if ( !QUriDrag::canDecode( e ) ) + { + e->ignore(); + return; + } + + KURL::List urlList; + KURLDrag::decode( e, urlList ); + + for ( KURL::List::Iterator it = urlList.begin(); it != urlList.end(); ++it ) + { + KURL url = (*it); + if( url.protocol() == QString::fromLatin1("kopetemessage") ) + { + //Add a contact + addDraggedContactByInfo( url.queryItem("protocolId"), + url.queryItem("accountId"), url.host(), after ); + } + else if( dest_metaLVI ) + { + QPoint p = contentsToViewport(e->pos()); + int px = p.x() - ( header()->sectionPos( header()->mapToIndex( 0 ) ) + + treeStepSize() * ( dest_metaLVI->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + itemMargin() ); + int py = p.y() - itemRect( dest_metaLVI ).y(); + + Kopete::Contact *c = dest_metaLVI->contactForPoint( QPoint( px, py ) ); + + if( url.isLocalFile() ) + { + //send a file + if(c) + c->sendFile( url ); + else + dest_metaLVI->metaContact()->sendFile( url ); + } + else + { + //this is a URL, send the URL in a message + if(!c) + { + // We need to know which contact was chosen as the preferred + // in order to message it + c = dest_metaLVI->metaContact()->execute(); + } + + if (!c) + return; + + Kopete::Message msg(c->account()->myself(), c, url.url(), + Kopete::Message::Outbound); + c->manager(Kopete::Contact::CanCreate)->sendMessage(msg); + } + } + } + e->acceptAction(); + } +} + +void KopeteContactListView::moveDraggedContactToGroup( Kopete::MetaContact *contact, Kopete::Group *from, Kopete::Group *to ) +{ + contact->moveToGroup( from, to ); + + insertUndoItem( new UndoItem( UndoItem::MetaContactCopy , contact, to ) ); + UndoItem *u=new UndoItem( UndoItem::MetaContactRemove, contact, to ); + u->isStep=false; + insertUndoItem(u); +} + +void KopeteContactListView::addDraggedContactToGroup( Kopete::MetaContact *contact, Kopete::Group *group ) +{ + int r=KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(), + i18n( "<qt>Would you like to add <b>%1</b> to your contact list as a member of <b>%2</b>?</qt>" ) + .arg( contact->displayName(), group->displayName() ), + i18n( "Kopete" ), i18n("Add"), i18n("Do Not Add"), + "addTemporaryWhenMoving" ); + + if( r == KMessageBox::Yes ) + { + contact->setTemporary( false, group ); + Kopete::ContactList::self()->addMetaContact( contact ); + insertUndoItem( new UndoItem( UndoItem::MetaContactAdd, contact, group ) ); + } +} + +void KopeteContactListView::addDraggedContactToMetaContact( Kopete::Contact *contact, Kopete::MetaContact *parent ) +{ + int r = KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(), + i18n( "<qt>Would you like to add <b>%1</b> to your contact list as a child contact of <b>%2</b>?</qt>" ) + .arg( contact->contactId(), parent->displayName() ), + i18n( "Kopete" ), i18n("Add"), i18n("Do Not Add"), + "addTemporaryWhenMoving" ); + + if( r == KMessageBox::Yes ) + { + contact->setMetaContact(parent); + + UndoItem *u=new UndoItem; + u->type=UndoItem::ContactAdd; + u->args << contact->protocol()->pluginId() << contact->account()->accountId() << contact->contactId(); + insertUndoItem(u); + } +} + +void KopeteContactListView::addDraggedContactByInfo( const QString &protocolId, const QString &accountId, + const QString &contactId, QListViewItem *after ) +{ + kdDebug(14000) << k_funcinfo << "protocolId=" << protocolId << + ", accountId=" << accountId << ", contactId=" << contactId << endl; + + Kopete::Account *account = Kopete::AccountManager::self()->findAccount( protocolId,accountId ); + if( account ) + { + QDict<Kopete::Contact> contacts = account->contacts(); + Kopete::Contact *source_contact = contacts[ contactId ]; + + if( source_contact ) + { + if( source_contact->metaContact()->isTemporary() ) + { + KopeteMetaContactLVI *dest_metaLVI=dynamic_cast<KopeteMetaContactLVI*>(after); + KopeteGroupViewItem *dest_groupLVI=dynamic_cast<KopeteGroupViewItem*>(after); + + if( dest_metaLVI ) + { + addDraggedContactToMetaContact( source_contact, dest_metaLVI->metaContact() ); + } + else if( dest_groupLVI ) + { + addDraggedContactToGroup( source_contact->metaContact(),dest_groupLVI->group() ); + } + else + { + addDraggedContactToGroup( source_contact->metaContact(), Kopete::Group::topLevel() ); + } + } + else + { + KMessageBox::sorry( Kopete::UI::Global::mainWidget(), + i18n("<qt>This contact is already on your contact list. It is a child contact of <b>%1</b></qt>") + .arg( source_contact->metaContact()->displayName() ) + ); + } + } + } +} + +bool KopeteContactListView::acceptDrag(QDropEvent *e) const +{ + QListViewItem *source=currentItem(); + QListViewItem *parent; + QListViewItem *afterme; + // Due to a little design problem in KListView::findDrop() we can't + // call it directly from a const method until KDE 4.0, but as the + // method is in fact const we can of course get away with a + // const_cast... + const_cast<KopeteContactListView *>( this )->findDrop( e->pos(), parent, afterme ); + + KopeteMetaContactLVI *dest_metaLVI=dynamic_cast<KopeteMetaContactLVI*>(afterme); + + if( const_cast<const QWidget *>( e->source() ) == this ) + { + KopeteMetaContactLVI *source_metaLVI=dynamic_cast<KopeteMetaContactLVI*>(source); + KopeteGroupViewItem *dest_groupLVI=dynamic_cast<KopeteGroupViewItem*>(afterme); + Kopete::Contact *source_contact=0L; + + if(source_metaLVI) + source_contact = source_metaLVI->contactForPoint( m_startDragPos ); + + if( source_metaLVI && dest_groupLVI && !source_contact) + { //we are moving a metacontact to another group + if(source_metaLVI->group() == dest_groupLVI->group()) + return false; + if ( dest_groupLVI->group()->type() == Kopete::Group::Temporary ) + return false; + // if(source_metaLVI->metaContact()->isTemporary()) + // return false; + return true; + } + else if(source_metaLVI && !dest_metaLVI && !dest_groupLVI && !source_contact) + { //we are moving a metacontact to toplevel + if ( source_metaLVI->group()->type() == Kopete::Group::TopLevel ) + return false; + // if(source_metaLVI->metaContact()->isTemporary()) + // return false; + + return true; + } + else if(source_contact && dest_metaLVI) //we are moving a contact to another metacontact + { + if(source_contact->metaContact() == dest_metaLVI->metaContact() ) + return false; + if(dest_metaLVI->metaContact()->isTemporary()) + return false; + return true; + } +/* else if(source_groupLVI && dest_groupLVI) //we are moving a group to another group + { + if(dest_groupLVI->group() == Kopete::Group::temporary) + return false; + if(source_groupLVI->group() == Kopete::Group::temporary) + return false; + if(source_groupLVI->group()->parentGroup() == dest_groupLVI->group() ) + return false; + Kopete::Group *g=dest_groupLVI->group()->parentGroup(); + while(g && g != Kopete::Group::toplevel) + { + if(g==source_groupLVI->group()) + return false; + g=g->parentGroup(); + } + return true; + } + else if(source_groupLVI && !dest_groupLVI && dest_metaLVI) //we are moving a group to toplevel + { + if(source_groupLVI->group() == Kopete::Group::temporary) + return false; + if(source_groupLVI->group()->parentGroup() == Kopete::Group::toplevel) + return false; + return true; + }*/ + } + else + { + if( e->provides( "text/uri-list" ) ) + { + //we are sending a file (or dragging from the chat view) + if( dest_metaLVI ) + { + QPoint p=contentsToViewport(e->pos()); + int px = p.x() - ( header()->sectionPos( header()->mapToIndex( 0 ) ) + + treeStepSize() * ( dest_metaLVI->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + itemMargin() ); + int py = p.y() - itemRect( dest_metaLVI ).y(); + Kopete::Contact *c = dest_metaLVI->contactForPoint( QPoint( px, py ) ) ; + + if( c ? !c->isReachable() : !dest_metaLVI->metaContact()->isReachable() ) + return false; //If the pointed contact is not reachable, abort + + if( c ? c->canAcceptFiles() : dest_metaLVI->metaContact()->canAcceptFiles() ) + { + // If the pointed contact, or the metacontact if no contact are + // pointed can accept file, return true in everycase + return true; + } + } + + if ( !QUriDrag::canDecode(e) ) + return false; + + KURL::List urlList; + KURLDrag::decode( e, urlList ); + + for ( KURL::List::Iterator it = urlList.begin(); it != urlList.end(); ++it ) + { + if( (*it).protocol() != QString::fromLatin1("kopetemessage") && (*it).isLocalFile() ) + return false; //we can't send links if a locale file is in link + } + + return true; //we will send a link + } + else if( e->provides( "application/x-qlistviewitem" ) ) + { + //Coming from chat members + return true; + } + else + { + QString text; + QTextDrag::decode(e, text); + kdDebug(14000) << k_funcinfo << "drop with mimetype:" << e->format() << " data as text:" << text << endl; + } + + } + + return false; +} + +void KopeteContactListView::findDrop(const QPoint &pos, QListViewItem *&parent, + QListViewItem *&after) +{ + //Since KDE 3.1.1 , the original find Drop return 0L for afterme if the group is open. + //This woraround allow us to keep the highlight of the item, and give always a correct position + parent=0L; + QPoint p (contentsToViewport(pos)); + after=itemAt(p); +} + + +void KopeteContactListView::contentsMousePressEvent( QMouseEvent *e ) +{ + KListView::contentsMousePressEvent( e ); + if (e->button() == LeftButton ) + { + QPoint p=contentsToViewport(e->pos()); + QListViewItem *i=itemAt( p ); + if( i ) + { + //Maybe we are starting a drag? + //memorize the position to know later if the user move a small contacticon + + int px = p.x() - ( header()->sectionPos( header()->mapToIndex( 0 ) ) + + treeStepSize() * ( i->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + itemMargin() ); + int py = p.y() - itemRect( i ).y(); + + m_startDragPos = QPoint( px , py ); + } + } +} + +void KopeteContactListView::slotNewMessageEvent(Kopete::MessageEvent *event) +{ + Kopete::Message msg=event->message(); + //only for single chat + if(msg.from() && msg.to().count()==1) + { + Kopete::MetaContact *m=msg.from()->metaContact(); + if(!m) + return; + + for ( QListViewItem *item = firstChild(); item; item = nextItem(item) ) + if ( KopeteMetaContactLVI *li = dynamic_cast<KopeteMetaContactLVI*>(item) ) + if ( li->metaContact() == m ) + li->catchEvent(event); + } +} + +QDragObject *KopeteContactListView::dragObject() +{ + // Discover what the drag started on. + // If it's a MetaContactLVI, it was either on the MCLVI itself + // or on one of the child contacts + // Once we know this, + // we set the pixmap for the drag to the MC's pixmap + // or the child contact's small icon + + QListViewItem *currentLVI = currentItem(); + if( !currentLVI ) + return 0L; + + KopeteMetaContactLVI *metaLVI = dynamic_cast<KopeteMetaContactLVI*>( currentLVI ); + if( !metaLVI ) + return 0L; + + QPixmap pm; + Kopete::Contact *c = metaLVI->contactForPoint( m_startDragPos ); + KMultipleDrag *drag = new KMultipleDrag( this ); + drag->addDragObject( new QStoredDrag("application/x-qlistviewitem", 0L ) ); + + QStoredDrag *d = new QStoredDrag("kopete/x-metacontact", 0L ); + d->setEncodedData( metaLVI->metaContact()->metaContactId().utf8() ); + drag->addDragObject( d ); + + if ( c ) // dragging a contact + { + QStoredDrag *d = new QStoredDrag("kopete/x-contact", 0L ); + d->setEncodedData( QString( c->protocol()->pluginId() +QChar( 0xE000 )+ c->account()->accountId() +QChar( 0xE000 )+ c->contactId() ).utf8() ); + drag->addDragObject( d ); + + pm = c->onlineStatus().iconFor( c, 12 ); // FIXME: fixed icon scaling + } + else // dragging a metacontact + { + // FIXME: first start at rendering the whole MC incl small icons + // into a pixmap to drag - anyone know how to complete this? + //QPainter p( pm ); + //source_metaLVI->paintCell( p, cg?, width(), 0, 0 ); + pm = SmallIcon( metaLVI->metaContact()->statusIcon() ); + } + + KABC::Addressee address = KABC::StdAddressBook::self()->findByUid( + metaLVI->metaContact()->metaContactId() + ); + + if( !address.isEmpty() ) + { + drag->addDragObject( new QTextDrag( address.fullEmail(), 0L ) ); + KABC::VCardConverter converter; + QString vcard = converter.createVCard( address ); + if( !vcard.isNull() ) + { + QStoredDrag *vcardDrag = new QStoredDrag("text/x-vcard", 0L ); + vcardDrag->setEncodedData( vcard.utf8() ); + drag->addDragObject( vcardDrag ); + } + } + + //QSize s = pm.size(); + drag->setPixmap( pm /*, QPoint( s.width() , s.height() )*/ ); + + return drag; +} + +void KopeteContactListView::slotViewSelectionChanged() +{ + QPtrList<Kopete::MetaContact> contacts; + QPtrList<Kopete::Group> groups; + + m_selectedContacts.clear(); + m_selectedGroups.clear(); + + QListViewItemIterator it( this ); + while ( it.current() ) + { + QListViewItem *item = it.current(); + ++it; + + if ( item->isSelected() ) + { + KopeteMetaContactLVI *metaLVI=dynamic_cast<KopeteMetaContactLVI*>(item); + if(metaLVI) + { + m_selectedContacts.append( metaLVI ); + if(!contacts.contains(metaLVI->metaContact())) + contacts.append( metaLVI->metaContact() ); + } + KopeteGroupViewItem *groupLVI=dynamic_cast<KopeteGroupViewItem*>(item); + if(groupLVI) + { + m_selectedGroups.append( groupLVI ); + if(!groups.contains(groupLVI->group())) + groups.append( groupLVI->group() ); + + } + } + } + + // will cause slotListSelectionChanged to be called to update our actions. + Kopete::ContactList::self()->setSelectedItems(contacts , groups); +} + +void KopeteContactListView::slotListSelectionChanged() +{ + QPtrList<Kopete::MetaContact> contacts = Kopete::ContactList::self()->selectedMetaContacts(); + QPtrList<Kopete::Group> groups = Kopete::ContactList::self()->selectedGroups(); + + //TODO: update the list to select the items that should be selected. + // make sure slotViewSelectionChanged is *not* called. + updateActionsForSelection( contacts, groups ); +} + +void KopeteContactListView::updateActionsForSelection( + QPtrList<Kopete::MetaContact> contacts, QPtrList<Kopete::Group> groups ) +{ + bool singleContactSelected = groups.isEmpty() && contacts.count() == 1; + bool inkabc=false; + if(singleContactSelected) + { + QString kabcid=contacts.first()->metaContactId(); + inkabc= !kabcid.isEmpty() && !kabcid.contains(":"); + } + + actionSendFile->setEnabled( singleContactSelected && contacts.first()->canAcceptFiles()); + actionAddContact->setEnabled( singleContactSelected && !contacts.first()->isTemporary()); + actionSendEmail->setEnabled( inkabc ); + + if( singleContactSelected ) + { + actionRename->setText(i18n("Rename Contact")); + actionRemove->setText(i18n("Remove Contact")); + actionSendMessage->setText(i18n("Send Single Message...")); + actionRename->setEnabled(true); + actionRemove->setEnabled(true); + actionAddContact->setText(i18n("&Add Subcontact")); + actionAddContact->setEnabled(!contacts.first()->isTemporary()); + } + else if( groups.count() == 1 && contacts.isEmpty() ) + { + actionRename->setText(i18n("Rename Group")); + actionRemove->setText(i18n("Remove Group")); + actionSendMessage->setText(i18n("Send Message to Group")); + actionRename->setEnabled(true); + actionRemove->setEnabled(true); + actionSendMessage->setEnabled(true); + actionAddContact->setText(i18n("&Add Contact to Group")); + actionAddContact->setEnabled(groups.first()->type()==Kopete::Group::Normal); + } + else + { + actionRename->setText(i18n("Rename")); + actionRemove->setText(i18n("Remove")); + actionRename->setEnabled(false); + actionRemove->setEnabled(contacts.count()+groups.count()); + actionAddContact->setEnabled(false); + } + + actionMove->setCurrentItem( -1 ); + actionCopy->setCurrentItem( -1 ); + + actionProperties->setEnabled( ( groups.count() + contacts.count() ) == 1 ); +} + +void KopeteContactListView::slotSendMessage() +{ + Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first(); + Kopete::Group *group = Kopete::ContactList::self()->selectedGroups().first(); + if(m) + m->sendMessage(); + else + if(group) + group->sendMessage(); +} + +void KopeteContactListView::slotStartChat() +{ + Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first(); + if(m) + m->startChat(); +} + +void KopeteContactListView::slotSendFile() +{ + Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first(); + if(m) + m->sendFile(KURL()); +} + + void KopeteContactListView::slotSendEmail() +{ + //I borrowed this from slotSendMessage + Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first(); + if ( !m->metaContactId().isEmpty( ) ) // check if in kabc + { + KABC::Addressee addressee = KABC::StdAddressBook::self()->findByUid( m->metaContactId() ); + if ( !addressee.isEmpty() ) + { + QString emailAddr = addressee.fullEmail(); + + kdDebug( 14000 ) << "Email: " << emailAddr << "!" << endl; + if ( !emailAddr.isEmpty() ) + kapp->invokeMailer( emailAddr, QString::null ); + else + KMessageBox::queuedMessageBox( this, KMessageBox::Sorry, i18n( "There is no email address set for this contact in the KDE address book." ), i18n( "No Email Address in Address Book" ) ); + } + else + KMessageBox::queuedMessageBox( this, KMessageBox::Sorry, i18n( "This contact was not found in the KDE address book. Check that a contact is selected in the properties dialog." ), i18n( "Not Found in Address Book" ) ); + } + else + KMessageBox::queuedMessageBox( this, KMessageBox::Sorry, i18n( "This contact is not associated with a KDE address book entry, where the email address is stored. Check that a contact is selected in the properties dialog." ), i18n( "Not Found in Address Book" ) ); +} + +void KopeteContactListView::slotMoveToGroup() +{ + KopeteMetaContactLVI *metaLVI=dynamic_cast<KopeteMetaContactLVI*>(currentItem()); + if(!metaLVI) + return; + Kopete::MetaContact *m=metaLVI->metaContact(); + Kopete::Group *g=metaLVI->group(); + + //FIXME What if two groups have the same name? + Kopete::Group *to = actionMove->currentItem() ? + Kopete::ContactList::self()->findGroup( actionMove->currentText() ) : + Kopete::Group::topLevel(); + + if( !to || to->type() == Kopete::Group::Temporary ) + return; + + if(m->isTemporary()) + { + if( KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(), + i18n( "<qt>Would you like to add this contact to your contact list?</qt>" ), + i18n( "Kopete" ), i18n("Add"), i18n("Do Not Add"), + "addTemporaryWhenMoving" ) == KMessageBox::Yes ) + { + m->setTemporary(false,to); + + insertUndoItem( new UndoItem( UndoItem::MetaContactAdd , m ) ); + } + } + else if( !m->groups().contains( to ) ) + { + m->moveToGroup( g, to ); + + insertUndoItem( new UndoItem( UndoItem::MetaContactCopy , m , to ) ); + + UndoItem *u=new UndoItem( UndoItem::MetaContactRemove, m, g ); + u->isStep=false; + insertUndoItem(u); + } + + actionMove->setCurrentItem( -1 ); +} + +void KopeteContactListView::slotCopyToGroup() +{ + Kopete::MetaContact *m = + Kopete::ContactList::self()->selectedMetaContacts().first(); + + if(!m) + return; + + //FIXME! what if two groups have the same name? + Kopete::Group *to = actionCopy->currentItem() ? + Kopete::ContactList::self()->findGroup( actionCopy->currentText() ) : + Kopete::Group::topLevel(); + + if( !to || to->type() == Kopete::Group::Temporary ) + return; + + if( m->isTemporary() ) + return; + + if( !m->groups().contains( to ) ) + { + m->addToGroup( to ); + + insertUndoItem( new UndoItem( UndoItem::MetaContactCopy , m , to ) ); + } + + actionCopy->setCurrentItem( -1 ); +} + + + +void KopeteContactListView::slotRemove() +{ + QPtrList<Kopete::MetaContact> contacts = Kopete::ContactList::self()->selectedMetaContacts(); + QPtrList<Kopete::Group> groups = Kopete::ContactList::self()->selectedGroups(); + + if(groups.count() + contacts.count() == 0) + return; + + QStringList items; + for( Kopete::Group *it = groups.first(); it; it = groups.next() ) + { + if(!it->displayName().isEmpty()) + items.append( it->displayName() ); + } + for( Kopete::MetaContact *it = contacts.first(); it; it = contacts.next() ) + { + if(!it->displayName().isEmpty() ) + items.append( it->displayName() ); + } + + if( items.count() <= 1 ) + { + // we are deleting an empty contact + QString msg; + if( !contacts.isEmpty() ) + { + msg = i18n( "<qt>Are you sure you want to remove the contact <b>%1</b>" \ + " from your contact list?</qt>" ) + .arg( contacts.first()->displayName() ) ; + } + else if( !groups.isEmpty() ) + { + msg = i18n( "<qt>Are you sure you want to remove the group <b>%1</b> " \ + "and all contacts that are contained within it?</qt>" ) + .arg( groups.first()->displayName() ); + } + else + return; // this should never happen + + if( KMessageBox::warningContinueCancel( this, msg, i18n( "Remove" ), KGuiItem(i18n("Remove"),"editdelete") , + "askRemovingContactOrGroup" , KMessageBox::Notify | KMessageBox::Dangerous ) != + KMessageBox::Continue ) + { + return; + } + } + else + { + QString msg = groups.isEmpty() ? + i18n( "Are you sure you want to remove these contacts " \ + "from your contact list?" ) : + i18n( "Are you sure you want to remove these groups and " \ + "contacts from your contact list?" ); + + if( KMessageBox::warningContinueCancelList( this, msg, items, i18n("Remove"), + KGuiItem(i18n("Remove"),"editdelete"), "askRemovingContactOrGroup", + KMessageBox::Notify | KMessageBox::Dangerous ) != KMessageBox::Continue ) + { + return; + } + } + + bool undo_step=true; //only the first undo item we will add will be a step + + for( Kopete::MetaContact *mc = contacts.first(); mc; mc = contacts.next() ) + { + if(mc->groups().count()==1 || mc->isTemporary() ) + Kopete::ContactList::self()->removeMetaContact( mc ); + else + { + //try to guess from what group we are removing that contact. + QListViewItemIterator lvi_it( this ); + while ( lvi_it.current() ) + { + QListViewItem *item = lvi_it.current(); + ++lvi_it; + + if ( item->isSelected() ) + { + KopeteMetaContactLVI *metaLVI=dynamic_cast<KopeteMetaContactLVI*>(item); + if(metaLVI && metaLVI->metaContact() == mc ) + { + if(mc->groups().count()==1) + { + Kopete::ContactList::self()->removeMetaContact( mc ); + break; + } + else + { + mc->removeFromGroup(metaLVI->group()); + insertUndoItem( new UndoItem( UndoItem::MetaContactRemove , mc , metaLVI->group() ) ); + m_undo->isStep=undo_step; //if there is several selected contacts. + undo_step=false; + } + //let's continue, it's possible this contact is selected several times + } + } + } + } + } + + for( Kopete::Group *it = groups.first(); it; it = groups.next() ) + { + QPtrList<Kopete::MetaContact> list = it->members(); + for( list.first(); list.current(); list.next() ) + { + Kopete::MetaContact *mc = list.current(); + if(mc->groups().count()==1) + Kopete::ContactList::self()->removeMetaContact(mc); + else + mc->removeFromGroup(it); + } + + if( !it->members().isEmpty() ) + { + kdDebug(14000) << "KopeteContactListView::slotRemove(): " + << "all subMetaContacts are not removed... Aborting" << endl; + continue; + } + + Kopete::ContactList::self()->removeGroup( it ); + } +} + +void KopeteContactListView::slotRename() +{ + if ( KopeteMetaContactLVI *metaLVI = dynamic_cast<KopeteMetaContactLVI *>( currentItem() ) ) + { + metaLVI->setRenameEnabled( 0, true); + metaLVI->startRename( 0 ); + } + else if ( KopeteGroupViewItem *groupLVI = dynamic_cast<KopeteGroupViewItem *>( currentItem() ) ) + { + if ( !KopetePrefs::prefs()->sortByGroup() ) + return; + groupLVI->setRenameEnabled( 0, true); + groupLVI->startRename( 0 ); + } +} + +void KopeteContactListView::slotAddContact() +{ + if( !sender() ) + return; + + Kopete::MetaContact *metacontact = + Kopete::ContactList::self()->selectedMetaContacts().first(); + Kopete::Group *group = + Kopete::ContactList::self()->selectedGroups().first(); + Kopete::Account *account = dynamic_cast<Kopete::Account*>( sender()->parent() ); + + if ( ( metacontact && metacontact->isTemporary() ) || + (group && group->type()!=Kopete::Group::Normal ) ) + return; + + + if( account && ( metacontact || group) ) + { + KDialogBase *addDialog = new KDialogBase( this, "addDialog", true, + i18n( "Add Contact" ), KDialogBase::Ok|KDialogBase::Cancel, + KDialogBase::Ok, true ); + + AddContactPage *addContactPage = + account->protocol()->createAddContactWidget( addDialog, account ); + + if (!addContactPage) + { + kdDebug(14000) << k_funcinfo << + "Error while creating addcontactpage" << endl; + } + else + { + addDialog->setMainWidget( addContactPage ); + if( addDialog->exec() == QDialog::Accepted ) + { + if( addContactPage->validateData() ) + { + if(!metacontact) + { + metacontact = new Kopete::MetaContact(); + metacontact->addToGroup( group ); + if (addContactPage->apply( account, metacontact )) + { + Kopete::ContactList::self()->addMetaContact( metacontact ); + } + else + { + delete metacontact; + } + } + else + { + addContactPage->apply( account, metacontact ); + } + } + } + } + addDialog->deleteLater(); + } +} + +void KopeteContactListView::slotAddTemporaryContact() +{ + Kopete::MetaContact *metacontact = + Kopete::ContactList::self()->selectedMetaContacts().first(); + if( metacontact ) + { +/* int r=KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(), + i18n( "<qt>Would you like to add this contact to your contact list?</qt>" ), + i18n( "Kopete" ), i18n("Add"), i18n("Do Not Add"), + "addTemporaryWhenMoving" ); + + if(r==KMessageBox::Yes)*/ + if(metacontact->isTemporary() ) + metacontact->setTemporary( false ); + } +} + +void KopeteContactListView::slotProperties() +{ +// kdDebug(14000) << k_funcinfo << "Called" << endl; + + KopeteMetaContactLVI *metaLVI = + dynamic_cast<KopeteMetaContactLVI *>( currentItem() ); + KopeteGroupViewItem *groupLVI = + dynamic_cast<KopeteGroupViewItem *>( currentItem() ); + + if(metaLVI) + { + + KopeteMetaLVIProps *propsDialog = + new KopeteMetaLVIProps( metaLVI, 0L, "propsDialog" ); + + propsDialog->exec(); // modal + delete propsDialog; + + /* + if( metaLVI->group()->useCustomIcon() ) + { + metaLVI->updateCustomIcons( mShowAsTree ); + } + else + { + } + */ + metaLVI->repaint(); + } + else if(groupLVI) + { + KopeteGVIProps *propsDialog = + new KopeteGVIProps( groupLVI, 0L, "propsDialog"); + + propsDialog->exec(); // modal + delete propsDialog; + + groupLVI->updateIcon(); + } +} + +void KopeteContactListView::slotItemRenamed( QListViewItem */*item*/ ) +{ + //everithing is now done in KopeteMetaContactLVI::rename + +/* KopeteMetaContactLVI *metaLVI = dynamic_cast<KopeteMetaContactLVI *>( item ); + Kopete::MetaContact *m= metaLVI ? metaLVI->metaContact() : 0L ; + if ( m ) + { + m->setDisplayName( metaLVI->text( 0 ) ); + } + else + { + //group are handled differently in KopeteGroupViewItem + // kdWarning( 14000 ) << k_funcinfo << "Unknown list view item '" << item + // << "' renamed, ignoring item" << endl; + } + */ +} + +void KopeteContactListView::insertUndoItem( KopeteContactListView::UndoItem *u) +{ + u->next=m_undo; + m_undo=u; + actionUndo->setEnabled(true); + while(m_redo) + { + UndoItem *i=m_redo->next; + delete m_redo; + m_redo=i; + } + actionRedo->setEnabled(false); + undoTimer.start(10*60*1000); +} + + +void KopeteContactListView::slotUndo() +{ + bool step = false; + while(m_undo && !step) + { + bool success=false; + switch (m_undo->type) + { + case UndoItem::MetaContactAdd: + { + Kopete::MetaContact *m=m_undo->metacontact; + if(m) + { + m->setTemporary(true); + success=true; + } + break; + } + case UndoItem::MetaContactCopy: + { + Kopete::MetaContact *m=m_undo->metacontact; + Kopete::Group *to=m_undo->group; + if( m && to ) + { + m->removeFromGroup( to ); + success=true; + } + break; + } + case UndoItem::MetaContactRemove: + { + Kopete::MetaContact *m=m_undo->metacontact; + Kopete::Group *g=m_undo->group; + if( m && g ) + { + m->addToGroup( g ); + success=true; + } + break; + } + case UndoItem::MetaContactRename: + { + Kopete::MetaContact *m=m_undo->metacontact; + if( m ) + { + // make a copy + QStringList undoArgs = m_undo->args; + Kopete::MetaContact::PropertySource undoSource = m_undo->nameSource; + // set undo undo + // set the source first + m_undo->nameSource = m->displayNameSource(); + if ( m->displayNameSource() == Kopete::MetaContact::SourceCustom ) + { + m_undo->args[0] = m->customDisplayName(); + } + else if ( m->displayNameSource() == Kopete::MetaContact::SourceContact ) + { + Kopete::Contact* c = m->displayNameSourceContact(); + m_undo->args[0] = c->contactId(); + m_undo->args[1] = c->protocol()->pluginId(); + m_undo->args[2] = c->account()->accountId(); + } + // source kabc requires no arguments + + // do the undo + if ( undoSource == Kopete::MetaContact::SourceContact ) + { // do undo + Kopete::Contact *c = Kopete::ContactList::self()->findContact( undoArgs[1], undoArgs[2], undoArgs[0]); + if (!c) + { + success=false; + break; + } + // do undo + m->setDisplayNameSourceContact(c); + m->setDisplayNameSource(Kopete::MetaContact::SourceContact); + } + else if ( undoSource == Kopete::MetaContact::SourceCustom ) + { + m->setDisplayName(undoArgs[0]); + m->setDisplayNameSource(Kopete::MetaContact::SourceCustom); + } + else if ( undoSource == Kopete::MetaContact::SourceKABC ) + { + m->setDisplayNameSource(Kopete::MetaContact::SourceKABC); + } + success=true; + } + break; + } + case UndoItem::GroupRename: + { + if( m_undo->group ) + { + const QString old=m_undo->group->displayName(); + m_undo->group->setDisplayName( m_undo->args[0] ); + m_undo->args[0]=old; + success=true; + } + break; + } + case UndoItem::MetaContactChange: + { + Kopete::Contact *c=Kopete::ContactList::self()->findContact(m_undo->args[0] , m_undo->args[1], m_undo->args[2] ) ; + if(c) + { + success=true; + if(m_undo->metacontact) + c->setMetaContact(m_undo->metacontact); + else + { + Kopete::MetaContact *m=new Kopete::MetaContact; + m->addToGroup(m_undo->group); + m->setDisplayName(m_undo->args[3]); + c->setMetaContact(m); + Kopete::ContactList::self()->addMetaContact(m); + } + m_undo->metacontact=c->metaContact(); //for the redo + } + break; + } + case UndoItem::ContactAdd: + { + Kopete::Contact *c=Kopete::ContactList::self()->findContact(m_undo->args[0] , m_undo->args[1], m_undo->args[2] ) ; + if(c) + { + success=true; + Kopete::MetaContact *m=new Kopete::MetaContact; + m->setTemporary(true); + c->setMetaContact(m); + Kopete::ContactList::self()->addMetaContact(m); + m_undo->metacontact=c->metaContact(); + } + break; + } + } + + if(success) //the undo item has been correctly performed + { + step=m_undo->isStep; + UndoItem *u=m_undo->next; + m_undo->next=m_redo; + m_redo=m_undo; + m_undo=u; + } + else //something has been corrupted, clear all undo items + { + while(m_undo) + { + UndoItem *u=m_undo->next; + delete m_undo; + m_undo=u; + } + } + } + actionUndo->setEnabled(m_undo); + actionRedo->setEnabled(m_redo); + undoTimer.start(10*60*1000); +} + +void KopeteContactListView::slotRedo() +{ + bool step = false; + while(m_redo && (!step || !m_redo->isStep )) + { + bool success=false; + switch (m_redo->type) + { + case UndoItem::MetaContactAdd: + { + Kopete::MetaContact *m=m_redo->metacontact; + if(m && m_redo->group) + { + m->setTemporary(false,m_redo->group); + success=true; + } + break; + } + case UndoItem::MetaContactCopy: + { + Kopete::MetaContact *m=m_redo->metacontact; + Kopete::Group *to=m_redo->group; + if( m && to ) + { + m->addToGroup( to ); + success=true; + } + break; + } + case UndoItem::MetaContactRemove: + { + Kopete::MetaContact *m=m_redo->metacontact; + Kopete::Group *g=m_redo->group; + if( m && g ) + { + m->removeFromGroup( g ); + success=true; + } + break; + } + case UndoItem::MetaContactRename: + { + /* + Kopete::MetaContact *m=m_redo->metacontact; + if( m ) + { + const QString old=m->displayName(); + if( m_redo->args[1].isEmpty() ) + { + const QString name = m_redo->args[0]; + m_redo->args[0] = m->displayNameSource()->contactId(); + m_redo->args[1] = m->displayNameSource()->protocol()->pluginId(); + m_redo->args[2] = m->displayNameSource()->account()->accountId(); + m->setDisplayName( name ); + } + else + { + const QString oldName = m->displayName(); + QPtrList< Kopete::Contact > cList = m->contacts(); + QPtrListIterator< Kopete::Contact > it (cList); + Kopete::Contact *c = Kopete::ContactList::self()->findContact( args[0], args[2], args[1]); + if ( !c) + return; + m->setNameSourceContact(c); + break; + + m_redo->args[0] = oldName; + m_redo->args[1] = ""; + m_redo->args[2] = ""; + } + success=true; + } + */ //Why is this code commented ? - Olivier 2006-04 + break; + } + case UndoItem::GroupRename: + { + if( m_redo->group ) + { + const QString old=m_redo->group->displayName(); + m_redo->group->setDisplayName( m_redo->args[0] ); + m_redo->args[0]=old; + success=true; + } + break; + } + case UndoItem::MetaContactChange: + case UndoItem::ContactAdd: + { + Kopete::Contact *c=Kopete::ContactList::self()->findContact(m_redo->args[0] , m_redo->args[1], m_redo->args[2] ) ; + if(c && m_redo->metacontact) + { + success=true; + c->setMetaContact(m_redo->metacontact); + m_redo->metacontact=c->metaContact(); + } + break; + } + } + + if(success) //the undo item has been correctly performed + { + step=true; + UndoItem *u=m_redo->next; + m_redo->next=m_undo; + m_undo=m_redo; + m_redo=u; + } + else //something has been corrupted, clear all undo items + { + while(m_redo) + { + UndoItem *u=m_redo->next; + delete m_redo; + m_redo=u; + } + } + } + actionUndo->setEnabled(m_undo); + actionRedo->setEnabled(m_redo); + undoTimer.start(10*60*1000); +} + +void KopeteContactListView::slotTimeout() +{ + undoTimer.stop(); + + //we will keep one (complete) undo action + UndoItem *Sdel=m_undo; + while(Sdel && !Sdel->isStep) + Sdel=Sdel->next; + + if(Sdel) while( Sdel->next ) + { + UndoItem *u=Sdel->next->next; + delete Sdel->next; + Sdel->next=u; + } + actionUndo->setEnabled(m_undo); + while(m_redo) + { + UndoItem *i=m_redo->next; + delete m_redo; + m_redo=i; + } + actionRedo->setEnabled(false); +} + +#include "kopetecontactlistview.moc" + +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/kopete/contactlist/kopetecontactlistview.h b/kopete/kopete/contactlist/kopetecontactlistview.h new file mode 100644 index 00000000..43c60ebe --- /dev/null +++ b/kopete/kopete/contactlist/kopetecontactlistview.h @@ -0,0 +1,252 @@ +/* + kopetecontactlistview.h + + Kopete Contactlist GUI + + Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org> + Copyright (c) 2002 by Nick Betcher <nbetcher@usinternet.com> + Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net> + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KOPETE_CONTACTLISTVIEW_H +#define KOPETE_CONTACTLISTVIEW_H + +#include "kopetelistview.h" +#include "kopetemetacontact.h" + +#include <qpixmap.h> +#include <qptrlist.h> +#include <qstringlist.h> +#include <qrect.h> +#include <qtimer.h> +#include <qguardedptr.h> + +class KopeteMetaContactLVI; +class KopeteGroupViewItem; +class KopeteStatusGroupViewItem; +class KRootPixmap; +class KActionCollection; +class KAction; +class KListAction; +class KActionMenu; + +class KopeteContactListViewPrivate; + +namespace Kopete +{ +class Contact; +class MetaContact; +class Group; +class MessageEvent; +} + +/** + * @author Duncan Mac-Vicar P. <duncan@kde.org> + */ +class KopeteContactListView : public Kopete::UI::ListView::ListView +{ + Q_OBJECT + +public: + KopeteContactListView( QWidget *parent = 0, const char *name = 0 ); + ~KopeteContactListView(); + + /** + * Init MetaContact related actions + */ + void initActions(KActionCollection*); + + /** + * Add a given group name and return it + */ + void addGroup( const QString &groupName ); + + /** + * Are we displaying as a tree view (true), or in a flat list (false)? + * @todo make this an enum + */ + bool showAsTree() { return mShowAsTree; } + +public slots: + /** + * Remove all KopeteMetaContactLVI of a metaContact + */ + void removeContact( Kopete::MetaContact *contact ); + + /** + * Prompt the user for the group name (slot) + */ + void addGroup(); + +protected: + virtual void contentsMousePressEvent( QMouseEvent *e ); + + virtual bool acceptDrag(QDropEvent *e) const; + + /** + * Start a drag operation + * @return a KMultipleDrag containing: 1) A QStoredDrag of type "application/x-qlistviewitem", 2) If the MC is associated with a KABC entry, i) a QTextDrag containing their email address, and ii) their vCard representation. + */ + virtual QDragObject *dragObject(); + + /** + * Since KDE 3.1.1 , the original find Drop return 0L for afterme if the group is open. + * This woraround allow us to keep the highlight of the item, and give always a correct position + */ + virtual void findDrop(const QPoint &pos, QListViewItem *&parent, QListViewItem *&after); + + /** + * The selected items have changed; update our actions to show what's possible. + */ + void updateActionsForSelection( QPtrList<Kopete::MetaContact> contacts, QPtrList<Kopete::Group> groups ); + +private slots: + /** + * When an account is added, so we add it to the menu action + */ + void slotAddSubContactActionNewAccount(Kopete::Account*); + /** + * When an account is destroyed, the child add subcontact action is deleted + * so we remove it from the menu action + */ + void slotAddSubContactActionAccountDeleted(const Kopete::Account *); + + void slotViewSelectionChanged(); + void slotListSelectionChanged(); + void slotContextMenu(KListView*,QListViewItem *item, const QPoint &point ); + void slotExpanded( QListViewItem *item ); + void slotCollapsed( QListViewItem *item ); + + void slotSettingsChanged( void ); + void slotUpdateAllGroupIcons(); + void slotExecuted( QListViewItem *item, const QPoint &pos, int c ); + + void slotAddedToGroup( Kopete::MetaContact *mc, Kopete::Group *to ); + void slotRemovedFromGroup( Kopete::MetaContact *mc, Kopete::Group *from ); + void slotMovedToGroup( Kopete::MetaContact *mc, Kopete::Group *from, Kopete::Group *to ); + + /** + * A meta contact was added to the contact list - update the view + */ + void slotMetaContactAdded( Kopete::MetaContact *mc ); + void slotMetaContactDeleted( Kopete::MetaContact *mc ); + void slotMetaContactSelected( bool sel ); + + void slotGroupAdded(Kopete::Group *); + + void slotContactStatusChanged( Kopete::MetaContact *mc ); + + void slotDropped(QDropEvent *e, QListViewItem *parent, QListViewItem*); + + void slotShowAddContactDialog(); + void slotNewMessageEvent(Kopete::MessageEvent *); + + /** + * Handle renamed items by renaming the meta contact + */ + void slotItemRenamed( QListViewItem *item ); + + /** Actions related slots **/ + void slotSendMessage(); + void slotStartChat(); + void slotSendFile(); + void slotSendEmail(); + void slotMoveToGroup(); + void slotCopyToGroup(); + void slotRemove(); + void slotRename(); + void slotAddContact(); + void slotAddTemporaryContact(); + void slotProperties(); + void slotUndo(); + void slotRedo(); + + void slotTimeout(); + +private: + bool mShowAsTree; + + // TODO: do we really need to store these? + QPtrList<KopeteMetaContactLVI> m_selectedContacts; + QPtrList<KopeteGroupViewItem> m_selectedGroups; + + bool mSortByGroup; + KRootPixmap *root; + + QRect m_onItem; + + QPoint m_startDragPos; + + /* ACTIONS */ + KAction *actionSendMessage; + KAction *actionStartChat; + KAction *actionSendFile; + KAction *actionSendEmail; + KListAction *actionMove; + KListAction *actionCopy; + KAction *actionRename; + KAction *actionRemove; + KAction *actionAddTemporaryContact; + KAction *actionProperties; + KAction *actionUndo; + KAction *actionRedo; + + KopeteContactListViewPrivate *d; + + void moveDraggedContactToGroup( Kopete::MetaContact *contact, Kopete::Group *from, Kopete::Group *to ); + void addDraggedContactToGroup( Kopete::MetaContact *contact, Kopete::Group *group ); + void addDraggedContactToMetaContact( Kopete::Contact *contact, Kopete::MetaContact *parent ); + void addDraggedContactByInfo( const QString &protocolId, const QString &accountId, + const QString &contactId, QListViewItem *after ); + +public: + struct UndoItem; + UndoItem *m_undo; + UndoItem *m_redo; + void insertUndoItem(UndoItem *u); + QTimer undoTimer; + +public: + // This is public so the chatwinodw can handle sub actions + // FIXME: do we not believe in accessor functions any more? + KActionMenu *actionAddContact; + QMap<const Kopete::Account *, KAction *> m_accountAddContactMap; +}; + +struct KopeteContactListView::UndoItem +{ + enum Type { MetaContactAdd, MetaContactRemove , MetaContactCopy , MetaContactRename, MetaContactChange, ContactAdd, GroupRename } type; + QStringList args; + QGuardedPtr<Kopete::MetaContact> metacontact; + QGuardedPtr<Kopete::Group> group; + UndoItem *next; + bool isStep; + Kopete::MetaContact::PropertySource nameSource; + + UndoItem() : isStep(true) {} + UndoItem(Type t, Kopete::MetaContact *m=0L ,Kopete::Group *g=0L) + { + isStep=true; + type=t; + metacontact=m; + group=g; + next=0L; + } +}; + + +#endif +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/kopete/contactlist/kopetegrouplistaction.cpp b/kopete/kopete/contactlist/kopetegrouplistaction.cpp new file mode 100644 index 00000000..1556f9b6 --- /dev/null +++ b/kopete/kopete/contactlist/kopetegrouplistaction.cpp @@ -0,0 +1,66 @@ +/* + kopetegrouplistcation.cpp - the action used for Move To and copy To + + Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org> + + Kopete (c) 2001-2005 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. * + * * + ************************************************************************* +*/ +/* This code was previously in libkopete/ui/kopetestdactions.cpp */ + +#include "kopetegrouplistaction.h" + +#include <kdebug.h> +#include <kguiitem.h> +#include <klocale.h> +#include <kaction.h> +#include <kwin.h> +#include <kcmultidialog.h> + +#include "kopetecontactlist.h" +#include "kopetegroup.h" + +KopeteGroupListAction::KopeteGroupListAction( const QString &text, const QString &pix, const KShortcut &cut, const QObject *receiver, + const char *slot, QObject *parent, const char *name ) +: KListAction( text, pix, cut, parent, name ) +{ + connect( this, SIGNAL( activated() ), receiver, slot ); + + connect( Kopete::ContactList::self(), SIGNAL( groupAdded( Kopete::Group * ) ), this, SLOT( slotUpdateList() ) ); + connect( Kopete::ContactList::self(), SIGNAL( groupRemoved( Kopete::Group * ) ), this, SLOT( slotUpdateList() ) ); + connect( Kopete::ContactList::self(), SIGNAL( groupRenamed(Kopete::Group*, const QString& ) ), this, SLOT( slotUpdateList() ) ); + slotUpdateList(); +} + +KopeteGroupListAction::~KopeteGroupListAction() +{ +} + +void KopeteGroupListAction::slotUpdateList() +{ + QStringList groupList; + + // Add groups to our list + QPtrList<Kopete::Group> groups = Kopete::ContactList::self()->groups(); + for ( Kopete::Group *it = groups.first(); it; it = groups.next() ) + { + if(it->type() == Kopete::Group::Normal) + groupList.append( it->displayName() ); + } + + groupList.sort(); + groupList.prepend(QString::null); //add a separator; + groupList.prepend( i18n("Top Level") ); //the top-level group, with the id 0 + setItems( groupList ); +} + +#include "kopetegrouplistaction.moc" diff --git a/kopete/kopete/contactlist/kopetegrouplistaction.h b/kopete/kopete/contactlist/kopetegrouplistaction.h new file mode 100644 index 00000000..3576f3ed --- /dev/null +++ b/kopete/kopete/contactlist/kopetegrouplistaction.h @@ -0,0 +1,44 @@ +/* + kopetegrouplistaction.h + + Copyright (c) 2005 Olivier Goffart <ogoffart@ kde.org> + + + Kopete (c) 2005 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. * + * * + ************************************************************************* +*/ + +#ifndef KOPETEGRLISTACT_H +#define KOPETEGRLISTACT_H + + +#include <kaction.h> + + +/** + * Action used for Copy To and Move To + */ +class KopeteGroupListAction : public KListAction +{ + Q_OBJECT + +public: + KopeteGroupListAction( const QString &, const QString &, const KShortcut &, + const QObject *, const char *, QObject *, const char * ); + ~KopeteGroupListAction(); + +protected slots: + void slotUpdateList(); +private: + QStringList m_groupList; +}; + +#endif diff --git a/kopete/kopete/contactlist/kopetegroupviewitem.cpp b/kopete/kopete/contactlist/kopetegroupviewitem.cpp new file mode 100644 index 00000000..21b1cf79 --- /dev/null +++ b/kopete/kopete/contactlist/kopetegroupviewitem.cpp @@ -0,0 +1,286 @@ +/* + kopeteonlinestatus.cpp - Kopete Online Status + + Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2003 by Martijn Klingens <klingens@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qpainter.h> + +#include <klocale.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <kapplication.h> + +#include "kopetecontactlistview.h" +#include "kopetegroupviewitem.h" +#include "kopetegroup.h" +#include "kopeteonlinestatus.h" +#include "kopeteprefs.h" +#include "kopetemetacontactlvi.h" +#include "kopetemetacontact.h" + +#include <memory> + +//using namespace Kopete::UI; + +class KopeteGroupViewItem::Private +{ +public: + Kopete::UI::ListView::ImageComponent *image; + Kopete::UI::ListView::DisplayNameComponent *name; + Kopete::UI::ListView::TextComponent *count; + std::auto_ptr<Kopete::UI::ListView::ToolTipSource> toolTipSource; +}; + +namespace Kopete { +namespace UI { +namespace ListView { + +class GroupToolTipSource : public ToolTipSource +{ +public: + GroupToolTipSource( KopeteGroupViewItem *gp ) + : group( gp ) + { + } + QString operator()( ComponentBase *, const QPoint &, QRect & ) + { + return group->toolTip(); + } +private: + KopeteGroupViewItem *group; +}; + +} // END namespace ListView +} // END namespace UI +} // END namespace Kopete + +KopeteGroupViewItem::KopeteGroupViewItem( Kopete::Group *group_, QListView *parent, const char *name ) +: Kopete::UI::ListView::Item( parent, group_, name ) +{ + m_group = group_; + initLVI(); +} + +KopeteGroupViewItem::KopeteGroupViewItem( Kopete::Group *group_, QListViewItem *parent, const char *name ) + : Kopete::UI::ListView::Item( parent, group_, name ) +{ + m_group = group_; + initLVI(); +} + +KopeteGroupViewItem::~KopeteGroupViewItem() +{ + delete d; +} + +void KopeteGroupViewItem::initLVI() +{ + d = new Private; + + d->toolTipSource.reset( new Kopete::UI::ListView::GroupToolTipSource( this ) ); + + using namespace Kopete::UI::ListView; + Component *hbox = new BoxComponent( this, BoxComponent::Horizontal ); + d->image = new ImageComponent( hbox ); + d->name = new DisplayNameComponent( hbox ); + d->count = new TextComponent( hbox ); + + d->image->setToolTipSource( d->toolTipSource.get() ); + d->name->setToolTipSource( d->toolTipSource.get() ); + d->count->setToolTipSource( d->toolTipSource.get() ); + + connect( m_group, SIGNAL( displayNameChanged( Kopete::Group*, const QString& ) ), + this, SLOT( refreshDisplayName() ) ); + + connect( KopetePrefs::prefs(), SIGNAL( contactListAppearanceChanged() ), + SLOT( slotConfigChanged() ) ); + connect( kapp, SIGNAL( appearanceChanged() ), SLOT( slotConfigChanged() ) ); + + connect( m_group, SIGNAL( iconAppearanceChanged() ), SLOT( updateIcon() ) ); + + refreshDisplayName(); + slotConfigChanged(); +} + +Kopete::Group* KopeteGroupViewItem::group() const +{ + return m_group; +} + +QString KopeteGroupViewItem::toolTip() const +{ + // TODO: add icon, and some more information than that which + // is already displayed in the list view item + // FIXME: post-KDE-3.3, make this better i18n-able + // currently it can't cause more problems than the contact list itself, at least. + return "<b>" + d->name->text() + "</b> " + d->count->text(); +} + +void KopeteGroupViewItem::slotConfigChanged() +{ + updateIcon(); + updateVisibility(); + + d->name->setColor( KopetePrefs::prefs()->contactListGroupNameColor() ); + + QFont font = listView()->font(); + if ( KopetePrefs::prefs()->contactListUseCustomFonts() ) + font = KopetePrefs::prefs()->contactListCustomNormalFont(); + d->name->setFont( font ); + + d->count->setFont( KopetePrefs::prefs()->contactListSmallFont() ); +} + +void KopeteGroupViewItem::refreshDisplayName() +{ + totalMemberCount = 0; + onlineMemberCount = 0; + + for ( QListViewItem *lvi = firstChild(); lvi; lvi = lvi->nextSibling() ) + { + if ( KopeteMetaContactLVI *kc = dynamic_cast<KopeteMetaContactLVI*>( lvi ) ) + { + totalMemberCount++; + if ( kc->metaContact()->isOnline() ) + onlineMemberCount++; + } + } + + d->name->setText( m_group->displayName() ); + d->count->setText( i18n( "(NUMBER OF ONLINE CONTACTS/NUMBER OF CONTACTS IN GROUP)", "(%1/%2)" ) + .arg( QString::number( onlineMemberCount ), QString::number( totalMemberCount ) ) ); + + updateVisibility(); + + // Sorting in this slot is extremely expensive as it's called dozens of times and + // the sorting itself is rather slow. Therefore we call delayedSort, which tries + // to group multiple sort requests into one. + using namespace Kopete::UI::ListView; + if ( ListView::ListView *lv = dynamic_cast<ListView::ListView *>( listView() ) ) + lv->delayedSort(); + else + listView()->sort(); +} + +QString KopeteGroupViewItem::key( int, bool ) const +{ + //Groups are placed after topLevel contact. + //Exepted Temporary group which is the first group + if ( group()->type() != Kopete::Group::Normal ) + return "0" + d->name->text(); + return "M" + d->name->text(); +} + +void KopeteGroupViewItem::startRename( int /*col*/ ) +{ + //kdDebug(14000) << k_funcinfo << endl; + KListViewItem::startRename( 0 ); +} + +void KopeteGroupViewItem::okRename( int col ) +{ + //kdDebug(14000) << k_funcinfo << endl; + KListViewItem::okRename(col); + setRenameEnabled( 0, false ); +} + +void KopeteGroupViewItem::cancelRename( int col ) +{ + //kdDebug(14000) << k_funcinfo << endl; + KListViewItem::cancelRename(col); + setRenameEnabled( 0, false ); +} + +void KopeteGroupViewItem::updateVisibility() +{ + //FIXME: A contact can ve visible if he has a unknwon status (it's not online) + // or if he has an event (blinking icon). If such as contact is not with + // others inline contact in the group. the group will stay hidden. + int visibleUsers = onlineMemberCount; + if ( KopetePrefs::prefs()->showOffline() ) + visibleUsers = totalMemberCount; + + bool visible = KopetePrefs::prefs()->showEmptyGroups() || ( visibleUsers > 0 ); + + if ( isVisible() != visible ) + { + setTargetVisibility( visible ); + if ( visible ) + { + // When calling setVisible(true) EVERY child item will be shown, + // even if they should be hidden. + // We just re-update the visibility of all child items + for ( QListViewItem *lvi = firstChild(); lvi; lvi = lvi->nextSibling() ) + { + if ( KopeteMetaContactLVI *kmc = dynamic_cast<KopeteMetaContactLVI *>( lvi ) ) + kmc->updateVisibility(); + } + } + } +} + +void KopeteGroupViewItem::updateIcon() +{ + // TODO: clever caching + if ( isOpen() ) + { + if ( group()->useCustomIcon() && !group()->icon( Kopete::ContactListElement::Open ).isEmpty() ) + open = SmallIcon( group()->icon( Kopete::ContactListElement::Open ) ); + else + open = SmallIcon( KOPETE_GROUP_DEFAULT_OPEN_ICON ); + + d->image->setPixmap( open ); + } + else + { + if ( group()->useCustomIcon() && !group()->icon( Kopete::ContactListElement::Closed ).isEmpty() ) + closed = SmallIcon( group()->icon( Kopete::ContactListElement::Closed ) ); + else + closed = SmallIcon( KOPETE_GROUP_DEFAULT_CLOSED_ICON ); + + d->image->setPixmap( closed ); + } +} + +QString KopeteGroupViewItem::text( int column ) const +{ + if ( column == 0 ) + return d->name->text(); + else + return KListViewItem::text( column ); +} + +void KopeteGroupViewItem::setText( int column, const QString &text ) +{ + if ( column == 0 ) + { + KopeteContactListView *lv = dynamic_cast<KopeteContactListView *>( listView() ); + if ( lv ) + { + KopeteContactListView::UndoItem *u=new KopeteContactListView::UndoItem(KopeteContactListView::UndoItem::GroupRename, 0L, m_group); + u->args << m_group->displayName(); + lv->insertUndoItem(u); + } + group()->setDisplayName( text ); + } + else + KListViewItem::setText( column, text ); +} + +#include "kopetegroupviewitem.moc" +// vim: set noet ts=4 sts=4 sw=4: + + diff --git a/kopete/kopete/contactlist/kopetegroupviewitem.h b/kopete/kopete/contactlist/kopetegroupviewitem.h new file mode 100644 index 00000000..777ea2e8 --- /dev/null +++ b/kopete/kopete/contactlist/kopetegroupviewitem.h @@ -0,0 +1,82 @@ +/*************************************************************************** + kopetegroupviewitem.h - description + ------------------- + begin : lun oct 28 2002 + copyright : (C) 2002 by Olivier Goffart + email : ogoffart @ kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KOPETEGROUPVIEWITEM_H +#define KOPETEGROUPVIEWITEM_H + +#include "kopetelistviewitem.h" +#include <qpixmap.h> + +#define KOPETE_GROUP_DEFAULT_OPEN_ICON "folder_open" +#define KOPETE_GROUP_DEFAULT_CLOSED_ICON "folder" + +namespace Kopete +{ +class Group; +} + +/** + * @author Olivier Goffart + */ +class KopeteGroupViewItem : public Kopete::UI::ListView::Item +{ + Q_OBJECT +public: + KopeteGroupViewItem( Kopete::Group *group , QListView *parent, const char *name = 0 ); + KopeteGroupViewItem( Kopete::Group *group , QListViewItem *parent, const char *name = 0 ); + ~KopeteGroupViewItem(); + + Kopete::Group * group() const; + + virtual void startRename( int col ); + + /** + * reimplemented from KListViewItem to take into account our alternate text storage + */ + virtual QString text( int column ) const; + virtual void setText( int column, const QString &text ); + + QString toolTip() const; + +public slots: + void refreshDisplayName(); + void updateIcon(); + void updateVisibility(); + +protected: + virtual void okRename( int col ); + virtual void cancelRename( int col ); + +private: + void initLVI(); + + Kopete::Group *m_group; + QPixmap open, closed; + + QString key( int column, bool ascending ) const; + + unsigned int onlineMemberCount; + unsigned int totalMemberCount; + + class Private; + Private *d; + +private slots: + void slotConfigChanged(); +}; + +#endif diff --git a/kopete/kopete/contactlist/kopetegvipropswidget.ui b/kopete/kopete/contactlist/kopetegvipropswidget.ui new file mode 100644 index 00000000..4dbb1599 --- /dev/null +++ b/kopete/kopete/contactlist/kopetegvipropswidget.ui @@ -0,0 +1,156 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KopeteGVIPropsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KopeteGVIPropsWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>220</width> + <height>223</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabWidget</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&General</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>lblDisplayName</cstring> + </property> + <property name="text"> + <string>&Name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>edtDisplayName</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>edtDisplayName</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>grpIcons</cstring> + </property> + <property name="title"> + <string>Icons</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KIconButton" row="1" column="1"> + <property name="name"> + <cstring>icnbOpen</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>lblOpen</cstring> + </property> + <property name="text"> + <string>O&pen:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>icnbOpen</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>lblClosed</cstring> + </property> + <property name="text"> + <string>C&losed:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>icnbClosed</cstring> + </property> + </widget> + <widget class="KIconButton" row="2" column="1"> + <property name="name"> + <cstring>icnbClosed</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>chkUseCustomIcons</cstring> + </property> + <property name="text"> + <string>Use custom &icons</string> + </property> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacerHorizontal1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + </spacer> + </grid> + </widget> + </vbox> + </widget> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>tabWidget</tabstop> + <tabstop>edtDisplayName</tabstop> + <tabstop>chkUseCustomIcons</tabstop> + <tabstop>icnbOpen</tabstop> + <tabstop>icnbClosed</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kicondialog.h</includehint> + <includehint>kicondialog.h</includehint> +</includehints> +</UI> diff --git a/kopete/kopete/contactlist/kopetelviprops.cpp b/kopete/kopete/contactlist/kopetelviprops.cpp new file mode 100644 index 00000000..bf5431bf --- /dev/null +++ b/kopete/kopete/contactlist/kopetelviprops.cpp @@ -0,0 +1,570 @@ +/* + kopetelviprops.cpp + + Kopete Contactlist Properties GUI for Groups and MetaContacts + + Copyright (c) 2002-2003 by Stefan Gehn <metz AT gehn.net> + Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk> + Copyright (c) 2004-2005 by Duncan Mac-Vicar P. <duncan@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "kopetelviprops.h" + +#include <kdebug.h> + +#include <qapplication.h> +#include <qcheckbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qtabwidget.h> +#include <qcombobox.h> + +#include <kdialogbase.h> +#include <kfiledialog.h> +#include <kicondialog.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kurlrequester.h> +#include <kabc/addresseedialog.h> +#include <kabc/stdaddressbook.h> +#include <kabc/addressee.h> +#include <kstandarddirs.h> +#include <kurlrequester.h> + +#include "kabcpersistence.h" +#include "kopeteaddrbookexport.h" +#include "kopetecontact.h" +#include "kopetegroup.h" +#include "kopetegroupviewitem.h" +#include "kopetemetacontactlvi.h" +#include "kopeteaccount.h" +#include "kopeteprotocol.h" +#include "addressbooklinkwidget.h" +#include "addressbookselectordialog.h" + +#include "customnotificationprops.h" +#include "customnotifications.h" + +const char MC_OFF[] = "metacontact_offline"; +const char MC_ON[] = "metacontact_online"; +const char MC_AW[] = "metacontact_away"; +const char MC_UNK[] = "metacontact_unknown"; + + +KopeteGVIProps::KopeteGVIProps(KopeteGroupViewItem *gvi, QWidget *parent, const char *name) +: KDialogBase(parent, name, true, i18n("Properties of Group %1").arg(gvi->group()->displayName()), Ok|Cancel, Ok, false) +{ + mainWidget = new KopeteGVIPropsWidget(this, "mainWidget"); + mainWidget->icnbOpen->setIconSize(KIcon::SizeSmall); + mainWidget->icnbClosed->setIconSize(KIcon::SizeSmall); + + mNotificationProps = new CustomNotificationProps( this, gvi->group() ); + mainWidget->tabWidget->addTab( mNotificationProps->widget(), i18n( "Custom &Notifications" ) ); + + setMainWidget(mainWidget); + item = gvi; + m_dirty = false; + + mainWidget->edtDisplayName->setText( item->group()->displayName() ); + + mainWidget->chkUseCustomIcons->setChecked( item->group()->useCustomIcon() ); + + QString openName = item->group()->icon( Kopete::ContactListElement::Open ); + if(openName.isEmpty()) + openName = KOPETE_GROUP_DEFAULT_OPEN_ICON; + QString closeName = item->group()->icon( Kopete::ContactListElement::Closed ); + if(closeName.isEmpty()) + closeName = KOPETE_GROUP_DEFAULT_CLOSED_ICON; + mainWidget->icnbOpen->setIcon( openName ); + mainWidget->icnbClosed->setIcon( closeName ); + + connect( this, SIGNAL(okClicked()), this, SLOT( slotOkClicked() ) ); + connect( mainWidget->chkUseCustomIcons, SIGNAL( toggled( bool ) ), + this, SLOT( slotUseCustomIconsToggled( bool ) ) ); + connect( mainWidget->icnbOpen, SIGNAL( iconChanged( QString ) ), + SLOT( slotIconChanged() ) ); + connect( mainWidget->icnbClosed, SIGNAL( iconChanged( QString ) ), + SLOT( slotIconChanged() ) ); + slotUseCustomIconsToggled( mainWidget->chkUseCustomIcons->isChecked() ); +} + +KopeteGVIProps::~KopeteGVIProps() +{ +} + +void KopeteGVIProps::slotOkClicked() +{ + if( mainWidget->edtDisplayName->text() != item->group()->displayName() ) + { + item->group()->setDisplayName( mainWidget->edtDisplayName->text() ); + item->refreshDisplayName(); + } + + item->group()->setUseCustomIcon( mainWidget->chkUseCustomIcons->isChecked() ); + + // only call setIcon if the icon was changed + if( m_dirty ) + { + item->group()->setIcon( mainWidget->icnbOpen->icon(), + Kopete::ContactListElement::Open ); + + item->group()->setIcon( mainWidget->icnbClosed->icon(), + Kopete::ContactListElement::Closed ); + } + + mNotificationProps->storeCurrentCustoms(); +} + +void KopeteGVIProps::slotUseCustomIconsToggled(bool on) +{ + mainWidget->lblOpen->setEnabled( on ); + mainWidget->icnbOpen->setEnabled( on ); + mainWidget->lblClosed->setEnabled( on ); + mainWidget->icnbClosed->setEnabled( on ); +} + +void KopeteGVIProps::slotIconChanged() +{ + m_dirty = true; +} + +// ============================================================================= + + +KopeteMetaLVIProps::KopeteMetaLVIProps(KopeteMetaContactLVI *lvi, QWidget *parent, const char *name) +: KDialogBase(parent, name, true, i18n("Properties of Meta Contact %1").arg(lvi->metaContact()->displayName()), Ok|Cancel, Ok, false) +{ + m_countPhotoCapable = 0; + mainWidget = new KopeteMetaLVIPropsWidget( this, "mainWidget" ); + mainWidget->icnbOffline->setIconSize( KIcon::SizeSmall ); + mainWidget->icnbOnline->setIconSize( KIcon::SizeSmall ); + mainWidget->icnbAway->setIconSize( KIcon::SizeSmall ); + mainWidget->icnbUnknown->setIconSize( KIcon::SizeSmall ); + + mNotificationProps = new CustomNotificationProps( this, lvi->metaContact() ); + // add a button to the notification props to get the sound from KABC + // the widget's vert box layout, horiz box layout containing button, spacer, followed by a spacer + QBoxLayout * vb = static_cast<QVBoxLayout*>( mNotificationProps->widget()->layout() ); + + QHBoxLayout* hb = new QHBoxLayout( vb, -1, "soundFromKABClayout" ); + mFromKABC = new QPushButton( i18n( "Sync KABC..." ), mNotificationProps->widget(), "getSoundFromKABC" ); + hb->addWidget( mFromKABC ); // [ [Button] <-xxxxx-> ] + hb->addStretch(); + vb->addStretch(); // vert spacer keeps the rest snug + + mainWidget->tabWidget->addTab( mNotificationProps->widget(), i18n( "Custom &Notifications" ) ); + setMainWidget( mainWidget ); + item = lvi; + + connect( mainWidget->radioNameKABC, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets())); + connect( mainWidget->radioNameContact, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets())); + connect( mainWidget->radioNameCustom, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets())); + connect( mainWidget->radioPhotoKABC, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets())); + connect( mainWidget->radioPhotoContact, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets())); + connect( mainWidget->radioPhotoCustom, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets())); + connect( mainWidget->cmbPhotoUrl, SIGNAL(urlSelected(const QString &)), SLOT(slotEnableAndDisableWidgets())); + connect( mainWidget->cmbAccountPhoto, SIGNAL(activated ( int )), SLOT(slotEnableAndDisableWidgets())); + + + mainWidget->btnClearPhoto->setIconSet( SmallIconSet( QApplication::reverseLayout() ? "locationbar_erase" : "clear_left" ) ); + connect( mainWidget->btnClearPhoto, SIGNAL( clicked() ), this, SLOT( slotClearPhotoClicked() ) ); + connect( mainWidget->widAddresseeLink, SIGNAL( addresseeChanged( const KABC::Addressee & ) ), SLOT( slotAddresseeChanged( const KABC::Addressee & ) ) ); + mainWidget->chkUseCustomIcons->setChecked( item->metaContact()->useCustomIcon() ); + + QString offlineName = item->metaContact()->icon( Kopete::ContactListElement::Offline ); + if(offlineName.isEmpty()) + offlineName = QString::fromLatin1(MC_OFF); // Default + + QString onlineName = item->metaContact()->icon( Kopete::ContactListElement::Online ); + if(onlineName.isEmpty()) + onlineName = QString::fromLatin1(MC_ON); // Default + + QString awayName = item->metaContact()->icon( Kopete::ContactListElement::Away ); + if(awayName.isEmpty()) + awayName = QString::fromLatin1(MC_AW); // Default + + QString unknownName = item->metaContact()->icon( Kopete::ContactListElement::Unknown ); + if(unknownName.isEmpty()) + unknownName = QString::fromLatin1(MC_UNK); // Default + + mainWidget->icnbOffline->setIcon( offlineName ); + mainWidget->icnbOnline->setIcon( onlineName ); + mainWidget->icnbAway->setIcon( awayName ); + mainWidget->icnbUnknown->setIcon( unknownName ); + + mainWidget->widAddresseeLink->setMetaContact( lvi->metaContact() ); + + mAddressBookUid = item->metaContact()->metaContactId(); + + mExport = 0L; + + if ( !mAddressBookUid.isEmpty() ) + { + KABC::AddressBook *ab = Kopete::KABCPersistence::self()->addressBook(); + KABC::Addressee a = ab->findByUid( mAddressBookUid ); + mainWidget->widAddresseeLink->setAddressee( a ); + + if ( !a.isEmpty() ) + { + mainWidget->btnImportKABC->setEnabled( true ); + mainWidget->btnExportKABC->setEnabled( true ); + mExport = new KopeteAddressBookExport( this, item->metaContact() ); + + mSound = a.sound(); + mFromKABC->setEnabled( !( mSound.isIntern() || mSound.url().isEmpty() ) ); + } + } + + slotLoadNameSources(); + slotLoadPhotoSources(); + + connect( this, SIGNAL(okClicked()), this, SLOT( slotOkClicked() ) ); + connect( mainWidget->chkUseCustomIcons, SIGNAL( toggled( bool ) ), + this, SLOT( slotUseCustomIconsToggled( bool ) ) ); + connect( mainWidget->btnImportKABC, SIGNAL( clicked() ), + this, SLOT( slotImportClicked() ) ); + connect( mainWidget->btnExportKABC, SIGNAL( clicked() ), + this, SLOT( slotExportClicked() ) ); + connect( mFromKABC, SIGNAL( clicked() ), + this, SLOT( slotFromKABCClicked() ) ); + connect( mNotificationProps->widget()->customSound, SIGNAL( openFileDialog( KURLRequester * )), + SLOT( slotOpenSoundDialog( KURLRequester * ))); + + slotUseCustomIconsToggled( mainWidget->chkUseCustomIcons->isChecked() ); + slotEnableAndDisableWidgets(); +} + +KopeteMetaLVIProps::~KopeteMetaLVIProps() +{ +} + + +void KopeteMetaLVIProps::slotLoadNameSources() +{ + Kopete::Contact* trackingName = item->metaContact()->displayNameSourceContact(); + QPtrList< Kopete::Contact > cList = item->metaContact()->contacts(); + QPtrListIterator<Kopete::Contact> it( cList ); + mainWidget->cmbAccountName->clear(); + for( ; it.current(); ++it ) + { + QString acct = it.current()->property( Kopete::Global::Properties::self()->nickName() ).value().toString() + " <" + it.current()->contactId() + ">"; + QPixmap acctIcon = it.current()->account()->accountIcon(); + mainWidget->cmbAccountName->insertItem( acctIcon, acct ); + + // Select this item if it's the one we're tracking. + if( it.current() == trackingName ) + { + mainWidget->cmbAccountName->setCurrentItem( mainWidget->cmbAccountName->count() - 1 ); + } + } + + mainWidget->edtDisplayName->setText( item->metaContact()->customDisplayName() ); + + Kopete::MetaContact::PropertySource nameSource = item->metaContact()->displayNameSource(); + + mainWidget->radioNameContact->setChecked(nameSource == Kopete::MetaContact::SourceContact); + mainWidget->radioNameKABC->setChecked(nameSource == Kopete::MetaContact::SourceKABC); + mainWidget->radioNameCustom->setChecked(nameSource == Kopete::MetaContact::SourceCustom); + +} + +void KopeteMetaLVIProps::slotLoadPhotoSources() +{ + // fill photo contact sources + QPtrList< Kopete::Contact > cList = item->metaContact()->contacts(); + m_withPhotoContacts.clear(); + Kopete::Contact* trackingPhoto = item->metaContact()->photoSourceContact(); + mainWidget->cmbAccountPhoto->clear(); + QPtrListIterator<Kopete::Contact> itp( cList ); + for( ; itp.current(); ++itp ) + { + Kopete::Contact *citem = itp.current(); + if ( citem->hasProperty( Kopete::Global::Properties::self()->photo().key() ) ) + { + QString acct = citem->property( Kopete::Global::Properties::self()->nickName() ).value().toString() + " <" + citem->contactId() + ">"; + QPixmap acctIcon = citem->account()->accountIcon(); + mainWidget->cmbAccountPhoto->insertItem( acctIcon, acct ); + + // Select this item if it's the one we're tracking. + if( citem == trackingPhoto ) + { + mainWidget->cmbAccountPhoto->setCurrentItem( mainWidget->cmbAccountPhoto->count() - 1 ); + } + m_withPhotoContacts.insert(mainWidget->cmbAccountPhoto->count() - 1 , citem ); + } + } +#if KDE_IS_VERSION(3,4,0) + mainWidget->cmbPhotoUrl->setKURL(item->metaContact()->customPhoto().url()); +#else + mainWidget->cmbPhotoUrl->setURL(item->metaContact()->customPhoto().url()); +#endif + + Kopete::MetaContact::PropertySource photoSource = item->metaContact()->photoSource(); + + mainWidget->radioPhotoContact->setChecked(photoSource == Kopete::MetaContact::SourceContact); + mainWidget->radioPhotoKABC->setChecked(photoSource == Kopete::MetaContact::SourceKABC); + mainWidget->radioPhotoCustom->setChecked(photoSource == Kopete::MetaContact::SourceCustom); + + mainWidget->chkSyncPhoto->setChecked(item->metaContact()->isPhotoSyncedWithKABC()); +} + +void KopeteMetaLVIProps::slotEnableAndDisableWidgets() +{ + KABC::AddressBook *ab = Kopete::KABCPersistence::self()->addressBook(); + KABC::Addressee a = ab->findByUid( mAddressBookUid ); + bool validLink = ! a.isEmpty(); + // kabc source requires a kabc link + mainWidget->radioNameKABC->setEnabled(validLink); + // kabc source requires a kabc link + mainWidget->radioPhotoKABC->setEnabled(validLink); + // sync with kabc has no sense if we use kabc as source (sync kabc with kabc? uh?) + // it has also no sense if they are no kabc link + if( selectedPhotoSource() == Kopete::MetaContact::SourceKABC || !validLink ) + { + mainWidget->chkSyncPhoto->setEnabled(false); + } + else + { + mainWidget->chkSyncPhoto->setEnabled(true); + } + + mainWidget->radioNameContact->setEnabled(item->metaContact()->contacts().count()); + mainWidget->radioPhotoContact->setEnabled(!m_withPhotoContacts.isEmpty()); + + mainWidget->cmbAccountName->setEnabled(selectedNameSource() == Kopete::MetaContact::SourceContact); + mainWidget->edtDisplayName->setEnabled(selectedNameSource() == Kopete::MetaContact::SourceCustom); + + mainWidget->cmbAccountPhoto->setEnabled(selectedPhotoSource() == Kopete::MetaContact::SourceContact); + mainWidget->cmbPhotoUrl->setEnabled(selectedPhotoSource() == Kopete::MetaContact::SourceCustom); + + if ( m_withPhotoContacts.isEmpty() ) + { + mainWidget->cmbAccountPhoto->clear(); + mainWidget->cmbAccountPhoto->insertItem(i18n("No Contacts with Photo Support")); + mainWidget->cmbAccountPhoto->setEnabled(false); + } + + QImage photo; + switch ( selectedPhotoSource() ) + { + case Kopete::MetaContact::SourceKABC: + photo = Kopete::photoFromKABC(mAddressBookUid); + break; + case Kopete::MetaContact::SourceContact: + photo = Kopete::photoFromContact(selectedPhotoSourceContact()); + break; + case Kopete::MetaContact::SourceCustom: + photo = QImage(KURL::decode_string(mainWidget->cmbPhotoUrl->url())); + break; + } + if( !photo.isNull() ) + mainWidget->photoLabel->setPixmap(QPixmap(photo.smoothScale( 64, 92, QImage::ScaleMin ))); + else + mainWidget->photoLabel->setPixmap( QPixmap() ); +} + +Kopete::MetaContact::PropertySource KopeteMetaLVIProps::selectedNameSource() const +{ + if ( mainWidget->radioNameKABC->isChecked() ) + return Kopete::MetaContact::SourceKABC; + if ( mainWidget->radioNameContact->isChecked() ) + return Kopete::MetaContact::SourceContact; + if ( mainWidget->radioNameCustom->isChecked() ) + return Kopete::MetaContact::SourceCustom; + else + return Kopete::MetaContact::SourceCustom; +} + +Kopete::MetaContact::PropertySource KopeteMetaLVIProps::selectedPhotoSource() const +{ + if ( mainWidget->radioPhotoKABC->isChecked() ) + return Kopete::MetaContact::SourceKABC; + if ( mainWidget->radioPhotoContact->isChecked() ) + return Kopete::MetaContact::SourceContact; + if ( mainWidget->radioPhotoCustom->isChecked() ) + return Kopete::MetaContact::SourceCustom; + else + return Kopete::MetaContact::SourceCustom; +} + +Kopete::Contact* KopeteMetaLVIProps::selectedNameSourceContact() const +{ + Kopete::Contact *c= item->metaContact()->contacts().at( mainWidget->cmbAccountName->currentItem() ); + return c ? c : 0L; +} + +Kopete::Contact* KopeteMetaLVIProps::selectedPhotoSourceContact() const +{ + if (m_withPhotoContacts.isEmpty()) + return 0L; + Kopete::Contact *c = m_withPhotoContacts[mainWidget->cmbAccountPhoto->currentItem() ]; + return c ? c : 0L; +} + +void KopeteMetaLVIProps::slotOkClicked() +{ + // update meta contact's UID + item->metaContact()->setMetaContactId( mAddressBookUid ); + //this has to be done first, in the case something is synced with KABC (see bug 109494) + + // set custom display name + if( mainWidget->edtDisplayName->text() != item->metaContact()->customDisplayName() ) + item->metaContact()->setDisplayName( mainWidget->edtDisplayName->text() ); + + item->metaContact()->setDisplayNameSource(selectedNameSource()); + item->metaContact()->setDisplayNameSourceContact( selectedNameSourceContact() ); + + // set photo source + item->metaContact()->setPhotoSource(selectedPhotoSource()); + item->metaContact()->setPhotoSourceContact( selectedPhotoSourceContact() ); + if ( !mainWidget->cmbPhotoUrl->url().isEmpty()) + item->metaContact()->setPhoto(KURL::fromPathOrURL((mainWidget->cmbPhotoUrl->url()))); + item->metaContact()->setPhotoSyncedWithKABC( mainWidget->chkSyncPhoto->isChecked() ); + + item->metaContact()->setUseCustomIcon( + mainWidget->chkUseCustomIcons->isChecked() ); + + // only call setIcon if any of the icons is not set to default icon + if( + mainWidget->icnbOffline->icon() != MC_OFF || + mainWidget->icnbOnline->icon() != MC_ON || + mainWidget->icnbAway->icon() != MC_AW || + mainWidget->icnbUnknown->icon() != MC_UNK ) + { + item->metaContact()->setIcon( mainWidget->icnbOffline->icon(), + Kopete::ContactListElement::Offline ); + + item->metaContact()->setIcon( mainWidget->icnbOnline->icon(), + Kopete::ContactListElement::Online ); + + item->metaContact()->setIcon( mainWidget->icnbAway->icon(), + Kopete::ContactListElement::Away ); + + item->metaContact()->setIcon( mainWidget->icnbUnknown->icon(), + Kopete::ContactListElement::Unknown ); + } + + mNotificationProps->storeCurrentCustoms(); +} + +void KopeteMetaLVIProps::slotUseCustomIconsToggled(bool on) +{ + mainWidget->lblOffline->setEnabled( on ); + mainWidget->lblOnline->setEnabled( on ); + mainWidget->lblAway->setEnabled( on ); + mainWidget->lblUnknown->setEnabled( on ); + + mainWidget->icnbOffline->setEnabled( on ); + mainWidget->icnbOnline->setEnabled( on ); + mainWidget->icnbAway->setEnabled( on ); + mainWidget->icnbUnknown->setEnabled( on ); +} + +void KopeteMetaLVIProps::slotAddresseeChanged( const KABC::Addressee & a ) +{ + if ( !a.isEmpty() ) + { + mSound = a.sound(); + mFromKABC->setEnabled( !( mSound.isIntern() || mSound.url().isEmpty() ) ); + mainWidget->btnExportKABC->setEnabled( true ); + mainWidget->btnImportKABC->setEnabled( true ); + // set/update the MC's addressee uin field + mAddressBookUid = a.uid(); + } + else + { + mainWidget->btnExportKABC->setEnabled( false ); + mainWidget->btnImportKABC->setEnabled( false ); + mAddressBookUid = QString::null; + mainWidget->radioNameContact->setChecked( true ); + mainWidget->radioPhotoContact->setChecked( true ); + } + slotEnableAndDisableWidgets(); +} + +void KopeteMetaLVIProps::slotExportClicked() +{ + item->metaContact()->setMetaContactId( mAddressBookUid ); + delete mExport; + mExport = new KopeteAddressBookExport( this, item->metaContact() ); + if ( mExport->showDialog() == QDialog::Accepted ) + mExport->exportData(); +} + +void KopeteMetaLVIProps::slotImportClicked() +{ + item->metaContact()->setMetaContactId( mAddressBookUid ); + if ( Kopete::KABCPersistence::self()->syncWithKABC( item->metaContact() ) ) + KMessageBox::queuedMessageBox( this, KMessageBox::Information, + i18n( "No contacts were imported from the address book." ), + i18n( "No Change" ) ); +} + +void KopeteMetaLVIProps::slotFromKABCClicked() +{ + mNotificationProps->widget()->customSound->setURL( mSound.url() ); +} + +void KopeteMetaLVIProps::slotOpenSoundDialog( KURLRequester *requester ) +{ + // taken from kdelibs/kio/kfile/knotifydialog.cpp + // only need to init this once + requester->disconnect( SIGNAL( openFileDialog( KURLRequester * )), + this, SLOT( slotOpenSoundDialog( KURLRequester * ))); + + KFileDialog *fileDialog = requester->fileDialog(); + //fileDialog->setCaption( i18n("Select Sound File") ); + QStringList filters; + filters << "audio/x-wav" << "audio/x-mp3" << "application/ogg" + << "audio/x-adpcm"; + fileDialog->setMimeFilter( filters ); + + // find the first "sound"-resource that contains files + QStringList soundDirs = + KGlobal::dirs()->findDirs("data", "kopete/sounds"); + soundDirs += KGlobal::dirs()->resourceDirs( "sound" ); + + if ( !soundDirs.isEmpty() ) { + KURL soundURL; + QDir dir; + dir.setFilter( QDir::Files | QDir::Readable ); + QStringList::ConstIterator it = soundDirs.begin(); + while ( it != soundDirs.end() ) { + dir = *it; + if ( dir.isReadable() && dir.count() > 2 ) { + soundURL.setPath( *it ); + fileDialog->setURL( soundURL ); + break; + } + ++it; + } + } +} + +void KopeteMetaLVIProps::slotClearPhotoClicked() +{ +#if KDE_IS_VERSION(3,4,0) + mainWidget->cmbPhotoUrl->setKURL( KURL() ); +#else + mainWidget->cmbPhotoUrl->setURL( QString::null ); +#endif + item->metaContact()->setPhoto( KURL() ); + + slotEnableAndDisableWidgets(); +} + +#include "kopetelviprops.moc" diff --git a/kopete/kopete/contactlist/kopetelviprops.h b/kopete/kopete/contactlist/kopetelviprops.h new file mode 100644 index 00000000..9a2ebf34 --- /dev/null +++ b/kopete/kopete/contactlist/kopetelviprops.h @@ -0,0 +1,102 @@ +/* + kopetelviprops.h + + Kopete Contactlist Properties GUI for Groups and MetaContacts + + Copyright (c) 2002-2003 by Stefan Gehn <metz AT gehn.net> + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KOPETELVIPROPS_H +#define KOPETELVIPROPS_H + +#include <kdialogbase.h> +#include <kabc/sound.h> + +#include "kopetemetacontact.h" + +#include "kopetegvipropswidget.h" +#include "kopetemetalvipropswidget.h" + +class QButtonGroup; + +class AddressBookLinkWidget; +class CustomNotificationProps; +class KPushButton; +class KopeteGroupViewItem; +class KopeteMetaContactLVI; +class KopeteAddressBookExport; +class KURLRequester; + +namespace KABC { class Addressee; } +namespace Kopete { class Contact; } + +class KopeteGVIProps: public KDialogBase +{ + Q_OBJECT + + public: + KopeteGVIProps(KopeteGroupViewItem *gvi, QWidget *parent, const char *name=0L); + ~KopeteGVIProps(); + + private: + CustomNotificationProps * mNotificationProps; + KopeteGVIPropsWidget *mainWidget; + KopeteGroupViewItem *item; + bool m_dirty; + + private slots: + void slotOkClicked(); + void slotUseCustomIconsToggled(bool on); + void slotIconChanged(); +}; + + +class KopeteMetaLVIProps: public KDialogBase +{ + Q_OBJECT + + public: + KopeteMetaLVIProps(KopeteMetaContactLVI *gvi, QWidget *parent, const char *name=0L); + ~KopeteMetaLVIProps(); + + private: + CustomNotificationProps * mNotificationProps; + QPushButton *mFromKABC; + KopeteMetaLVIPropsWidget *mainWidget; + AddressBookLinkWidget *linkWidget; + KopeteMetaContactLVI *item; + KopeteAddressBookExport *mExport; + KABC::Sound mSound; + int m_countPhotoCapable; + QMap<int, Kopete::Contact *> m_withPhotoContacts; + QString mAddressBookUid; // the currently selected addressbook UID + + Kopete::MetaContact::PropertySource selectedNameSource() const; + Kopete::MetaContact::PropertySource selectedPhotoSource() const; + Kopete::Contact* selectedNameSourceContact() const; + Kopete::Contact* selectedPhotoSourceContact() const; + private slots: + void slotOkClicked(); + void slotUseCustomIconsToggled( bool on ); + void slotClearPhotoClicked(); + void slotAddresseeChanged( const KABC::Addressee & ); + void slotExportClicked(); + void slotImportClicked(); + void slotFromKABCClicked(); + void slotOpenSoundDialog( KURLRequester *requester ); + void slotLoadNameSources(); + void slotLoadPhotoSources(); + void slotEnableAndDisableWidgets(); +}; + +#endif diff --git a/kopete/kopete/contactlist/kopetemetacontactlvi.cpp b/kopete/kopete/contactlist/kopetemetacontactlvi.cpp new file mode 100644 index 00000000..86dc4b40 --- /dev/null +++ b/kopete/kopete/contactlist/kopetemetacontactlvi.cpp @@ -0,0 +1,1102 @@ +/* + kopetemetacontactlvi.cpp - Kopete Meta Contact KListViewItem + + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + Copyright (c) 2002-2004 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2002 by Duncan Mac-Vicar P <duncan@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qpainter.h> +#include <qtimer.h> +#include <qvariant.h> +#include <qmime.h> +#include <qstylesheet.h> + +#include "knotification.h" +#include <kdebug.h> +#include <kiconeffect.h> +#include <kimageeffect.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpassivepopup.h> +#include <kpopupmenu.h> +#include <kglobal.h> +#include <kconfig.h> +#include <kapplication.h> + +#include <kabc/addressbook.h> +#include <kabc/addressee.h> + +#include <kdeversion.h> +#include <kinputdialog.h> + + +#include "addcontactpage.h" +#include "kopeteaccount.h" +#include "kopeteaccountmanager.h" +#include "kopetecontactlist.h" +#include "kopetecontactlistview.h" +#include "kopeteemoticons.h" +#include "kopeteuiglobal.h" +#include "kopetegroup.h" +#include "kopetegroupviewitem.h" +#include "kopetemetacontact.h" +#include "kopetemetacontactlvi.h" +#include "kopetepluginmanager.h" +#include "kopeteprefs.h" +#include "kopetestdaction.h" +#include "systemtray.h" +#include "kopeteglobal.h" +#include "kopetecontact.h" +#include "kabcpersistence.h" + +#include <memory> + +using namespace Kopete::UI; + +namespace Kopete { +namespace UI { +namespace ListView { + +class MetaContactToolTipSource : public ToolTipSource +{ +public: + MetaContactToolTipSource( MetaContact *mc ) + : metaContact( mc ) + { + } + QString operator()( ComponentBase *, const QPoint &, QRect & ) + { + + // We begin with the meta contact display name at the top of the tooltip + QString toolTip = QString::fromLatin1("<qt><table cellpadding=\"0\" cellspacing=\"1\">"); + + toolTip += QString::fromLatin1("<tr><td>"); + + if ( ! metaContact->photo().isNull() ) + { + QString photoName = QString::fromLatin1("kopete-metacontact-photo:%1").arg( KURL::encode_string( metaContact->metaContactId() )); + //QMimeSourceFactory::defaultFactory()->setImage( "contactimg", metaContact->photo() ); + toolTip += QString::fromLatin1("<img src=\"%1\">").arg( photoName ); + } + + toolTip += QString::fromLatin1("</td><td>"); + + QString displayName; + Kopete::Emoticons *e = Kopete::Emoticons::self(); + QValueList<Emoticons::Token> t = e->tokenize( metaContact->displayName()); + QValueList<Emoticons::Token>::iterator it; + for( it = t.begin(); it != t.end(); ++it ) + { + if( (*it).type == Kopete::Emoticons::Image ) + { + displayName += (*it).picHTMLCode; + } else if( (*it).type == Kopete::Emoticons::Text ) + { + displayName += QStyleSheet::escape( (*it).text ); + } + } + + toolTip += QString::fromLatin1("<b><font size=\"+1\">%1</font></b><br><br>").arg( displayName ); + + QPtrList<Contact> contacts = metaContact->contacts(); + if ( contacts.count() == 1 ) + { + return toolTip + contacts.first()->toolTip() + QString::fromLatin1("</td></tr></table></qt>"); + } + + toolTip += QString::fromLatin1("<table>"); + + // We are over a metacontact with > 1 child contacts, and not over a specific contact + // Iterate through children and display a summary tooltip + for(Contact *c = contacts.first(); c; c = contacts.next()) + { + QString iconName = QString::fromLatin1("kopete-contact-icon:%1:%2:%3") + .arg( KURL::encode_string( c->protocol()->pluginId() ), + KURL::encode_string( c->account()->accountId() ), + KURL::encode_string( c->contactId() ) + ); + + toolTip += i18n("<tr><td>STATUS ICON <b>PROTOCOL NAME</b> (ACCOUNT NAME)</td><td>STATUS DESCRIPTION</td></tr>", + "<tr><td><img src=\"%1\"> <nobr><b>%2</b></nobr> <nobr>(%3)</nobr></td><td align=\"right\"><nobr>%4</nobr></td></tr>") + .arg( iconName, Kopete::Emoticons::parseEmoticons(c->property(Kopete::Global::Properties::self()->nickName()).value().toString()) , c->contactId(), c->onlineStatus().description() ); + } + + return toolTip + QString::fromLatin1("</table></td></tr></table></qt>"); + } +private: + MetaContact *metaContact; +}; + +} // END namespace ListView +} // END namespace UI +} // END namespace Kopete + +class KopeteMetaContactLVI::Private +{ +public: + Private() : metaContactIcon( 0L ), nameText( 0L ), extraText( 0L ), contactIconBox( 0L ), + currentMode( -1 ), currentIconMode( -1 ) {} + ListView::ImageComponent *metaContactIcon; + ListView::DisplayNameComponent *nameText; + ListView::DisplayNameComponent *extraText; + ListView::BoxComponent *contactIconBox; + ListView::BoxComponent *spacerBox; + std::auto_ptr<ListView::ToolTipSource> toolTipSource; + // metacontact icon size + int iconSize; + // protocol icon size + int contactIconSize; + int currentMode; + int currentIconMode; + + QPtrList<Kopete::MessageEvent> events; +}; + +KopeteMetaContactLVI::KopeteMetaContactLVI( Kopete::MetaContact *contact, KopeteGroupViewItem *parent ) +: ListView::Item( parent, contact, "MetaContactLVI" ) +//: QObject( contact, "MetaContactLVI" ), KListViewItem( parent ) +{ + m_metaContact = contact; + m_isTopLevel = false; + m_parentGroup = parent; + m_parentView = 0L; + + initLVI(); + parent->refreshDisplayName(); +} + +KopeteMetaContactLVI::KopeteMetaContactLVI( Kopete::MetaContact *contact, QListViewItem *parent ) +: ListView::Item( parent, contact, "MetaContactLVI" ) +//: QObject( contact, "MetaContactLVI" ), KListViewItem( parent ) +{ + m_metaContact = contact; + + m_isTopLevel = true; + m_parentGroup = 0L; + m_parentView = 0L; + + initLVI(); +} + +KopeteMetaContactLVI::KopeteMetaContactLVI( Kopete::MetaContact *contact, QListView *parent ) +: ListView::Item( parent, contact, "MetaContactLVI" ) +//: QObject( contact, "MetaContactLVI" ), KListViewItem( parent ) +{ + m_metaContact = contact; + + m_isTopLevel = true; + m_parentGroup = 0L; + m_parentView = parent; + + initLVI(); +} + +void KopeteMetaContactLVI::initLVI() +{ + d = new Private; + + d->toolTipSource.reset( new ListView::MetaContactToolTipSource( m_metaContact ) ); + + m_oldStatus = m_metaContact->status(); + + connect( m_metaContact, SIGNAL( displayNameChanged( const QString &, const QString & ) ), + SLOT( slotDisplayNameChanged() ) ); + + connect( m_metaContact, SIGNAL( photoChanged() ), + SLOT( slotPhotoChanged() ) ); + + connect( m_metaContact, SIGNAL( onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType ) ), + SLOT( slotPhotoChanged() ) ); + + connect( m_metaContact, SIGNAL( onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType ) ), + this, SLOT(slotIdleStateChanged( ) ) ); + + connect( m_metaContact, SIGNAL( contactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus & ) ), + SLOT( slotContactStatusChanged( Kopete::Contact * ) ) ); + + connect( m_metaContact, SIGNAL( contactAdded( Kopete::Contact * ) ), + SLOT( slotContactAdded( Kopete::Contact * ) ) ); + + connect( m_metaContact, SIGNAL( contactRemoved( Kopete::Contact * ) ), + SLOT( slotContactRemoved( Kopete::Contact * ) ) ); + + connect( m_metaContact, SIGNAL( iconAppearanceChanged() ), + SLOT( slotUpdateMetaContact() ) ); + + connect( m_metaContact, SIGNAL( useCustomIconChanged( bool ) ), + SLOT( slotUpdateMetaContact() ) ); + + connect( m_metaContact, SIGNAL( contactIdleStateChanged( Kopete::Contact * ) ), + SLOT( slotIdleStateChanged( Kopete::Contact * ) ) ); + + connect( KopetePrefs::prefs(), SIGNAL( contactListAppearanceChanged() ), + SLOT( slotConfigChanged() ) ); + + connect( kapp, SIGNAL( appearanceChanged() ), SLOT( slotConfigChanged() ) ); + + mBlinkTimer = new QTimer( this, "mBlinkTimer" ); + connect( mBlinkTimer, SIGNAL( timeout() ), SLOT( slotBlink() ) ); + mIsBlinkIcon = false; + + //if ( !mBlinkIcon ) + // mBlinkIcon = new QPixmap( KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "newmsg" ), KIcon::Small ) ); + + slotConfigChanged(); // this calls slotIdleStateChanged(), which sets up the constituent components, spacing, fonts and indirectly, the contact icon + slotDisplayNameChanged(); + updateContactIcons(); +} + +KopeteMetaContactLVI::~KopeteMetaContactLVI() +{ + delete d; + //if ( m_parentGroup ) + // m_parentGroup->refreshDisplayName(); +} + +void KopeteMetaContactLVI::movedToDifferentGroup() +{ + KopeteContactListView *lv = dynamic_cast<KopeteContactListView *>( listView() ); + if ( !lv ) + return; + + if ( m_parentGroup ) + m_parentGroup->refreshDisplayName(); + + // create a spacer if wanted + // I assume that the safety property that allows the delete in slotConfigChanged holds here - Will + delete d->spacerBox->component( 0 ); + if ( KListViewItem::parent() && KopetePrefs::prefs()->contactListIndentContacts() && + !KopetePrefs::prefs()->treeView() ) + { + new ListView::SpacerComponent( d->spacerBox, 20, 0 ); + } + + KopeteGroupViewItem *group_item = dynamic_cast<KopeteGroupViewItem*>(KListViewItem::parent()); + if ( group_item ) + { + m_isTopLevel = false; + m_parentGroup = group_item; + m_parentView = 0L; + group_item->refreshDisplayName(); + } + else + { + m_isTopLevel = true; + m_parentGroup = 0L; + m_parentView = lv; + } +} + +void KopeteMetaContactLVI::rename( const QString& newName ) +{ + QString oldName = m_metaContact->displayName(); + KopeteContactListView *lv = dynamic_cast<KopeteContactListView *>( listView() ); + if ( lv ) + { + KopeteContactListView::UndoItem *u=new KopeteContactListView::UndoItem(KopeteContactListView::UndoItem::MetaContactRename, m_metaContact); + // HACK but args are strings not ints + u->nameSource = m_metaContact->displayNameSource(); + // additional args + if ( m_metaContact->displayNameSource() == Kopete::MetaContact::SourceCustom ) + { + u->args << m_metaContact->customDisplayName(); + } + else if ( m_metaContact->displayNameSource() == Kopete::MetaContact::SourceContact ) + { + Kopete::Contact* c = m_metaContact->displayNameSourceContact(); + if(c) + u->args << c->contactId() << c->protocol()->pluginId() << c->account()->accountId(); + } + // source kabc requires no arguments + + lv->insertUndoItem(u); + } + + if ( newName.isEmpty() ) + { + // fallback to KABC + if ( !m_metaContact->metaContactId().isEmpty() ) + { + m_metaContact->setDisplayNameSource(Kopete::MetaContact::SourceKABC); + if ( ! m_metaContact->displayName().isEmpty() ) + { + slotDisplayNameChanged(); + return; + } + } + // bad luck with KABC + m_metaContact->setDisplayNameSource(Kopete::MetaContact::SourceContact); + // TODO iterate though all subcontacts to check non empty nick + m_metaContact->setDisplayNameSourceContact( m_metaContact->contacts().first() ); + slotDisplayNameChanged(); + } + else // user changed name manually, set source to custom + { + m_metaContact->setDisplayNameSource( Kopete::MetaContact::SourceCustom ); + m_metaContact->setDisplayName( newName ); + slotDisplayNameChanged(); + } + + kdDebug( 14000 ) << k_funcinfo << "newName=" << newName << endl; +} + +void KopeteMetaContactLVI::slotContactStatusChanged( Kopete::Contact *c ) +{ + updateContactIcon( c ); + + // FIXME: All this code should be in kopetemetacontact.cpp.. having it in the LVI makes it all fire + // multiple times if the user is in multiple groups - Jason + + // comparing the status of the previous and new preferred contact is the determining factor in deciding to notify + Kopete::OnlineStatus newStatus; + if ( m_metaContact->preferredContact() ) + newStatus = m_metaContact->preferredContact()->onlineStatus(); + else + { + // the last child contact has gone offline or otherwise unreachable, so take the changed contact's online status + newStatus = c->onlineStatus(); + } + + // ensure we are not suppressing notifications, because connecting or disconnected + if ( !(c->account()->suppressStatusNotification() + || ( c->account()->myself()->onlineStatus().status() == Kopete::OnlineStatus::Connecting ) + || !c->account()->isConnected() ) ) + { + if ( !c->account()->isAway() || KopetePrefs::prefs()->soundIfAway() ) + { + //int winId = KopeteSystemTray::systemTray() ? KopeteSystemTray::systemTray()->winId() : 0; + + QString text = i18n( "<qt><i>%1</i> is now %2.</qt>" ) + .arg( Kopete::Emoticons::parseEmoticons( QStyleSheet::escape(m_metaContact->displayName()) ) , + QStyleSheet::escape(c->onlineStatus().description())); + + // figure out what's happened + enum ChangeType { noChange, noEvent, signedIn, changedStatus, signedOut }; + ChangeType t = noChange; + //kdDebug( 14000 ) << k_funcinfo << m_metaContact->displayName() << + //" - Old MC Status: " << m_oldStatus.status() << ", New MC Status: " << newStatus.status() << endl; + // first, exclude changes due to blocking or subscription changes at the protocol level + if ( ( m_oldStatus.status() == Kopete::OnlineStatus::Unknown + || newStatus.status() == Kopete::OnlineStatus::Unknown ) ) + t = noEvent; // This means the contact's changed from or to unknown - due to a protocol state change, not a contact state change + else // we're dealing with a genuine contact state change + { + if ( m_oldStatus.status() == Kopete::OnlineStatus::Offline ) + { + if ( newStatus.status() != Kopete::OnlineStatus::Offline ) + { + //kdDebug( 14000 ) << "signed in" << endl; + t = signedIn; // contact has gone from offline to something else, it's a sign-in + } + } + else if ( m_oldStatus.status() == Kopete::OnlineStatus::Online + || m_oldStatus.status() == Kopete::OnlineStatus::Away + || m_oldStatus.status() == Kopete::OnlineStatus::Invisible) + { + if ( newStatus.status() == Kopete::OnlineStatus::Offline ) + { + //kdDebug( 14000 ) << "signed OUT" << endl; + t = signedOut; // contact has gone from an online state to an offline state, it's a sign out + } + else if ( m_oldStatus > newStatus || m_oldStatus < newStatus ) // operator!= is useless because it's an identity operator, not an equivalence operator + { + // contact has changed online states, it's a status change, + // and the preferredContact changed status, or there is a new preferredContacat + // so it's worth notifying + //kdDebug( 14000 ) << "changed status" << endl; + t = changedStatus; + } + } + else if ( m_oldStatus != newStatus ) + { + //kdDebug( 14000 ) << "non-event" << endl; + // catch-all for any other status change we don't know about + t = noEvent; + } + // if none of the above were true, t will still be noChange + } + + // now issue the appropriate notification + switch ( t ) + { + case noEvent: + case noChange: + break; + case signedIn: + connect(KNotification::event(m_metaContact, "kopete_contact_online", text, m_metaContact->photo(), KopeteSystemTray::systemTray(), i18n( "Chat" )) , + SIGNAL(activated(unsigned int )) , this, SLOT( execute() ) ); + break; + case changedStatus: + connect(KNotification::event(m_metaContact, "kopete_contact_status_change", text, m_metaContact->photo(), KopeteSystemTray::systemTray(), i18n( "Chat" )) , + SIGNAL(activated(unsigned int )) , this, SLOT( execute() )); + break; + case signedOut: + KNotification::event(m_metaContact, "kopete_contact_offline", text, m_metaContact->photo(), KopeteSystemTray::systemTray()); + break; + } + } + //blink if the metacontact icon has changed. + if ( !mBlinkTimer->isActive() && d->metaContactIcon /*&& d->metaContactIcon->pixmap() != m_oldStatusIcon */) + { + mIsBlinkIcon = false; + m_blinkLeft = 9; + mBlinkTimer->start( 400, false ); + } + } + else + { + //the status icon probably changed, but we didn't blink. + //So the olfStatusIcon will not be set to the real after the blink. + //we set it now. + if( !mBlinkTimer->isActive() ) + m_oldStatusIcon=d->metaContactIcon ? d->metaContactIcon->pixmap() : QPixmap(); + } + + // make a note of the current status for the next time we get a status change + m_oldStatus = newStatus; + + if ( m_parentGroup ) + m_parentGroup->refreshDisplayName(); + updateVisibility(); +} + +void KopeteMetaContactLVI::slotUpdateMetaContact() +{ + slotIdleStateChanged( 0 ); + updateVisibility(); + + if ( m_parentGroup ) + m_parentGroup->refreshDisplayName(); +} + +void KopeteMetaContactLVI::execute() const +{ + if ( d->events.first() ) + d->events.first()->apply(); + else + m_metaContact->execute(); + + //The selection is removed, but the contact still hihjlihted, remove the selection in the contactlist (see bug 106090) + Kopete::ContactList::self()->setSelectedItems( QPtrList<Kopete::MetaContact>() , QPtrList<Kopete::Group>() ); +} + +void KopeteMetaContactLVI::slotDisplayNameChanged() +{ + if ( d->nameText ) + { + d->nameText->setText( m_metaContact->displayName() ); + + // delay the sort if we can + if ( ListView::ListView *lv = dynamic_cast<ListView::ListView *>( listView() ) ) + lv->delayedSort(); + else + listView()->sort(); + } +} + +void KopeteMetaContactLVI::slotPhotoChanged() +{ + if ( d->metaContactIcon && d->currentIconMode == KopetePrefs::PhotoPic ) + { + m_oldStatusIcon= d->metaContactIcon->pixmap(); + QPixmap photoPixmap; + //QPixmap defaultIcon( KGlobal::iconLoader()->loadIcon( "vcard", KIcon::Desktop ) ); + QImage photoImg = m_metaContact->photo(); + if ( !photoImg.isNull() && (photoImg.width() > 0) && (photoImg.height() > 0) ) + { + int photoSize = d->iconSize; + + photoImg = photoImg.smoothScale( photoSize, photoSize, QImage::ScaleMin ); + + KImageEffect *effect = 0L; + switch ( m_metaContact->status() ) + { + case Kopete::OnlineStatus::Online: + break; + case Kopete::OnlineStatus::Away: + effect = new KImageEffect(); + effect->fade(photoImg, 0.5, Qt::white); + break; + case Kopete::OnlineStatus::Offline: + effect = new KImageEffect(); + effect->fade(photoImg, 0.4, Qt::white); + effect->toGray(photoImg); + break; + case Kopete::OnlineStatus::Unknown: + default: + effect = new KImageEffect(); + effect->fade(photoImg, 0.8, Qt::white); + } + delete effect; + + photoPixmap = photoImg; + } + else + { + photoPixmap=SmallIcon(m_metaContact->statusIcon(), d->iconSize); + } + d->metaContactIcon->setPixmap( photoPixmap, false); + if(mBlinkTimer->isActive()) + m_originalBlinkIcon=photoPixmap; + } +} + +/* +void KopeteMetaContactLVI::slotRemoveThisUser() +{ + kdDebug( 14000 ) << k_funcinfo << " Removing user" << endl; + //m_metaContact->removeThisUser(); + + if ( KMessageBox::warningContinueCancel( Kopete::UI::Global::mainWidget(), + i18n( "Are you sure you want to remove %1 from your contact list?" ). + arg( m_metaContact->displayName() ), i18n( "Remove Contact" ), KGuiItem(i18n("Remove"),"delete_user") ) + == KMessageBox::Continue ) + { + Kopete::ContactList::self()->removeMetaContact( m_metaContact ); + } +} + +void KopeteMetaContactLVI::slotRemoveFromGroup() +{ + if ( m_metaContact->isTemporary() ) + return; + + m_metaContact->removeFromGroup( group() ); +} +*/ + +void KopeteMetaContactLVI::startRename( int /*col*/ ) +{ + KListViewItem::startRename( 0 ); +} + +void KopeteMetaContactLVI::okRename( int col ) +{ + KListViewItem::okRename( col ); + setRenameEnabled( 0, false ); +} + +void KopeteMetaContactLVI::cancelRename( int col ) +{ + KListViewItem::cancelRename( col ); + setRenameEnabled( 0, false ); +} + +/* +void KopeteMetaContactLVI::slotMoveToGroup() +{ + if ( m_actionMove && !m_metaContact->isTemporary() ) + { + if ( m_actionMove->currentItem() == 0 ) + { + // we are moving to top-level + if ( group() != Kopete::Group::toplevel ) + m_metaContact->moveToGroup( group(), Kopete::Group::toplevel ); + } + else + { + Kopete::Group *to = Kopete::ContactList::self()->getGroup( m_actionMove->currentText() ); + if ( !m_metaContact->groups().contains( to ) ) + m_metaContact->moveToGroup( group(), to ); + } + } +} + +void KopeteMetaContactLVI::slotAddToGroup() +{ + if ( m_actionCopy ) + { + kdDebug( 14000 ) << "KopeteMetaContactLVI::slotAddToGroup " << endl; + if ( m_actionCopy->currentItem() == 0 ) + { + // we are adding to top-level + m_metaContact->addToGroup( Kopete::Group::toplevel ); + } + else + { + m_metaContact->addToGroup( Kopete::ContactList::self()->getGroup( m_actionCopy->currentText() ) ); + } + } +} +*/ + +//FIXME: this is not used... remove? +void KopeteMetaContactLVI::slotAddToNewGroup() +{ + if ( m_metaContact->isTemporary() ) + return; + + QString groupName = KInputDialog::getText( + i18n( "New Group" ), i18n( "Please enter the name for the new group:" ) ); + + if ( !groupName.isEmpty() ) + m_metaContact->addToGroup( Kopete::ContactList::self()->findGroup( groupName ) ); +} + +void KopeteMetaContactLVI::slotConfigChanged() +{ + setDisplayMode( KopetePrefs::prefs()->contactListDisplayMode(), + KopetePrefs::prefs()->contactListIconMode() ); + + // create a spacer if wanted + delete d->spacerBox->component( 0 ); + if ( KListViewItem::parent() && KopetePrefs::prefs()->contactListIndentContacts() && + !KopetePrefs::prefs()->treeView() ) + { + new ListView::SpacerComponent( d->spacerBox, 20, 0 ); + } + + if ( KopetePrefs::prefs()->contactListUseCustomFonts() ) + { + d->nameText->setFont( KopetePrefs::prefs()->contactListCustomNormalFont() ); + if ( d->extraText ) + d->extraText->setFont( KopetePrefs::prefs()->contactListSmallFont() ); + } + else + { + QFont font=listView()->font(); + d->nameText->setFont( font ); + if(d->extraText) + { + if ( font.pixelSize() != -1 ) + font.setPixelSize( (font.pixelSize() * 3) / 4 ); + else + font.setPointSizeFloat( font.pointSizeFloat() * 0.75 ); + d->extraText->setFont( font ); + } + } + + updateVisibility(); + updateContactIcons(); + slotIdleStateChanged( 0 ); + if(d->nameText) + d->nameText->redraw(); + if(d->extraText) + d->extraText->redraw(); +} + +void KopeteMetaContactLVI::setMetaContactToolTipSourceForComponent( ListView::Component *comp ) +{ + if ( comp ) + comp->setToolTipSource( d->toolTipSource.get() ); +} + +void KopeteMetaContactLVI::setDisplayMode( int mode, int iconmode ) +{ + if ( mode == d->currentMode && iconmode == d->currentIconMode ) + return; + + d->currentMode = mode; + d->currentIconMode = iconmode; + + // empty... + while ( component( 0 ) ) + delete component( 0 ); + + d->nameText = 0L; + d->extraText = 0L; + d->metaContactIcon = 0L; + d->contactIconSize = 12; + if (mode == KopetePrefs::Detailed) { + d->iconSize = iconmode == KopetePrefs::IconPic ? KIcon::SizeMedium : KIcon::SizeLarge; + } else { + d->iconSize = iconmode == KopetePrefs::IconPic ? IconSize( KIcon::Small ) : KIcon::SizeMedium; + } + disconnect( Kopete::KABCPersistence::self()->addressBook() , 0 , this , 0); + + // generate our contents + using namespace ListView; + Component *hbox = new BoxComponent( this, BoxComponent::Horizontal ); + d->spacerBox = new BoxComponent( hbox, BoxComponent::Horizontal ); + + if (iconmode == KopetePrefs::PhotoPic) { + Component *imageBox = new BoxComponent( hbox, BoxComponent::Vertical ); + new VSpacerComponent( imageBox ); + d->metaContactIcon = new ImageComponent( imageBox, d->iconSize + 2 , d->iconSize + 2 ); + new VSpacerComponent( imageBox ); + if(!metaContact()->photoSource() && !Kopete::KABCPersistence::self()->addressBook()->findByUid( metaContact()->metaContactId() ).isEmpty() ) + { //if the photo is the one of the kaddressbook, track every change in the adressbook, it might be the photo of our contact. + connect( Kopete::KABCPersistence::self()->addressBook() , SIGNAL(addressBookChanged (AddressBook *) ) , + this , SLOT(slotPhotoChanged())); + } + } else { + d->metaContactIcon = new ImageComponent( hbox ); + } + + if( mode == KopetePrefs::Detailed ) + { + d->contactIconSize = IconSize( KIcon::Small ); + Component *vbox = new BoxComponent( hbox, BoxComponent::Vertical ); + d->nameText = new DisplayNameComponent( vbox ); + d->extraText = new DisplayNameComponent( vbox ); + + Component *box = new BoxComponent( vbox, BoxComponent::Horizontal ); + d->contactIconBox = new BoxComponent( box, BoxComponent::Horizontal ); + } + else if( mode == KopetePrefs::RightAligned ) // old right-aligned contact + { + d->nameText = new DisplayNameComponent( hbox ); + new HSpacerComponent( hbox ); + d->contactIconBox = new BoxComponent( hbox, BoxComponent::Horizontal ); + } + else // older left-aligned contact + { + d->nameText = new DisplayNameComponent( hbox ); + d->contactIconBox = new BoxComponent( hbox, BoxComponent::Horizontal ); + } + + // set some components to have the metacontact tooltip + setMetaContactToolTipSourceForComponent( d->metaContactIcon ); + setMetaContactToolTipSourceForComponent( d->nameText ); + setMetaContactToolTipSourceForComponent( d->extraText ); + + // update the display name + slotDisplayNameChanged(); + slotPhotoChanged(); + slotIdleStateChanged( 0 ); + + // finally, re-add all contacts so their icons appear. remove them first for consistency. + QPtrList<Kopete::Contact> contacts = m_metaContact->contacts(); + for ( QPtrListIterator<Kopete::Contact> it( contacts ); it.current(); ++it ) + { + slotContactRemoved( *it ); + slotContactAdded( *it ); + } + m_oldStatusIcon=d->metaContactIcon ? d->metaContactIcon->pixmap() : QPixmap(); + if( mBlinkTimer->isActive() ) + m_originalBlinkIcon=m_oldStatusIcon; +} + +void KopeteMetaContactLVI::updateVisibility() +{ + if ( KopetePrefs::prefs()->showOffline() || !d->events.isEmpty() ) + setTargetVisibility( true ); + else if ( !m_metaContact->isOnline() && !mBlinkTimer->isActive() ) + setTargetVisibility( false ); + else + setTargetVisibility( true ); +} + +void KopeteMetaContactLVI::slotContactPropertyChanged( Kopete::Contact *contact, + const QString &key, const QVariant &old, const QVariant &newVal ) +{ +// if ( key == QString::fromLatin1("awayMessage") ) +// kdDebug( 14000 ) << k_funcinfo << "contact=" << contact->contactId() << ", isonline=" << contact->isOnline() << ", alloffline=" << !m_metaContact->isOnline() << ", oldvalue=" << old.toString() << ", newvalue=" << newVal.toString() << endl; + if ( key == QString::fromLatin1("awayMessage") && d->extraText && old != newVal ) + { + bool allOffline = !m_metaContact->isOnline(); + if ( newVal.toString().isEmpty() || ( !contact->isOnline() && !allOffline ) ) + { + // try to find a more suitable away message to be displayed when: + // -new away message is empty or + // -contact who set it is offline and there are contacts online in the metacontact + bool allAwayMessagesEmpty = true; + QPtrList<Kopete::Contact> contacts = m_metaContact->contacts(); + for ( Kopete::Contact *c = contacts.first(); c; c = contacts.next() ) + { +// kdDebug( 14000 ) << k_funcinfo << "ccontact=" << c->contactId() << ", isonline=" << c->isOnline() << ", awaymsg=" << c->property( key ).value().toString() << endl; + QString awayMessage( c->property( key ).value().toString() ); + if ( ( allOffline || c->isOnline() ) && !awayMessage.isEmpty() ) + { + // display this contact's away message when: + // -this contact's away message is not empty and + // -this contact is online or there are no contacts online at all + allAwayMessagesEmpty = false; + d->extraText->setText( awayMessage ); + break; + } + } + if ( allAwayMessagesEmpty ) + d->extraText->setText( QString::null ); + } + else + { + // just use new away message when: + // -new away message is not empty and + // -contact who set it is online or there are no contacts online at all + d->extraText->setText( newVal.toString() ); + } + } // wtf? KopeteMetaContact also connects this signals and emits photoChanged! why no connect photoChanged to slotPhotoChanged? + /*else if ( key == QString::fromLatin1("photo") && (m_metaContact->photoSourceContact() == contact) && (m_metaContact->photoSource() == Kopete::MetaContact::SourceContact)) + { + slotPhotoChanged(); + }*/ +} + +void KopeteMetaContactLVI::slotContactAdded( Kopete::Contact *c ) +{ + connect( c, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, + const QVariant &, const QVariant & ) ), + this, SLOT( slotContactPropertyChanged( Kopete::Contact *, const QString &, + const QVariant &, const QVariant & ) ) ); + connect( c->account() , SIGNAL( colorChanged(const QColor& ) ) , this, SLOT( updateContactIcons() ) ); + + updateContactIcon( c ); + + slotContactPropertyChanged( c, QString::fromLatin1("awayMessage"), + QVariant(), c->property( QString::fromLatin1("awayMessage") ).value() ); +} + +void KopeteMetaContactLVI::slotContactRemoved( Kopete::Contact *c ) +{ + disconnect( c, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, + const QVariant &, const QVariant & ) ), + this, SLOT( slotContactPropertyChanged( Kopete::Contact *, + const QString &, const QVariant &, const QVariant & ) ) ); + disconnect( c->account() , SIGNAL( colorChanged(const QColor& ) ) , this, SLOT( updateContactIcons() ) ); + + if ( ListView::Component *comp = contactComponent( c ) ) + delete comp; + + slotContactPropertyChanged( c, QString::fromLatin1("awayMessage"), + c->property( QString::fromLatin1("awayMessage") ).value(), QVariant() ); +} + +void KopeteMetaContactLVI::updateContactIcons() +{ + // show offline contacts setting may have changed + QPtrList<Kopete::Contact> contacts = m_metaContact->contacts(); + for ( QPtrListIterator<Kopete::Contact> it( contacts ); it.current(); ++it ) + updateContactIcon( *it ); +} + +void KopeteMetaContactLVI::updateContactIcon( Kopete::Contact *c ) +{ + KGlobal::config()->setGroup( QString::fromLatin1("ContactList") ); + bool bHideOffline = KGlobal::config()->readBoolEntry( + QString::fromLatin1("HideOfflineContacts"), false ); + if ( KopetePrefs::prefs()->showOffline() ) + bHideOffline = false; + + ListView::ContactComponent *comp = contactComponent( c ); + bool bShow = !bHideOffline || c->isOnline(); + if ( bShow && !comp ) + (void)new ListView::ContactComponent( d->contactIconBox, c, d->contactIconSize ); + else if ( !bShow && comp ) + delete comp; + else if ( comp ) + comp->updatePixmap(); +} + +Kopete::Contact *KopeteMetaContactLVI::contactForPoint( const QPoint &p ) const +{ + if ( ListView::ContactComponent *comp = dynamic_cast<ListView::ContactComponent*>( d->contactIconBox->componentAt( p ) ) ) + return comp->contact(); + return 0L; +} + +ListView::ContactComponent *KopeteMetaContactLVI::contactComponent( const Kopete::Contact *c ) const +{ + for ( uint n = 0; n < d->contactIconBox->components(); ++n ) + { + if ( ListView::ContactComponent *comp = dynamic_cast<ListView::ContactComponent*>( d->contactIconBox->component( n ) ) ) + { + if ( comp->contact() == c ) + return comp; + } + } + return 0; +} + +QRect KopeteMetaContactLVI::contactRect( const Kopete::Contact *c ) const +{ + if ( ListView::Component *comp = contactComponent( c ) ) + return comp->rect(); + return QRect(); +} + +Kopete::Group *KopeteMetaContactLVI::group() +{ + if ( m_parentGroup && m_parentGroup->group() != Kopete::Group::topLevel() ) + return m_parentGroup->group(); + else + return Kopete::Group::topLevel(); +} + +QString KopeteMetaContactLVI::key( int, bool ) const +{ + char importanceChar; + switch ( m_metaContact->status() ) + { + case Kopete::OnlineStatus::Online: + importanceChar = 'A'; + break; + case Kopete::OnlineStatus::Away: + importanceChar = 'B'; + break; + case Kopete::OnlineStatus::Offline: + importanceChar = 'C'; + break; + case Kopete::OnlineStatus::Unknown: + default: + importanceChar = 'D'; + } + + return importanceChar + d->nameText->text().lower(); +} + +bool KopeteMetaContactLVI::isTopLevel() const +{ + return m_isTopLevel; +} + +bool KopeteMetaContactLVI::isGrouped() const +{ + if ( m_parentView ) + return true; + + if ( !m_parentGroup || !m_parentGroup->group() ) + return false; + + if ( m_parentGroup->group() == Kopete::Group::temporary() && !KopetePrefs::prefs()->sortByGroup() ) + return false; + + return true; +} + +void KopeteMetaContactLVI::slotIdleStateChanged( Kopete::Contact *c ) +{ + bool doWeHaveToGrayThatContact = KopetePrefs::prefs()->greyIdleMetaContacts() && ( m_metaContact->idleTime() >= 10 * 60 ); + if ( doWeHaveToGrayThatContact ) + { + d->nameText->setColor( KopetePrefs::prefs()->idleContactColor() ); + if ( d->extraText ) + d->extraText->setColor( KopetePrefs::prefs()->idleContactColor() ); + } + else + { + d->nameText->setDefaultColor(); + if ( d->extraText ) + d->extraText->setDefaultColor(); + } + + if(d->metaContactIcon && d->currentIconMode==KopetePrefs::IconPic) + { + m_oldStatusIcon=d->metaContactIcon->pixmap(); + + QPixmap icon = SmallIcon( m_metaContact->statusIcon(), d->iconSize ); + if ( doWeHaveToGrayThatContact ) + { + // TODO: QPixmapCache this result + KIconEffect::semiTransparent( icon ); + } + + d->metaContactIcon->setPixmap( icon ); + if(mBlinkTimer->isActive()) + m_originalBlinkIcon=icon; + } + // we only need to update the contact icon if one was supplied; + // if none was supplied, we only need to update the MC appearance + if ( c ) + updateContactIcon( c ); + else + return; +} + +void KopeteMetaContactLVI::catchEvent( Kopete::MessageEvent *event ) +{ + d->events.append( event ); + + connect( event, SIGNAL( done( Kopete::MessageEvent* ) ), + this, SLOT( slotEventDone( Kopete::MessageEvent * ) ) ); + + if ( mBlinkTimer->isActive() ) + mBlinkTimer->stop(); + + m_oldStatusIcon= d->metaContactIcon ? d->metaContactIcon->pixmap() : QPixmap(); + + mBlinkTimer->start( 400, false ); + + //show the contact if it was hidden because offline. + updateVisibility(); + } + +void KopeteMetaContactLVI::slotBlink() +{ + bool haveEvent = !d->events.isEmpty(); + if ( mIsBlinkIcon ) + { + if(d->metaContactIcon) + d->metaContactIcon->setPixmap( m_originalBlinkIcon ); + if ( !haveEvent && m_blinkLeft <= 0 ) + { + mBlinkTimer->stop(); + m_oldStatusIcon=d->metaContactIcon ? d->metaContactIcon->pixmap() : QPixmap(); + updateVisibility(); + m_originalBlinkIcon=QPixmap(); //i hope this help to reduce memory consuption + } + } + else + { + if(d->metaContactIcon) + m_originalBlinkIcon=d->metaContactIcon->pixmap(); + if ( haveEvent ) + { + if(d->metaContactIcon) + d->metaContactIcon->setPixmap( SmallIcon( "newmsg", d->iconSize ) ); + } + else + { + if(d->metaContactIcon) + d->metaContactIcon->setPixmap( m_oldStatusIcon ); + m_blinkLeft--; + } + } + + mIsBlinkIcon = !mIsBlinkIcon; +} + +void KopeteMetaContactLVI::slotEventDone( Kopete::MessageEvent *event ) +{ + d->events.remove( event ); + + if ( d->events.isEmpty() ) + { + if ( mBlinkTimer->isActive() ) + { + mBlinkTimer->stop(); + //If the contact gone offline while the timer was actif, + //the visibility has not been correctly updated. so do it now + updateVisibility(); + } + + if(d->metaContactIcon) + d->metaContactIcon->setPixmap( m_originalBlinkIcon ); + m_originalBlinkIcon=QPixmap(); //i hope this help to reduce memory consuption + mIsBlinkIcon = false; + } +} + +QString KopeteMetaContactLVI::text( int column ) const +{ + if ( column == 0 ) + return d->nameText->text(); + else + return KListViewItem::text( column ); +} + +void KopeteMetaContactLVI::setText( int column, const QString &text ) +{ + if ( column == 0 ) + rename( text ); + else + KListViewItem::setText( column, text ); +} + +#include "kopetemetacontactlvi.moc" + +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/kopete/contactlist/kopetemetacontactlvi.h b/kopete/kopete/contactlist/kopetemetacontactlvi.h new file mode 100644 index 00000000..767330ba --- /dev/null +++ b/kopete/kopete/contactlist/kopetemetacontactlvi.h @@ -0,0 +1,191 @@ +/* + kopetemetacontactlvi.h - Kopete Meta Contact KListViewItem + + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002 by Duncan Mac-Vicar P <duncan@kde.org> + + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef __kopetemetacontactlvi_h__ +#define __kopetemetacontactlvi_h__ + +#include "kopetelistviewitem.h" + +#include <qobject.h> +#include <qpixmap.h> +#include <qptrdict.h> + +#include <klistview.h> + +class QVariant; + +class KAction; +class KListAction; + +namespace Kopete +{ +class Account; +class MetaContact; +class Contact; +class Group; +class MessageEvent; +} + +class AddContactPage; +class KopeteGroupViewItem; + + +/** + * @author Martijn Klingens <klingens@kde.org> + */ +class KopeteMetaContactLVI : public Kopete::UI::ListView::Item +{ + Q_OBJECT + +public: + KopeteMetaContactLVI( Kopete::MetaContact *contact, KopeteGroupViewItem *parent ); + KopeteMetaContactLVI( Kopete::MetaContact *contact, QListViewItem *parent ); + KopeteMetaContactLVI( Kopete::MetaContact *contact, QListView *parent ); + ~KopeteMetaContactLVI(); + + /** + * metacontact this visual item represents + */ + Kopete::MetaContact *metaContact() const + { return m_metaContact; }; + + /** + * true if the item is at top level and not under a group + */ + bool isTopLevel() const; + + /** + * parent when top-level + */ + QListView *parentView() const { return m_parentView; }; + + /** + * parent when not top-level + */ + KopeteGroupViewItem *parentGroup() const { return m_parentGroup; }; + + /** + * call this when the item has been moved to a different group + */ + void movedToDifferentGroup(); + void rename( const QString& name ); + void startRename( int ); + + Kopete::Group *group(); + + /** + * Returns the Kopete::Contact of the small little icon at the point p + * @param p must be in the list view item's coordinate system. + * Returns a null pointer if p is not on a small icon. + * (This is used for e.g. the context-menu of a contact when + * right-clicking an icon, or the tooltips) + */ + Kopete::Contact *contactForPoint( const QPoint &p ) const; + + /** + * Returns the QRect small little icon used for the Kopete::Contact. + * The behavior is undefined if @param c doesn't point to a valid + * Kopete::Contact for this list view item. + * The returned QRect is using the list view item's coordinate + * system and should probably be transformed into the list view's + * coordinates before being of any practical use. + * Note that the returned Rect is always vertically stretched to fill + * the full list view item's height, only the width is relative to + * the actual icon width. + */ + QRect contactRect( const Kopete::Contact *c ) const; + + bool isGrouped() const; + + /** + * reimplemented from KListViewItem to take into account our alternate text storage + */ + virtual QString text( int column ) const; + virtual void setText( int column, const QString &text ); + +public slots: + /** + * Call the meta contact's execute as I don't want to expose m_contact + * directly. + */ + void execute() const; + + void catchEvent( Kopete::MessageEvent * ); + + void updateVisibility(); + +private slots: + void slotUpdateMetaContact(); + void slotContactStatusChanged( Kopete::Contact * ); + void slotContactPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ); + void slotContactAdded( Kopete::Contact * ); + void slotContactRemoved( Kopete::Contact * ); + + void slotDisplayNameChanged(); + void slotPhotoChanged(); + + void slotAddToNewGroup(); + void slotIdleStateChanged( Kopete::Contact * =0L); + + void slotConfigChanged(); + + void slotEventDone( Kopete::MessageEvent* ); + void slotBlink(); + + void updateContactIcons(); + +protected: + void okRename(int col); + void cancelRename(int col); + +private: + void initLVI(); + void setDisplayMode( int mode, int iconMode ); + void setMetaContactToolTipSourceForComponent( Kopete::UI::ListView::Component *comp ); + QString key( int column, bool ascending ) const; + void updateContactIcon( Kopete::Contact * ); + Kopete::UI::ListView::ContactComponent *contactComponent( const Kopete::Contact *c ) const; + + Kopete::MetaContact *m_metaContact; + KopeteGroupViewItem *m_parentGroup; + QListView *m_parentView; + bool m_isTopLevel; + + int m_pixelWide; + + Kopete::OnlineStatus m_oldStatus; + QPixmap m_oldStatusIcon; + QPixmap m_originalBlinkIcon; + + QTimer *mBlinkTimer; + + QPtrDict<Kopete::Account> m_addContactActions; + + bool mIsBlinkIcon; + int m_blinkLeft; + + class Private; + Private *d; +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/kopete/contactlist/kopetemetalvipropswidget.ui b/kopete/kopete/contactlist/kopetemetalvipropswidget.ui new file mode 100644 index 00000000..e191d4b9 --- /dev/null +++ b/kopete/kopete/contactlist/kopetemetalvipropswidget.ui @@ -0,0 +1,587 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KopeteMetaLVIPropsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KopeteMetaLVIPropsWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>530</width> + <height>457</height> + </rect> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTabWidget" row="0" column="0"> + <property name="name"> + <cstring>tabWidget</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&General</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>grpAddressbook</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Address Book Link</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="Kopete::UI::AddressBookLinkWidget"> + <property name="name"> + <cstring>widAddresseeLink</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnExportKABC</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>E&xport Details...</string> + </property> + <property name="toolTip" stdset="0"> + <string>Export contact's details to the KDE Address Book</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer8_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>107</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnImportKABC</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Import Contacts</string> + </property> + <property name="toolTip" stdset="0"> + <string>Import contacts from the KDE Address Book</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QButtonGroup" row="1" column="0"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="title"> + <string>Display Name Source</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioNameKABC</cstring> + </property> + <property name="text"> + <string>Use addressbook &name (needs addressbook link)</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioNameContact</cstring> + </property> + <property name="text"> + <string>From contact:</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer8</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QComboBox"> + <property name="name"> + <cstring>cmbAccountName</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" stdset="0"> + <string>Contact to synchronize the displayname with.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout12</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioNameCustom</cstring> + </property> + <property name="text"> + <string>Cus&tom:</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer8_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>50</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLineEdit"> + <property name="name"> + <cstring>edtDisplayName</cstring> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QButtonGroup" row="2" column="0"> + <property name="name"> + <cstring>buttonGroup2</cstring> + </property> + <property name="title"> + <string>Photo Source</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="1" rowspan="3" colspan="1"> + <property name="name"> + <cstring>photoLabel</cstring> + </property> + <property name="minimumSize"> + <size> + <width>64</width> + <height>92</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>64</width> + <height>32767</height> + </size> + </property> + <property name="frameShape"> + <enum>Box</enum> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="text"> + <string>Photo</string> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + </widget> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>radioPhotoKABC</cstring> + </property> + <property name="text"> + <string>U&se addressbook photo (needs addressbook link)</string> + </property> + </widget> + <widget class="QLayoutWidget" row="1" column="0"> + <property name="name"> + <cstring>layout13</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioPhotoContact</cstring> + </property> + <property name="text"> + <string>From contact:</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer9</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QComboBox"> + <property name="name"> + <cstring>cmbAccountPhoto</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" stdset="0"> + <string>Contact to synchronize the displayname with.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget" row="2" column="0"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioPhotoCustom</cstring> + </property> + <property name="text"> + <string>Custom:</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer9_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KURLComboRequester"> + <property name="name"> + <cstring>cmbPhotoUrl</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>btnClearPhoto</cstring> + </property> + <property name="maximumSize"> + <size> + <width>32</width> + <height>32</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </hbox> + </widget> + </grid> + </widget> + <widget class="QCheckBox" row="3" column="0"> + <property name="name"> + <cstring>chkSyncPhoto</cstring> + </property> + <property name="text"> + <string>S&ync photo to addressbook</string> + </property> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>Ad&vanced</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>grpIcons</cstring> + </property> + <property name="title"> + <string>Icons</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>lblAway</cstring> + </property> + <property name="text"> + <string>Awa&y:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>icnbAway</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>lblOnline</cstring> + </property> + <property name="text"> + <string>&Online:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>icnbOnline</cstring> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>chkUseCustomIcons</cstring> + </property> + <property name="text"> + <string>Use custom status &icons</string> + </property> + <property name="toolTip" stdset="0"> + <string>Check to set custom icons for this contact</string> + </property> + </widget> + <widget class="KIconButton" row="2" column="1"> + <property name="name"> + <cstring>icnbAway</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KIconButton" row="1" column="1"> + <property name="name"> + <cstring>icnbOnline</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KIconButton" row="1" column="3"> + <property name="name"> + <cstring>icnbOffline</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KIconButton" row="2" column="3"> + <property name="name"> + <cstring>icnbUnknown</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>lblOffline</cstring> + </property> + <property name="text"> + <string>O&ffline:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>icnbOffline</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="2"> + <property name="name"> + <cstring>lblUnknown</cstring> + </property> + <property name="text"> + <string>Un&known:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>icnbUnknown</cstring> + </property> + </widget> + <spacer row="1" column="4"> + <property name="name"> + <cstring>spacerHorizontal1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + </spacer> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer12</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>170</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </widget> + </grid> +</widget> +<tabstops> + <tabstop>tabWidget</tabstop> + <tabstop>btnExportKABC</tabstop> + <tabstop>btnImportKABC</tabstop> + <tabstop>edtDisplayName</tabstop> + <tabstop>cmbAccountName</tabstop> + <tabstop>cmbAccountPhoto</tabstop> + <tabstop>chkSyncPhoto</tabstop> + <tabstop>chkUseCustomIcons</tabstop> + <tabstop>icnbOnline</tabstop> + <tabstop>icnbAway</tabstop> + <tabstop>icnbOffline</tabstop> + <tabstop>icnbUnknown</tabstop> +</tabstops> +<customwidgets> + <customwidget> + <class>Kopete::UI::AddressBookLinkWidget</class> + <header>addressbooklinkwidget.h</header> + </customwidget> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>addressbooklinkwidget.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kicondialog.h</includehint> + <includehint>kicondialog.h</includehint> + <includehint>kicondialog.h</includehint> + <includehint>kicondialog.h</includehint> +</includehints> +</UI> diff --git a/kopete/kopete/contactlist/kopetestatusgroupviewitem.cpp b/kopete/kopete/contactlist/kopetestatusgroupviewitem.cpp new file mode 100644 index 00000000..9dc910dd --- /dev/null +++ b/kopete/kopete/contactlist/kopetestatusgroupviewitem.cpp @@ -0,0 +1,51 @@ +/* + kopetestatusgroupviewitem.cpp + + Class to show a status folder + + Copyright (c) 2001-2003 by Duncan Mac-Vicar Prett <duncan@kde.org> + + Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <kdebug.h> +#include "kopetestatusgroupviewitem.h" + +KopeteStatusGroupViewItem::KopeteStatusGroupViewItem( Kopete::OnlineStatus::StatusType status_ , QListView *parent, const char *name ) + : QListViewItem(parent,name) +{ + m_status = status_; +} + +KopeteStatusGroupViewItem::~KopeteStatusGroupViewItem() +{ +} + +QString KopeteStatusGroupViewItem::key( int, bool ) const +{ + switch (m_status) + { + case Kopete::OnlineStatus::Online : + return "A"; + break; + case Kopete::OnlineStatus::Away : + return "B"; + break; + case Kopete::OnlineStatus::Offline : + return "C"; + break; + case Kopete::OnlineStatus::Unknown : + default: + return "D"; + } +} + diff --git a/kopete/kopete/contactlist/kopetestatusgroupviewitem.h b/kopete/kopete/contactlist/kopetestatusgroupviewitem.h new file mode 100644 index 00000000..8b1a930f --- /dev/null +++ b/kopete/kopete/contactlist/kopetestatusgroupviewitem.h @@ -0,0 +1,43 @@ +/* + kopetestatusgroupviewitem.h + + Class to show a status folder + + Copyright (c) 2001-2003 by Duncan Mac-Vicar Prett <duncan@kde.org> + + Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef KOPETESTATUSGROUPVIEWITEM_H +#define KOPETESTATUSGROUPVIEWITEM_H + +#include <klistview.h> +#include "kopetemetacontact.h" + +/** + *@author Duncan Mac-Vicar Prett <duncan@kde.org> + */ + + class KopeteStatusGroupViewItem : public QListViewItem +{ +public: + KopeteStatusGroupViewItem( Kopete::OnlineStatus::StatusType status_ , QListView *parent, const char *name=0); + ~KopeteStatusGroupViewItem(); + +private: + + Kopete::OnlineStatus::StatusType m_status; + QString key( int column, bool ascending ) const; + +}; + +#endif |