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

2135 lines
64 KiB

/*
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 <tqcursor.h>
#include <tqdragobject.h>
#include <tqheader.h>
#include <tqstylesheet.h>
#include <tqtimer.h>
#include <tqtooltip.h>
#include <tqguardedptr.h>
#include <tdeaction.h>
#include <tdeapplication.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <klocale.h>
#include <tdemainwindow.h>
#include <kmessagebox.h>
#include <tdepopupmenu.h>
#include <kurldrag.h>
#include <tdemultipledrag.h>
#include <kabc/stdaddressbook.h>
#include <kabc/vcardconverter.h>
#include <tdeversion.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( TDEListView *view );
};
class ContactListViewStrategy
{
public:
ContactListViewStrategy( TDEListView *view )
: _listView( view )
{
view->clear();
}
virtual ~ContactListViewStrategy() {}
TDEListView *listView() { return _listView; }
void addCurrentItems()
{
// Add the already existing groups now
TQPtrList<Kopete::Group> grps = Kopete::ContactList::self()->groups();
for ( TQPtrListIterator<Kopete::Group> groupIt( grps ); groupIt.current(); ++groupIt )
addGroup( groupIt.current() );
// Add the already existing meta contacts now
TQPtrList<Kopete::MetaContact> metaContacts = Kopete::ContactList::self()->metaContacts();
for ( TQPtrListIterator<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 TQListView design stupidity.
// GroupViewItem will be TQListView-derived, or TQListViewItem-derived.
template<typename GroupViewItem>
void addMetaContactToGroupInner( Kopete::MetaContact *mc, GroupViewItem *gpi )
{
// check if the contact isn't already in the group
for( TQListViewItem *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;
TQListViewItem* item = gpi->firstChild();
while(item) {
mci = dynamic_cast<KopeteMetaContactLVI*>(item);
item = item->nextSibling();
if ( mci && mci->metaContact() == mc )
delete mci;
}
}
private:
TDEListView *_listView;
};
class ArrangeByGroupsViewStrategy : public ContactListViewStrategy
{
public:
ArrangeByGroupsViewStrategy( TDEListView *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( TQListViewItem *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( TDEListView *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"
TQListViewItem *currentGroup = mc->isOnline() ? m_onlineItem : m_offlineItem;
for( TQListViewItem *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
TQListViewItem *oppositeGroup = mc->isOnline() ? m_offlineItem : m_onlineItem;
for( TQListViewItem *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;
TQGuardedPtr<KopeteGroupViewItem> m_temporaryItem;
};
void KopeteContactListViewPrivate::updateViewStrategy( TDEListView *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 TQLVI::itemBelow but does not depend on visibility of items, etc.
static TQListViewItem *nextItem( TQListViewItem *item )
{
if ( TQListViewItem *it = item->firstChild() )
return it;
while ( item && !item->nextSibling() )
item = item->parent();
if ( !item )
return 0;
return item->nextSibling();
}
KopeteContactListView::KopeteContactListView( TQWidget *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, TQT_SIGNAL( contextMenu( TDEListView *, TQListViewItem *, const TQPoint & ) ),
TQT_SLOT( slotContextMenu( TDEListView *, TQListViewItem *, const TQPoint & ) ) );
connect( this, TQT_SIGNAL( expanded( TQListViewItem * ) ),
TQT_SLOT( slotExpanded( TQListViewItem * ) ) );
connect( this, TQT_SIGNAL( collapsed( TQListViewItem * ) ),
TQT_SLOT( slotCollapsed( TQListViewItem * ) ) );
connect( this, TQT_SIGNAL( executed( TQListViewItem *, const TQPoint &, int ) ),
TQT_SLOT( slotExecuted( TQListViewItem *, const TQPoint &, int ) ) );
connect( this, TQT_SIGNAL( selectionChanged() ), TQT_SLOT( slotViewSelectionChanged() ) );
connect( this, TQT_SIGNAL( itemRenamed( TQListViewItem * ) ),
TQT_SLOT( slotItemRenamed( TQListViewItem * ) ) );
connect( KopetePrefs::prefs(), TQT_SIGNAL( saved() ), TQT_SLOT( slotSettingsChanged() ) );
connect( Kopete::ContactList::self(), TQT_SIGNAL( selectionChanged() ),
TQT_SLOT( slotListSelectionChanged() ) );
connect( Kopete::ContactList::self(),
TQT_SIGNAL( metaContactAdded( Kopete::MetaContact * ) ),
TQT_SLOT( slotMetaContactAdded( Kopete::MetaContact * ) ) );
connect( Kopete::ContactList::self(),
TQT_SIGNAL( metaContactRemoved( Kopete::MetaContact * ) ),
TQT_SLOT( slotMetaContactDeleted( Kopete::MetaContact * ) ) );
connect( Kopete::ContactList::self(), TQT_SIGNAL( groupAdded( Kopete::Group * ) ),
TQT_SLOT( slotGroupAdded( Kopete::Group * ) ) );
connect( Kopete::ChatSessionManager::self(), TQT_SIGNAL( newEvent( Kopete::MessageEvent * ) ),
this, TQT_SLOT( slotNewMessageEvent( Kopete::MessageEvent * ) ) );
connect( this, TQT_SIGNAL( dropped( TQDropEvent *, TQListViewItem *, TQListViewItem * ) ),
this, TQT_SLOT( slotDropped( TQDropEvent *, TQListViewItem *, TQListViewItem * ) ) );
connect( &undoTimer, TQT_SIGNAL(timeout()) , this, TQT_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( TQListView::Extended );
// Load in the user's initial settings
slotSettingsChanged();
}
void KopeteContactListView::initActions( TDEActionCollection *ac )
{
actionUndo = KStdAction::undo( TQT_TQOBJECT(this) , TQT_SLOT( slotUndo() ) , ac );
actionRedo = KStdAction::redo( TQT_TQOBJECT(this) , TQT_SLOT( slotRedo() ) , ac );
actionUndo->setEnabled(false);
actionRedo->setEnabled(false);
new TDEAction( i18n( "Create New Group..." ), 0, 0, TQT_TQOBJECT(this), TQT_SLOT( addGroup() ),
ac, "AddGroup" );
actionSendMessage = KopeteStdAction::sendMessage(
TQT_TQOBJECT(this), TQT_SLOT( slotSendMessage() ), ac, "contactSendMessage" );
actionStartChat = KopeteStdAction::chat( TQT_TQOBJECT(this), TQT_SLOT( slotStartChat() ),
ac, "contactStartChat" );
actionMove = new KopeteGroupListAction( i18n( "&Move To" ), TQString::fromLatin1( "editcut" ),
0, TQT_TQOBJECT(this), TQT_SLOT( slotMoveToGroup() ), ac, "contactMove" );
actionCopy = new KopeteGroupListAction( i18n( "&Copy To" ), TQString::fromLatin1( "editcopy" ), 0,
TQT_TQOBJECT(this), TQT_SLOT( slotCopyToGroup() ), ac, "contactCopy" );
actionRemove = KopeteStdAction::deleteContact( TQT_TQOBJECT(this), TQT_SLOT( slotRemove() ),
ac, "contactRemove" );
actionSendEmail = new TDEAction( i18n( "Send Email..." ), TQString::fromLatin1( "mail_generic" ),
0, TQT_TQOBJECT(this), TQT_SLOT( slotSendEmail() ), ac, "contactSendEmail" );
/* this actionRename is buggy, and useless with properties, removed in kopeteui.rc*/
actionRename = new TDEAction( i18n( "Rename" ), "filesaveas", 0,
TQT_TQOBJECT(this), TQT_SLOT( slotRename() ), ac, "contactRename" );
actionSendFile = KopeteStdAction::sendFile( TQT_TQOBJECT(this), TQT_SLOT( slotSendFile() ),
ac, "contactSendFile" );
actionAddContact = new TDEActionMenu( i18n( "&Add Contact" ),
TQString::fromLatin1( "add_user" ), ac , "contactAddContact" );
actionAddContact->popupMenu()->insertTitle( i18n("Select Account") );
actionAddTemporaryContact = new TDEAction( i18n( "Add to Your Contact List" ), "add_user", 0,
TQT_TQOBJECT(this), TQT_SLOT( slotAddTemporaryContact() ), ac, "contactAddTemporaryContact" );
connect( Kopete::ContactList::self(), TQT_SIGNAL( metaContactSelected( bool ) ), this, TQT_SLOT( slotMetaContactSelected( bool ) ) );
connect( Kopete::AccountManager::self(), TQT_SIGNAL(accountRegistered( Kopete::Account* )), TQT_SLOT(slotAddSubContactActionNewAccount(Kopete::Account*)));
connect( Kopete::AccountManager::self(), TQT_SIGNAL(accountUnregistered( const Kopete::Account* )), TQT_SLOT(slotAddSubContactActionAccountDeleted(const Kopete::Account *)));
actionProperties = new TDEAction( i18n( "&Properties" ), "edit_user", TQt::Key_Alt + TQt::Key_Return,
TQT_TQOBJECT(this), TQT_SLOT( slotProperties() ), ac, "contactProperties" );
// Update enabled/disabled actions
slotViewSelectionChanged();
}
KopeteContactListView::~KopeteContactListView()
{
delete d;
}
void KopeteContactListView::slotAddSubContactActionNewAccount(Kopete::Account* account)
{
TDEAction *action = new TDEAction( account->accountLabel(), account->accountIcon(), 0 , TQT_TQOBJECT(this), TQT_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 ) )
{
TDEAction *action = m_accountAddContactMap[account];
m_accountAddContactMap.remove( account );
actionAddContact->remove( action );
}
}
void KopeteContactListView::slotMetaContactAdded( Kopete::MetaContact *mc )
{
d->viewStrategy->addMetaContact( mc );
connect( mc, TQT_SIGNAL( addedToGroup( Kopete::MetaContact *, Kopete::Group * ) ),
TQT_SLOT( slotAddedToGroup( Kopete::MetaContact *, Kopete::Group * ) ) );
connect( mc, TQT_SIGNAL( removedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ),
TQT_SLOT( slotRemovedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ) );
connect( mc, TQT_SIGNAL( movedToGroup( Kopete::MetaContact *, Kopete::Group *, Kopete::Group * ) ),
TQT_SLOT( slotMovedToGroup( Kopete::MetaContact *, Kopete::Group *, Kopete::Group * ) ) );
connect( mc, TQT_SIGNAL( onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType ) ),
TQT_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()
{
TQString groupName =
KInputDialog::getText( i18n( "New Group" ),
i18n( "Please enter the name for the new group:" ) );
if ( !groupName.isEmpty() )
addGroup( groupName );
}
void KopeteContactListView::addGroup( const TQString &groupName )
{
d->viewStrategy->addGroup( Kopete::ContactList::self()->findGroup(groupName) );
}
void KopeteContactListView::slotGroupAdded( Kopete::Group *group )
{
d->viewStrategy->addGroup( group );
}
void KopeteContactListView::slotExpanded( TQListViewItem *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( TQListViewItem *item )
{
KopeteGroupViewItem *groupLVI = dynamic_cast<KopeteGroupViewItem*>( item );
if ( groupLVI )
{
groupLVI->group()->setExpanded( false );
groupLVI->updateIcon();
}
}
void KopeteContactListView::slotContextMenu( TDEListView * /*listview*/,
TQListViewItem *item, const TQPoint &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( TQPtrList<Kopete::MetaContact>() , TQPtrList<Kopete::Group>() );
}
int nb = Kopete::ContactList::self()->selectedMetaContacts().count() +
Kopete::ContactList::self()->selectedGroups().count();
TDEMainWindow *window = dynamic_cast<TDEMainWindow *>(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( TQPoint( px, py ) ) ;
if ( c )
{
TDEPopupMenu *p = c->popupMenu();
connect( p, TQT_SIGNAL( aboutToHide() ), p, TQT_SLOT( deleteLater() ) );
p->popup( point );
}
else
{
TDEPopupMenu *popup = dynamic_cast<TDEPopupMenu *>(
window->factory()->container( "contact_popup", window ) );
if ( popup )
{
TQString title = i18n( "Translators: format: '<nickname> (<online status>)'", "%1 (%2)" ).
arg( metaLVI->metaContact()->displayName(), metaLVI->metaContact()->statusString() );
if ( title.length() > 43 )
title = title.left( 40 ) + TQString::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
TQPtrList<Kopete::Contact> it = metaLVI->metaContact()->contacts();
for( Kopete::Contact *c = it.first(); c; c = it.next() )
{
if( sep )
{
popup->insertSeparator();
sep = false;
}
TDEPopupMenu *contactMenu = it.current()->popupMenu();
connect( popup, TQT_SIGNAL( aboutToHide() ), contactMenu, TQT_SLOT( deleteLater() ) );
TQString nick=c->property(Kopete::Global::Properties::self()->nickName()).value().toString();
TQString 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 ) + TQString::fromLatin1( "..." );
popup->insertItem( c->onlineStatus().iconFor( c, 16 ), text , contactMenu );
}
popup->popup( point );
}
}
}
else if ( groupvi && nb == 1 )
{
TDEPopupMenu *popup = dynamic_cast<TDEPopupMenu *>(
window->factory()->container( "group_popup", window ) );
if ( popup )
{
TQString title = groupvi->group()->displayName();
if ( title.length() > 32 )
title = title.left( 30 ) + TQString::fromLatin1( "..." );
if( popup->title( 0 ).isNull() )
popup->insertTitle( title, 0, 0 );
else
popup->changeTitle( 0, title );
popup->popup( point );
}
}
else if ( nb >= 1 )
{
TDEPopupMenu *popup = dynamic_cast<TDEPopupMenu *>(
window->factory()->container( "contactlistitems_popup", window ) );
if ( popup )
popup->popup( point );
}
else
{
TDEPopupMenu *popup = dynamic_cast<TDEPopupMenu *>(
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 ( TQListViewItem *it = firstChild(); it; it = it->nextSibling() )
if ( KopeteGroupViewItem *gpi = dynamic_cast<KopeteGroupViewItem*>( it ) )
gpi->updateIcon();
}
void KopeteContactListView::slotExecuted( TQListViewItem *item, const TQPoint &p, int /* col */ )
{
item->setSelected( false );
KopeteMetaContactLVI *metaContactLVI = dynamic_cast<KopeteMetaContactLVI *>( item );
TQPoint 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
TQRect r = itemRect( item );
TQPoint 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(TQDropEvent *e, TQListViewItem *, TQListViewItem *after)
{
if(!acceptDrag(e))
return;
KopeteMetaContactLVI *dest_metaLVI=dynamic_cast<KopeteMetaContactLVI*>(after);
KopeteGroupViewItem *dest_groupLVI=dynamic_cast<KopeteGroupViewItem*>(after);
if( const_cast<const TQWidget *>( e->source() ) == this )
{
TQPtrListIterator<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") )
{
TQString contactInfo = TQString::fromUtf8( e->encodedData("kopete/x-contact") );
TQString protocolId = contactInfo.section( TQChar( 0xE000 ), 0, 0 );
TQString accountId = contactInfo.section( TQChar( 0xE000 ), 1, 1 );
TQString contactId = contactInfo.section( TQChar( 0xE000 ), 2 );
addDraggedContactByInfo( protocolId, accountId, contactId, after );
}
else if( e->provides("text/uri-list") )
{
if ( !TQUriDrag::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() == TQString::fromLatin1("kopetemessage") )
{
//Add a contact
addDraggedContactByInfo( url.queryItem("protocolId"),
url.queryItem("accountId"), url.host(), after );
}
else if( dest_metaLVI )
{
TQPoint 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( TQPoint( 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 TQString &protocolId, const TQString &accountId,
const TQString &contactId, TQListViewItem *after )
{
kdDebug(14000) << k_funcinfo << "protocolId=" << protocolId <<
", accountId=" << accountId << ", contactId=" << contactId << endl;
Kopete::Account *account = Kopete::AccountManager::self()->findAccount( protocolId,accountId );
if( account )
{
TQDict<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(TQDropEvent *e) const
{
TQListViewItem *source=currentItem();
TQListViewItem *parent;
TQListViewItem *afterme;
// Due to a little design problem in TDEListView::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 TQWidget *>( 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 )
{
TQPoint 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( TQPoint( 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 ( !TQUriDrag::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() != TQString::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
{
TQString text;
TQTextDrag::decode(e, text);
kdDebug(14000) << k_funcinfo << "drop with mimetype:" << e->format() << " data as text:" << text << endl;
}
}
return false;
}
void KopeteContactListView::findDrop(const TQPoint &pos, TQListViewItem *&parent,
TQListViewItem *&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;
TQPoint p (contentsToViewport(pos));
after=itemAt(p);
}
void KopeteContactListView::contentsMousePressEvent( TQMouseEvent *e )
{
TDEListView::contentsMousePressEvent( e );
if (e->button() == Qt::LeftButton )
{
TQPoint p=contentsToViewport(e->pos());
TQListViewItem *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 = TQPoint( 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 ( TQListViewItem *item = firstChild(); item; item = nextItem(item) )
if ( KopeteMetaContactLVI *li = dynamic_cast<KopeteMetaContactLVI*>(item) )
if ( li->metaContact() == m )
li->catchEvent(event);
}
}
TQDragObject *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
TQListViewItem *currentLVI = currentItem();
if( !currentLVI )
return 0L;
KopeteMetaContactLVI *metaLVI = dynamic_cast<KopeteMetaContactLVI*>( currentLVI );
if( !metaLVI )
return 0L;
TQPixmap pm;
Kopete::Contact *c = metaLVI->contactForPoint( m_startDragPos );
KMultipleDrag *drag = new KMultipleDrag( this );
drag->addDragObject( new TQStoredDrag("application/x-qlistviewitem", 0L ) );
TQStoredDrag *d = new TQStoredDrag("kopete/x-metacontact", 0L );
d->setEncodedData( metaLVI->metaContact()->metaContactId().utf8() );
drag->addDragObject( d );
if ( c ) // dragging a contact
{
TQStoredDrag *d = new TQStoredDrag("kopete/x-contact", 0L );
d->setEncodedData( TQString( c->protocol()->pluginId() +TQChar( 0xE000 )+ c->account()->accountId() +TQChar( 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?
//TQPainter 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 TQTextDrag( address.fullEmail(), 0L ) );
KABC::VCardConverter converter;
TQString vcard = converter.createVCard( address );
if( !vcard.isNull() )
{
TQStoredDrag *vcardDrag = new TQStoredDrag("text/x-vcard", 0L );
vcardDrag->setEncodedData( vcard.utf8() );
drag->addDragObject( vcardDrag );
}
}
//TQSize s = pm.size();
drag->setPixmap( pm /*, TQPoint( s.width() , s.height() )*/ );
return drag;
}
void KopeteContactListView::slotViewSelectionChanged()
{
TQPtrList<Kopete::MetaContact> contacts;
TQPtrList<Kopete::Group> groups;
m_selectedContacts.clear();
m_selectedGroups.clear();
TQListViewItemIterator it( this );
while ( it.current() )
{
TQListViewItem *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()
{
TQPtrList<Kopete::MetaContact> contacts = Kopete::ContactList::self()->selectedMetaContacts();
TQPtrList<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(
TQPtrList<Kopete::MetaContact> contacts, TQPtrList<Kopete::Group> groups )
{
bool singleContactSelected = groups.isEmpty() && contacts.count() == 1;
bool inkabc=false;
if(singleContactSelected)
{
TQString 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() )
{
TQString emailAddr = addressee.fullEmail();
kdDebug( 14000 ) << "Email: " << emailAddr << "!" << endl;
if ( !emailAddr.isEmpty() )
kapp->invokeMailer( emailAddr, TQString() );
else
KMessageBox::queuedMessageBox( this, KMessageBox::Sorry, i18n( "There is no email address set for this contact in the TDE address book." ), i18n( "No Email Address in Address Book" ) );
}
else
KMessageBox::queuedMessageBox( this, KMessageBox::Sorry, i18n( "This contact was not found in the TDE 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 TDE 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()
{
TQPtrList<Kopete::MetaContact> contacts = Kopete::ContactList::self()->selectedMetaContacts();
TQPtrList<Kopete::Group> groups = Kopete::ContactList::self()->selectedGroups();
if(groups.count() + contacts.count() == 0)
return;
TQStringList 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
TQString 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
{
TQString 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.
TQListViewItemIterator lvi_it( this );
while ( lvi_it.current() )
{
TQListViewItem *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() )
{
TQPtrList<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*>( TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(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() == TQDialog::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( TQListViewItem */*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
TQStringList 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 TQString 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 TQString old=m->displayName();
if( m_redo->args[1].isEmpty() )
{
const TQString 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 TQString oldName = m->displayName();
TQPtrList< Kopete::Contact > cList = m->contacts();
TQPtrListIterator< 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 TQString 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: