2
* discodlg.cpp - main dialog for the Service Discovery protocol
3
* Copyright (C) 2003 Michail Pishchagin
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License
7
* as published by the Free Software Foundation; either version 2
8
* of the License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this library; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
#include <qlistview.h>
24
#include <qcombobox.h>
25
#include <qcheckbox.h>
26
#include <qmessagebox.h>
28
#include <qstatusbar.h>
29
#include <qpopupmenu.h>
30
#include <qsignalmapper.h>
31
#include <qpushbutton.h>
32
#include <qdragobject.h>
34
#include <qtoolbutton.h>
37
#include "xmpp_tasks.h"
39
#include "psitoolbar.h"
41
#include "psiaccount.h"
43
#include "busywidget.h"
45
#include "iconaction.h"
47
//----------------------------------------------------------------------------
48
// DiscoData -- a shared data struct
49
//----------------------------------------------------------------------------
52
class DiscoConnector : public QObject
56
DiscoConnector(QObject *parent)
60
void itemUpdated(QListViewItem *);
63
friend class DiscoListItem;
81
//----------------------------------------------------------------------------
83
//----------------------------------------------------------------------------
85
class ProtocolAction : public QAction
89
ProtocolAction(QString text, QString toolTip, QObject *parent, QSignalMapper *sm, int parm);
91
bool addTo(QWidget *w);
99
ProtocolAction::ProtocolAction(QString text, QString toolTip, QObject *parent, QSignalMapper *sm, int parm)
100
: QAction(text, text, 0, parent)
103
setToggleAction(true);
105
connect(this, SIGNAL(activated()), sm, SLOT(map()));
106
sm->setMapping(this, parm);
110
bool ProtocolAction::addTo(QWidget *w)
112
if ( w->inherits("QToolBar") ) {
115
QCString bname = name() + QCString("_action_button");
116
btn = new QToolButton ( w, bname );
118
btn->setToggleButton ( isToggleAction() );
119
btn->setOn( isOn() );
120
btn->setTextLabel ( text() );
121
btn->setEnabled ( isEnabled() );
122
btn->setUsesTextLabel( true );
123
QToolTip::add(btn, toolTip());
125
connect(btn, SIGNAL(toggled(bool)), this, SLOT(setOn(bool)));
133
void ProtocolAction::setOn(bool b)
144
//----------------------------------------------------------------------------
146
//----------------------------------------------------------------------------
148
class DiscoListItem : public QObject, public QListViewItem
152
DiscoListItem(DiscoItem it, DiscoData *d, QListView *parent);
153
DiscoListItem(DiscoItem it, DiscoData *d, QListViewItem *parent);
156
QString text(int columns) const;
157
void setOpen(bool open);
158
const DiscoItem &item() const;
162
public slots: // the two are used internally by class, and also called by DiscoDlg::Private::refresh()
164
void updateItems(bool parentAutoItems = false);
167
void discoItemsFinished();
168
void discoInfoFinished();
170
void doBrowse(bool parentAutoItems = false);
171
void doAgents(bool parentAutoItems = false);
172
void browseFinished();
173
void agentsFinished();
179
bool alreadyItems, alreadyInfo;
180
bool autoItems; // used in updateItemsFinished
183
void copyItem(const DiscoItem &);
184
void updateInfo(const DiscoItem &);
185
void updateItemsFinished(const DiscoList &);
186
void autoItemsChildren() const; // automatically call disco#items for children :-)
187
QString hash() { return computeHash( item().jid().full(), item().node() ); }
188
QString computeHash( QString jid, QString node );
191
void init(DiscoItem it, DiscoData *dd);
193
bool autoItemsEnabled() const;
194
bool autoInfoEnabled() const;
195
DiscoDlg *dlg() const;
198
DiscoListItem::DiscoListItem(DiscoItem it, DiscoData *_d, QListView *parent)
199
: QListViewItem (parent)
206
DiscoListItem::DiscoListItem(DiscoItem it, DiscoData *_d, QListViewItem *parent)
207
: QListViewItem (parent)
214
DiscoListItem::~DiscoListItem()
218
void DiscoListItem::init(DiscoItem _item, DiscoData *_d)
224
alreadyItems = alreadyInfo = false;
226
if ( !autoItemsEnabled() )
227
setExpandable (true);
230
if ( autoInfoEnabled() || isRoot ) {
236
//setDragEnabled(true); // EXPERIMENTAL
239
void DiscoListItem::copyItem(const DiscoItem &it)
241
if ( !(!di.jid().full().isEmpty() && it.jid().full().isEmpty()) )
242
di.setJid ( it.jid() );
243
if ( !(!di.node().isEmpty() && it.node().isEmpty()) )
244
di.setNode ( it.node() );
245
if ( !(!di.name().isEmpty() && it.name().isEmpty()) )
246
di.setName ( it.name() );
248
di.setAction ( it.action() );
250
if ( !(!di.features().list().isEmpty() && it.features().list().isEmpty()) )
251
di.setFeatures ( it.features() );
252
if ( !(!di.identities().isEmpty() && it.identities().isEmpty()) )
253
di.setIdentities ( it.identities() );
255
if ( di.jid().userHost().left(4) == "jud." || di.jid().userHost().left(6) == "users." ) {
256
// nasty hack for the nasty (and outdated) JUD service :-/
257
if ( !di.features().canSearch() ) {
258
QStringList features = di.features().list();
259
features << "jabber:iq:search";
260
di.setFeatures( features );
264
DiscoItem::Identities::ConstIterator it = di.identities().begin();
265
for ( ; it != di.identities().end(); ++it) {
266
if ( (*it).category == "service" && (*it).type == "jud" ) {
272
DiscoItem::Identity id;
273
id.category = "service";
275
DiscoItem::Identities ids;
277
di.setIdentities( ids );
281
bool pixmapOk = false;
282
if ( !di.identities().isEmpty() ) {
283
DiscoItem::Identity id = di.identities().first();
285
if ( !id.category.isEmpty() ) {
286
Icon ic = category2icon(id.category, id.type);
288
if ( !ic.impix().isNull() ) {
289
setPixmap (0, ic.impix().pixmap());
296
setPixmap (0, is->status(di.jid(), STATUS_ONLINE));
300
if ( isSelected() ) // update actions
301
emit d->d->itemUpdated( this );
304
QString DiscoListItem::text (int c) const
309
return di.jid().full();
315
const DiscoItem &DiscoListItem::item() const
320
DiscoDlg *DiscoListItem::dlg() const
322
return (DiscoDlg *)listView()->parent()->parent();
325
bool DiscoListItem::autoItemsEnabled() const
327
return dlg()->ck_autoItems->isChecked();
330
bool DiscoListItem::autoInfoEnabled() const
332
return dlg()->ck_autoInfo->isChecked();
335
void DiscoListItem::setOpen (bool o)
344
QListViewItem::setOpen(o);
347
void DiscoListItem::itemSelected()
353
void DiscoListItem::updateItems(bool parentAutoItems)
355
if ( parentAutoItems ) {
360
// FIXME: currently, JUD doesn't seem to answer to browsing requests
361
if ( item().identities().size() ) {
362
DiscoItem::Identity id = item().identities().first();
363
if ( id.category == "service" && id.type == "jud" )
366
QString j = item().jid().host(); // just another method to discover if we're gonna to browse JUD
367
if ( item().jid().user().isEmpty() && (j.left(4) == "jud." || j.left(6) == "users.") )
371
autoItems = !parentAutoItems;
373
if ( !autoItemsEnabled() )
376
if ( d->protocol == DiscoData::Auto || d->protocol == DiscoData::Disco ) {
377
JT_DiscoItems *jt = new JT_DiscoItems(d->pa->client()->rootTask());
378
connect(jt, SIGNAL(finished()), SLOT(discoItemsFinished()));
379
jt->get(di.jid(), di.node());
381
d->tasks->append(jt);
383
else if ( d->protocol == DiscoData::Browse )
384
doBrowse(parentAutoItems);
385
else if ( d->protocol == DiscoData::Agents )
386
doAgents(parentAutoItems);
389
void DiscoListItem::discoItemsFinished()
391
JT_DiscoItems *jt = (JT_DiscoItems *)sender();
393
if ( jt->success() ) {
394
updateItemsFinished(jt->items());
396
else if ( d->protocol == DiscoData::Auto ) {
400
else if ( !autoItems ) {
401
QString error = jt->statusString();
402
QMessageBox::critical(dlg(), tr("Error"), tr("There was an error getting items for <b>%1</b>.\nReason: %2").arg(di.jid().full()).arg(error));
408
void DiscoListItem::doBrowse(bool parentAutoItems)
410
if ( parentAutoItems ) {
415
if ( item().identities().size() ) {
416
DiscoItem::Identity id = item().identities().first();
417
if ( id.category == "service" && id.type == "jud" )
422
autoItems = !parentAutoItems;
423
if ( !autoItemsEnabled() )
426
JT_Browse *jt = new JT_Browse(d->pa->client()->rootTask());
427
connect(jt, SIGNAL(finished()), SLOT(browseFinished()));
430
d->tasks->append(jt);
433
void DiscoListItem::browseFinished()
435
JT_Browse *jt = (JT_Browse *)sender();
437
if ( jt->success() ) {
440
root.fromAgentItem( jt->root() );
446
AgentList from = jt->agents();
448
AgentList::Iterator it = from.begin();
449
for ( ; it != from.end(); ++it) {
451
item.fromAgentItem( *it );
456
updateItemsFinished(to);
458
else if ( d->protocol == DiscoData::Auto ) {
462
else if ( !autoItems ) {
463
QString error = jt->statusString();
464
QMessageBox::critical(dlg(), tr("Error"), tr("There was an error browsing items for <b>%1</b>.\nReason: %2").arg(di.jid().full()).arg(error));
470
void DiscoListItem::doAgents(bool parentAutoItems)
472
if ( parentAutoItems ) {
477
if ( item().identities().size() ) {
478
DiscoItem::Identity id = item().identities().first();
479
if ( id.category == "service" && id.type == "jud" )
484
autoItems = !parentAutoItems;
485
if ( !autoItemsEnabled() )
488
JT_GetServices *jt = new JT_GetServices(d->pa->client()->rootTask());
489
connect(jt, SIGNAL(finished()), SLOT(agentsFinished()));
492
d->tasks->append(jt);
495
void DiscoListItem::agentsFinished()
497
JT_GetServices *jt = (JT_GetServices *)sender();
499
if ( jt->success() ) {
502
DiscoItem::Identity id;
503
id.name = tr("Jabber Service");
504
id.category = "service";
506
DiscoItem::Identities ids;
508
root.setIdentities(ids);
514
AgentList from = jt->agents();
516
AgentList::Iterator it = from.begin();
517
for ( ; it != from.end(); ++it) {
519
item.fromAgentItem( *it );
524
updateItemsFinished(to);
526
else if ( !autoItems ) {
527
QString error = jt->statusString();
528
QMessageBox::critical(dlg(), tr("Error"), tr("There was an error getting agents for <b>%1</b>.\nReason: %2").arg(di.jid().full()).arg(error));
534
QString DiscoListItem::computeHash( QString jid, QString node )
536
QString ret = jid.replace( '@', "\\@" );
538
ret += node.replace( '@', "\\@" );
542
void DiscoListItem::updateItemsFinished(const DiscoList &list)
544
QDict<DiscoListItem> children;
545
DiscoListItem *child = (DiscoListItem *)firstChild();
547
children.insert( child->hash(), child );
549
child = (DiscoListItem *)child->nextSibling();
553
for(DiscoList::ConstIterator it = list.begin(); it != list.end(); ++it) {
554
const DiscoItem a = *it;
556
QString key = computeHash(a.jid().full(), a.node());
557
DiscoListItem *child = children[ key ];
560
child->copyItem ( a );
561
children.remove( key );
564
new DiscoListItem (a, d, this);
568
// remove all items that are not on new DiscoList
569
children.setAutoDelete( true );
572
if ( autoItems && isOpen() )
575
// don't forget to remove '+' (or '-') sign in case, that the child list is empty
576
setExpandable ( !list.isEmpty() );
580
// root item is initially hidden
581
if ( isRoot && !isVisible() )
585
void DiscoListItem::autoItemsChildren() const
587
if ( !autoItemsEnabled() )
590
DiscoListItem *child = (DiscoListItem *)firstChild();
592
child->updateItems(true);
594
child = (DiscoListItem *)child->nextSibling();
598
void DiscoListItem::updateInfo()
600
if ( d->protocol != DiscoData::Auto && d->protocol != DiscoData::Disco )
603
JT_DiscoInfo *jt = new JT_DiscoInfo(d->pa->client()->rootTask());
604
connect(jt, SIGNAL(finished()), SLOT(discoInfoFinished()));
605
jt->get(di.jid(), di.node());
607
d->tasks->append(jt);
610
void DiscoListItem::discoInfoFinished()
612
JT_DiscoInfo *jt = (JT_DiscoInfo *)sender();
614
if ( jt->success() ) {
615
updateInfo( jt->item() );
617
else if ( !autoInfo && d->protocol != DiscoData::Auto ) {
618
QString error = jt->statusString();
619
QMessageBox::critical(dlg(), tr("Error"), tr("There was an error getting item's info for <b>%1</b>.\nReason: %2").arg(di.jid().full()).arg(error));
626
void DiscoListItem::updateInfo(const DiscoItem &item)
630
if ( isRoot && !isVisible() )
634
//----------------------------------------------------------------------------
636
//----------------------------------------------------------------------------
638
class DiscoListView : public QListView, public QToolTip
642
DiscoListView(QWidget *parent);
645
void maybeTip(const QPoint &);
646
QDragObject *dragObject();
649
DiscoListView::DiscoListView(QWidget *parent)
650
: QListView(parent), QToolTip(viewport())
652
addColumn( tr( "Name" ) );
653
addColumn( tr( "JID" ) );
654
addColumn( tr( "Node" ) );
657
void DiscoListView::maybeTip(const QPoint &pos)
659
DiscoListItem *i = (DiscoListItem *)itemAt(pos);
663
// NAME <JID> (Node "NODE")
666
// (icon) NAME (Category "CATEGORY"; Type "TYPE")
667
// (icon) NAME (Category "CATEGORY"; Type "TYPE")
670
// NAME (http://jabber.org/feature)
671
// NAME (http://jabber.org/feature)
674
QString text = "<qt><nobr>";
675
DiscoItem item = i->item();
677
if ( !item.name().isEmpty() )
678
text += item.name() + " ";
680
text += "<" + item.jid().full() + ">";
682
if ( !item.node().isEmpty() )
683
text += " (" + tr("Node") + " \"" + item.node() + "\")";
687
if ( !item.identities().isEmpty() || !item.features().list().isEmpty() )
691
if ( !item.identities().isEmpty() ) {
692
text += "<br>\n<b>" + tr("Identities:") + "</b>\n";
694
DiscoItem::Identities::ConstIterator it = item.identities().begin();
695
for ( ; it != item.identities().end(); ++it) {
697
Icon icon( category2icon((*it).category, (*it).type) );
698
if ( !icon.name().isEmpty() )
699
text += "<icon name=\"" + icon.name() + "\"> ";
701
text += " (" + tr("Category") + " \"" + (*it).category + "\"; " + tr("Type") + " \"" + (*it).type + "\")\n";
704
if ( !item.features().list().isEmpty() )
709
if ( !item.features().list().isEmpty() ) {
710
text += "<br>\n<b>" + tr("Features:") + "</b>\n";
712
QStringList features = item.features().list();
713
QStringList::ConstIterator it = features.begin();
714
for ( ; it != features.end(); ++it) {
717
if ( f.id() > Features::FID_None )
718
text += f.name() + " (";
720
if ( f.id() > Features::FID_None )
726
QRect r( itemRect(i) );
730
QDragObject *DiscoListView::dragObject()
732
DiscoListItem *i = (DiscoListItem *)selectedItem();
736
QDragObject *d = new QTextDrag(i->item().jid().full(), this);
737
d->setPixmap(IconsetFactory::icon("status/online"), QPoint(8,8));
741
//----------------------------------------------------------------------------
743
//----------------------------------------------------------------------------
745
class DiscoDlg::Private : public QObject
750
// helper class for use in toolbar
751
class StretchWidget : public QWidget
754
StretchWidget(QWidget *parent)
757
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
761
// helper class to store browser history
766
History(QListViewItem *it) {
775
QListViewItem *takeItem() {
776
QListViewItem *i = item;
790
IconAction *actBrowse, *actBack, *actForward, *actRefresh, *actStop;
792
// custom actions, that will be added to toolbar and context menu
793
IconAction *actRegister, *actSearch, *actJoin, *actVCard, *actAdd;
795
typedef QPtrList<History> HistoryList;
796
HistoryList backHistory, forwardHistory;
801
Private(DiscoDlg *parent, PsiAccount *pa);
805
void doDisco(QString host = QString::null, QString node = QString::null);
808
void actionRefresh();
812
void actionForward();
813
void updateBackForward();
814
void backForwardHelper(QListViewItem *);
816
void updateComboBoxes(Jid j, QString node);
818
void itemUpdateStarted();
819
void itemUpdateFinished();
821
void disableButtons();
822
void enableButtons(const DiscoItem &);
824
void itemSelected (QListViewItem *);
825
void itemDoubleclicked (QListViewItem *);
826
bool eventFilter (QObject *, QEvent *);
828
void setProtocol(int);
831
void actionActivated(int);
833
void objectDestroyed(QObject *);
836
friend class DiscoListItem;
839
DiscoDlg::Private::Private(DiscoDlg *parent, PsiAccount *pa)
843
data.tasks = new TaskList;
844
connect(data.tasks, SIGNAL(started()), SLOT(itemUpdateStarted()));
845
connect(data.tasks, SIGNAL(finished()), SLOT(itemUpdateFinished()));
846
data.d = new DiscoConnector(this);
847
connect(data.d, SIGNAL(itemUpdated(QListViewItem *)), SLOT(itemSelected (QListViewItem *)));
848
data.protocol = DiscoData::Auto;
850
backHistory.setAutoDelete(true);
851
forwardHistory.setAutoDelete(true);
855
connect(busy, SIGNAL(destroyed(QObject *)), SLOT(objectDestroyed(QObject *)));
857
QListView *lv_discoOld = dlg->lv_disco;
858
dlg->lv_disco = new DiscoListView(dlg->centralWidget());
859
replaceWidget(lv_discoOld, dlg->lv_disco);
861
dlg->lv_disco->installEventFilter (this);
862
connect(dlg->lv_disco, SIGNAL(selectionChanged (QListViewItem *)), SLOT(itemSelected (QListViewItem *)));;
863
connect(dlg->lv_disco, SIGNAL(doubleClicked (QListViewItem *)), SLOT(itemDoubleclicked (QListViewItem *)));;
866
QSignalMapper *pm = new QSignalMapper(this);
867
connect(pm, SIGNAL(mapped(int)), SLOT(setProtocol(int)));
868
QActionGroup *protocolActions = new QActionGroup (this);
869
protocolActions->setExclusive(true);
871
ProtocolAction *autoProtocol = new ProtocolAction (tr("Auto"), tr("Automatically determine protocol"), protocolActions, pm, DiscoData::Auto);
872
ProtocolAction *discoProtocol = new ProtocolAction ("D", tr("Service Discovery"), protocolActions, pm, DiscoData::Disco);
873
ProtocolAction *browseProtocol = new ProtocolAction ("B", tr("Browse Services"), protocolActions, pm, DiscoData::Browse);
874
ProtocolAction *agentsProtocol = new ProtocolAction ("A", tr("Browse Agents"), protocolActions, pm, DiscoData::Agents);
875
autoProtocol->setOn(true);
878
actBrowse = new IconAction (tr("Browse"), "psi/jabber", tr("&Browse"), 0, dlg);
879
connect (actBrowse, SIGNAL(activated()), SLOT(actionBrowse()));
880
actRefresh = new IconAction (tr("Refresh Item"), "psi/reload", tr("&Refresh Item"), 0, dlg);
881
connect (actRefresh, SIGNAL(activated()), SLOT(actionRefresh()));
882
actStop = new IconAction (tr("Stop"), "psi/stop", tr("Sto&p"), 0, dlg);
883
connect (actStop, SIGNAL(activated()), SLOT(actionStop()));
884
actBack = new IconAction (tr("Back"), "psi/arrowLeft", tr("&Back"), 0, dlg);
885
connect (actBack, SIGNAL(activated()), SLOT(actionBack()));
886
actForward = new IconAction (tr("Forward"), "psi/arrowRight", tr("&Forward"), 0, dlg);
887
connect (actForward, SIGNAL(activated()), SLOT(actionForward()));
890
QSignalMapper *sm = new QSignalMapper(this);
891
connect(sm, SIGNAL(mapped(int)), SLOT(actionActivated(int)));
892
actRegister = new IconAction (tr("Register"), "psi/register", tr("&Register"), 0, dlg);
893
connect (actRegister, SIGNAL(activated()), sm, SLOT(map()));
894
sm->setMapping(actRegister, Features::FID_Register);
895
actSearch = new IconAction (tr("Search"), "psi/search", tr("&Search"), 0, dlg);
896
connect (actSearch, SIGNAL(activated()), sm, SLOT(map()));
897
sm->setMapping(actSearch, Features::FID_Search);
898
actJoin = new IconAction (tr("Join"), "psi/groupChat", tr("&Join"), 0, dlg);
899
connect (actJoin, SIGNAL(activated()), sm, SLOT(map()));
900
sm->setMapping(actJoin, Features::FID_Groupchat);
901
actVCard = new IconAction (tr("vCard"), "psi/vCard", tr("&vCard"), 0, dlg);
902
connect (actVCard, SIGNAL(activated()), sm, SLOT(map()));
903
sm->setMapping(actVCard, Features::FID_VCard);
904
actAdd = new IconAction (tr("Add to roster"), "psi/addContact", tr("&Add to roster"), 0, dlg);
905
connect (actAdd, SIGNAL(activated()), sm, SLOT(map()));
906
sm->setMapping(actAdd, Features::FID_Add);
909
toolBar = new PsiToolBar(dlg);
910
toolBar->setCustomizeable( false );
912
actBack->addTo(toolBar);
913
actBrowse->addTo(toolBar);
914
actForward->addTo(toolBar);
916
toolBar->addSeparator();
917
actRefresh->addTo(toolBar);
918
actStop->addTo(toolBar);
921
toolBar->addSeparator();
922
actRegister->addTo(toolBar);
923
actSearch->addTo(toolBar);
924
actJoin->addTo(toolBar);
926
toolBar->addSeparator();
927
actAdd->addTo(toolBar);
928
actVCard->addTo(toolBar);
931
toolBar->addSeparator();
932
autoProtocol->addTo(toolBar);
933
discoProtocol->addTo(toolBar);
934
browseProtocol->addTo(toolBar);
935
agentsProtocol->addTo(toolBar);
937
toolBar->setStretchableWidget(new StretchWidget(toolBar));
938
pa->accountLabel(toolBar, true);
942
actStop->setEnabled(false); // stop action is not handled by disableButtons()
943
updateBackForward(); // same applies to back & forward
946
DiscoDlg::Private::~Private()
951
void DiscoDlg::Private::doDisco(QString _host, QString _node)
953
PsiAccount *pa = data.pa;
954
if ( !pa->checkConnected(dlg) )
959
QString host = _host;
960
if ( host.isEmpty() )
961
host = dlg->cb_address->currentText();
962
j = host.stripWhiteSpace();
966
QString n = _node.stripWhiteSpace();
968
n = dlg->cb_node->currentText().stripWhiteSpace();
970
// check, whether we need to update history
971
if ( (jid.full() != j.full()) || (node != n) ) {
972
QListViewItem *item = dlg->lv_disco->firstChild(); // get the root item
975
dlg->lv_disco->takeItem( item );
977
backHistory.append( new History(item) );
978
forwardHistory.clear();
985
updateComboBoxes(jid, node);
987
data.tasks->clear(); // also will call all all necessary functions
991
dlg->lv_disco->clear();
993
// create new root item
998
DiscoListItem *root = new DiscoListItem (di, &data, dlg->lv_disco);
999
root->setVisible (false); // don't confuse users with empty root
1001
root->setOpen(true); // begin browsing
1004
void DiscoDlg::Private::updateComboBoxes(Jid j, QString n)
1006
data.pa->psi()->recentBrowseAdd( j.full() );
1007
dlg->cb_address->clear();
1008
dlg->cb_address->insertStringList(data.pa->psi()->recentBrowseList());
1010
data.pa->psi()->recentNodeAdd( n );
1011
dlg->cb_node->clear();
1012
dlg->cb_node->insertStringList(data.pa->psi()->recentNodeList());
1015
void DiscoDlg::Private::actionStop()
1017
data.tasks->clear();
1020
void DiscoDlg::Private::actionRefresh()
1022
DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
1030
void DiscoDlg::Private::actionBrowse()
1032
DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
1036
doDisco(it->item().jid().full(), it->item().node());
1039
void DiscoDlg::Private::actionBack()
1041
// add current selection to forward history
1042
QListViewItem *item = dlg->lv_disco->firstChild();
1044
dlg->lv_disco->takeItem( item );
1046
forwardHistory.append( new History(item) );
1049
// now, take info from back history...
1050
QListViewItem *i = backHistory.last()->takeItem();
1051
backHistory.removeLast();
1054
backForwardHelper(i);
1057
void DiscoDlg::Private::actionForward()
1059
// add current selection to back history
1060
QListViewItem *item = dlg->lv_disco->firstChild();
1062
dlg->lv_disco->takeItem( item );
1064
backHistory.append( new History(item) );
1067
// now, take info from forward history...
1068
QListViewItem *i = forwardHistory.last()->takeItem();
1069
forwardHistory.removeLast();
1072
backForwardHelper(i);
1075
void DiscoDlg::Private::backForwardHelper(QListViewItem *root)
1077
DiscoListItem *i = (DiscoListItem *)root;
1079
jid = i->item().jid();
1080
node = i->item().node();
1082
updateComboBoxes(jid, node);
1084
data.tasks->clear(); // also will call all all necessary functions
1086
updateBackForward();
1088
dlg->lv_disco->insertItem( root );
1090
// fixes multiple selection bug
1091
QListViewItemIterator it( dlg->lv_disco );
1092
while ( it.current() ) {
1093
QListViewItem *item = it.current();
1096
if ( item->isSelected() )
1097
for (int i = 0; i <= 1; i++) // it's boring to write same line twice :-)
1098
dlg->lv_disco->setSelected(item, (bool)i);
1102
void DiscoDlg::Private::updateBackForward()
1104
actBack->setEnabled ( !backHistory.isEmpty() );
1105
actForward->setEnabled ( !forwardHistory.isEmpty() );
1108
void DiscoDlg::Private::itemUpdateStarted()
1110
actStop->setEnabled(true);
1115
void DiscoDlg::Private::itemUpdateFinished()
1117
actStop->setEnabled(false);
1122
void DiscoDlg::Private::disableButtons()
1125
enableButtons ( di );
1128
void DiscoDlg::Private::enableButtons(const DiscoItem &it)
1130
bool itemSelected = !it.jid().full().isEmpty();
1131
actRefresh->setEnabled( itemSelected );
1132
actBrowse->setEnabled( itemSelected );
1135
Features f = it.features();
1136
actRegister->setEnabled( f.canRegister() );
1137
actSearch->setEnabled( f.canSearch() );
1138
actJoin->setEnabled( f.canGroupchat() );
1139
actAdd->setEnabled( itemSelected );
1140
actVCard->setEnabled( f.haveVCard() );
1143
void DiscoDlg::Private::itemSelected (QListViewItem *item)
1145
DiscoListItem *it = (DiscoListItem *)item;
1153
const DiscoItem di = it->item();
1154
enableButtons ( di );
1157
void DiscoDlg::Private::itemDoubleclicked (QListViewItem *item)
1159
DiscoListItem *it = (DiscoListItem *)item;
1163
const DiscoItem d = it->item();
1164
const Features &f = d.features();
1166
// set the prior state of item
1167
// FIXME: causes minor flickering
1168
if ( f.canGroupchat() || f.canRegister() || f.canSearch() ) {
1169
if ( !it->isOpen() ) {
1170
if ( it->isExpandable() || it->childCount() )
1171
it->setOpen( true );
1174
it->setOpen( false );
1180
// trigger default action
1181
if ( f.canGroupchat() ) {
1182
id = Features::FID_Groupchat;
1185
// FIXME: check the category and type for JUD!
1186
DiscoItem::Identity ident = d.identities().first();
1187
bool searchFirst = ident.category == "service" && ident.type == "jud";
1189
if ( searchFirst && f.canSearch() ) {
1190
id = Features::FID_Search;
1193
if ( f.canRegister() )
1194
id = Features::FID_Register;
1199
emit dlg->featureActivated( Features::feature(id), d.jid(), d.node() );
1202
bool DiscoDlg::Private::eventFilter (QObject *object, QEvent *event)
1204
if ( object == dlg->lv_disco ) {
1205
if ( event->type() == QEvent::ContextMenu ) {
1206
QContextMenuEvent *e = (QContextMenuEvent *)event;
1208
DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
1212
// prepare features list
1213
QValueList<long> idFeatures;
1214
QStringList features = it->item().features().list();
1215
{ // convert all features to their IDs
1216
QStringList::Iterator it = features.begin();
1217
for ( ; it != features.end(); ++it) {
1219
if ( f.id() > Features::FID_None )
1220
idFeatures.append( Features::id(*it) );
1222
//qHeapSort(idFeatures);
1225
QValueList<long> ids;
1226
{ // ensure, that there's in no duplicated IDs inside. FIXME: optimize this, anyone?
1227
long id = 0, count = 0;
1228
QValueList<long>::Iterator it;
1229
while ( count < (long)idFeatures.count() ) {
1232
for (it = idFeatures.begin(); it != idFeatures.end(); ++it) {
1245
// prepare popup menu
1248
actBrowse->addTo (&p);
1249
actRefresh->addTo (&p);
1250
actStop->addTo (&p);
1253
p.insertSeparator();
1254
actRegister->addTo(&p);
1255
actSearch->addTo(&p);
1258
p.insertSeparator();
1260
actVCard->addTo(&p);
1262
// popup with all available features
1263
QPopupMenu *fm = new QPopupMenu(&p);
1265
QValueList<long>::Iterator it = ids.begin();
1266
for ( ; it != ids.end(); ++it)
1267
fm->insertItem(Features::name(*it), *it + 10000); // TODO: add pixmap
1270
//p.insertSeparator();
1271
//int menuId = p.insertItem(tr("Activate &Feature"), fm);
1272
//p.setItemEnabled(menuId, !ids.isEmpty());
1276
int r = p.exec ( e->globalPos() );
1279
actionActivated(r-10000);
1288
void DiscoDlg::Private::actionActivated(int id)
1290
DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
1294
emit dlg->featureActivated(Features::feature(id), it->item().jid(), it->item().node());
1297
void DiscoDlg::Private::objectDestroyed(QObject *obj)
1303
void DiscoDlg::Private::setProtocol(int p)
1305
data.protocol = (DiscoData::Protocol)p;
1308
//----------------------------------------------------------------------------
1310
//----------------------------------------------------------------------------
1312
DiscoDlg::DiscoDlg(PsiAccount *pa, const Jid &jid, const QString &node)
1313
: DiscoUI (0, 0, WDestructiveClose)
1316
ck_autoItems->setChecked(option.discoItems);
1317
ck_autoInfo->setChecked(option.discoInfo);
1320
d = new Private(this, pa);
1324
setCaption(CAP(caption()));
1325
setIcon(is->transportStatus("transport", STATUS_ONLINE));
1326
X11WM_CLASS("disco");
1328
statusBar()->hide();
1330
pb_browse->setDefault(true);
1331
connect (pb_browse, SIGNAL(clicked()), d, SLOT(doDisco()));
1334
cb_address->setInsertionPolicy(QComboBox::NoInsertion);
1335
cb_address->insertStringList(pa->psi()->recentBrowseList()); // FIXME
1336
cb_address->setFocus();
1337
connect(cb_address, SIGNAL(activated(const QString &)), d, SLOT(doDisco()));
1338
cb_address->setCurrentText(d->jid.full());
1340
cb_node->setInsertionPolicy(QComboBox::NoInsertion);
1341
cb_node->insertStringList(pa->psi()->recentNodeList());
1342
connect(cb_node, SIGNAL(activated(const QString &)), d, SLOT(doDisco()));
1343
cb_node->setCurrentText(node);
1345
if ( pa->loggedIn() )
1349
DiscoDlg::~DiscoDlg()
1354
option.discoItems = ck_autoItems->isChecked();
1355
option.discoInfo = ck_autoInfo->isChecked();
1358
void DiscoDlg::doDisco(QString host, QString node)
1360
d->doDisco(host, node);
1363
#include "discodlg.moc"