2
* browserdlg.cpp - main dialog for iq:browse 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
21
#include "browserdlg.h"
23
#include <qlistview.h>
24
#include <qpushbutton.h>
25
#include <qcombobox.h>
26
#include <qcheckbox.h>
27
#include <qstringlist.h>
28
#include <qguardedptr.h>
30
#include <qlistview.h>
31
#include <qgroupbox.h>
32
#include <qpopupmenu.h>
34
#include <qobjectlist.h>
37
#include <qstatusbar.h>
39
#include "ui_search.h"
41
#include "xmpp_tasks.h"
44
#include "psiaccount.h"
46
#include "busywidget.h"
48
#include "iconaction.h"
50
//----------------------------------------------------------------------------
51
// BrowserData -- a shared data struct
52
//----------------------------------------------------------------------------
59
//----------------------------------------------------------------------------
61
//----------------------------------------------------------------------------
63
class BrowserItem : public QObject, public QListViewItem
67
BrowserItem (const AgentItem &item, BrowserData *p, QListView *parent);
68
BrowserItem (const AgentItem &item, BrowserData *p, QListViewItem *parent);
70
QString text (int c) const;
71
const AgentItem & item() const;
72
void setOpen (bool o);
75
void update(bool preBrowse = true, bool parentPreBrowsing = false);
78
void updateFinished();
85
void init (const AgentItem &item, BrowserData *p);
89
bool preBrowsingEnabled();
92
class BrowserItem::Private : public QObject
98
Private (BrowserItem *parent)
102
parentPreBrowsing = false;
103
alreadyBrowsed = false;
106
void copyItem (const AgentItem &a)
108
if ( !a.name().isEmpty() )
109
item.setName( a.name() );
110
if ( !a.jid().full().isEmpty() )
111
item.setJid( a.jid() );
113
if ( !a.category().isEmpty() )
114
item.setCategory( a.category() );
115
if ( !a.type().isEmpty() ) {
116
// FIXME: workaround for buggy AIM transport (it is also used for an ICQ transport)
117
if ( !((item.type() == "aim" || item.type() == "icq") && a.type() == "jabber") ) {
118
item.setType( a.type() );
122
if ( !a.category().isEmpty() )
123
bItem->setPixmap (0, category2icon(item.category(), item.type()));
125
bItem->setPixmap (0, is->status(item.jid(), STATUS_ONLINE));
127
if ( !a.features().list().isEmpty() )
128
item.setFeatures(a.features());
131
void preBrowseChilds()
133
if ( !bItem->preBrowsingEnabled() )
136
// pre-browse all child items
137
BrowserItem *child = (BrowserItem *)bItem->firstChild();
139
child->update(false, true); // checkout update(bool, bool) function, if you change this
141
child = (BrowserItem *)child->nextSibling();
149
bool preBrowsing, parentPreBrowsing;
153
BrowserItem::BrowserItem (const AgentItem &item, BrowserData *p, QListView *parent)
154
: QListViewItem (parent)
160
BrowserItem::BrowserItem (const AgentItem &item, BrowserData *p, QListViewItem *parent)
161
: QListViewItem (parent)
167
void BrowserItem::init (const AgentItem &item, BrowserData *p)
169
d = new Private (this);
170
d->item = (AgentItem)item;
173
// find 'root' BrowserItem
175
while ( d->root->QListViewItem::parent() )
176
d->root = (BrowserItem *)d->root->QListViewItem::parent();
178
d->copyItem(item); // hmm... guess this is too much, concerning that we do 'd->item = item' already
180
if ( !preBrowsingEnabled() )
181
setExpandable (true);
184
QString BrowserItem::text (int c) const
187
return d->item.name();
189
return d->item.jid().full();
193
const AgentItem & BrowserItem::item() const
198
BrowserDlg *BrowserItem::dlg()
200
return (BrowserDlg *)listView()->parent()->parent();
203
bool BrowserItem::preBrowsingEnabled()
205
return dlg()->cb_preBrowse->isChecked();
208
void BrowserItem::setOpen (bool o)
214
d->preBrowseChilds();
217
QListViewItem::setOpen (o);
220
void BrowserItem::update(bool preBrowse, bool parentPreBrowsing)
222
if ( preBrowse == false && parentPreBrowsing == true ) {
223
// to save some traffic
224
if ( d->alreadyBrowsed )
227
// FIXME: currently, JUD doesn't seem to answer to browsing requests
228
if ( d->item.category() == "service" && d->item.type() == "jud" )
232
if ( d->root == this )
235
if ( !preBrowsingEnabled() )
238
d->preBrowsing = preBrowse;
239
d->parentPreBrowsing = parentPreBrowsing;
241
JT_Browse *jt = new JT_Browse(d->p->pa->client()->rootTask());
242
connect(jt, SIGNAL(finished()), SLOT(updateFinished()));
243
jt->get(d->item.jid());
245
d->p->tasks->append(jt);
248
void BrowserItem::updateFinished()
250
QObject *senderObj = (QObject *)sender();
251
JT_GetServices *jtAgents = 0;
252
JT_Browse *jtBrowse = (senderObj->inherits("XMPP::JT_Browse") ? (JT_Browse *)senderObj : 0);
254
jtAgents = (senderObj->inherits("XMPP::JT_GetServices") ? (JT_GetServices *)senderObj : 0);
256
if( (jtBrowse && jtBrowse->success()) || (jtAgents && jtAgents->success()) ) {
258
d->copyItem ( jtBrowse->root() );
260
// iq:agents doesn't have as much useful info as iq:browse has,
261
// so we're gonna to provide it by ourselves :-)
264
ai.setName ( "Jabber Service" );
265
ai.setCategory ( "services" );
266
ai.setType ( "jabber" );
271
if ( !d->parentPreBrowsing )
272
QListViewItem::setOpen (true);
276
agents = jtBrowse->agents();
278
agents = jtAgents->agents();
281
for(AgentList::ConstIterator it = agents.begin(); it != agents.end(); ++it) {
282
const AgentItem a = *it;
285
BrowserItem *child = (BrowserItem *)firstChild();
287
if ( child->item().jid().full() == a.jid().full() ) {
288
child->d->copyItem ( a );
294
child = (BrowserItem *)child->nextSibling();
298
new BrowserItem (a, d->p, this);
301
// check for removed items
302
QPtrList<BrowserItem> removeList;
303
removeList.setAutoDelete (true);
304
BrowserItem *child = (BrowserItem *)firstChild();
308
for(AgentList::ConstIterator it = agents.begin(); it != agents.end(); ++it) {
309
const AgentItem a = *it;
311
if ( child->item().jid().full() == a.jid().full() ) {
318
removeList.append ( child ); // items will be deleted later
320
child = (BrowserItem *)child->nextSibling();
323
if ( jtBrowse && !d->parentPreBrowsing ) // comment this to auto-browse entire server tree
324
d->preBrowseChilds();
326
// don't forget to remove '+' (or '-') sign in case, that the child list is empty
327
setExpandable ( !agents.isEmpty() );
329
// root item is initially hidden
334
setExpandable ( false );
336
if ( jtBrowse && d->isRoot ) {
337
// there are chances, that server just doesn't support browsing (this is
338
// true for the Jabber Inc. commercial server). Go to jabber.com for
341
JT_GetServices *jt = new JT_GetServices(d->p->pa->client()->rootTask());
342
connect(jt, SIGNAL(finished()), SLOT(updateFinished()));
343
jt->get(d->item.jid());
345
d->p->tasks->append(jt);
348
if ( !d->parentPreBrowsing ) {
351
error = jtBrowse->statusString();
353
error = jtAgents->statusString();
354
QMessageBox::critical(dlg(), tr("Error"), tr("There was an error browsing the <b>%1</b>.\nReason: %2").arg(item().jid().full()).arg(error));
359
dlg()->itemSelected (dlg()->lv_browse->selectedItem());
361
d->alreadyBrowsed = true;
362
d->preBrowsing = false;
363
d->parentPreBrowsing = false;
367
//----------------------------------------------------------------------------
369
//----------------------------------------------------------------------------
371
class BrowserDlg::Private : public QObject
376
Private (BrowserDlg *parent, PsiAccount *pa)
383
connect(busy, SIGNAL(destroyed(QObject *)), SLOT(objectDestroyed(QObject *)));
385
data.tasks = new TaskList;
386
connect(data.tasks, SIGNAL(started()), SLOT(itemUpdateStarted()));
387
connect(data.tasks, SIGNAL(finished()), SLOT(itemUpdateFinished()));
390
actBrowse = new IconAction (tr("Browse"), "psi/jabber", tr("&Browse"), 0, dlg);
391
connect (actBrowse, SIGNAL(activated()), SLOT(actionBrowse()));
392
actRefresh = new IconAction (tr("Refresh"), "psi/reload", tr("&Refresh"), 0, dlg);
393
connect (actRefresh, SIGNAL(activated()), SLOT(actionRefresh()));
394
actStop = new IconAction (tr("Stop"), "psi/stop", tr("Sto&p"), 0, dlg);
395
connect (actStop, SIGNAL(activated()), SLOT(actionStop()));
397
actRegister = new IconAction (tr("Register"), "psi/register", tr("&Register"), 0, dlg);
398
connect (actRegister, SIGNAL(activated()), SLOT(actionRegister()));
399
actSearch = new IconAction (tr("Search"), "psi/search", tr("&Search"), 0, dlg);
400
connect (actSearch, SIGNAL(activated()), SLOT(actionSearch()));
401
actJoin = new IconAction (tr("Join"), "psi/groupChat", tr("&Join"), 0, dlg);
402
connect (actJoin, SIGNAL(activated()), SLOT(actionJoin()));
404
actInfo = new IconAction (tr("Info"), "psi/vCard", tr("&Info"), 0, dlg);
405
connect (actInfo, SIGNAL(activated()), SLOT(actionInfo()));
407
/*actTime = new QAction (tr("Get Time"), QIconSet(is.getTime), tr("Get &Time"), 0, dlg);
408
connect (actTime, SIGNAL(activated()), SLOT(actionGetTime()));
409
actLast = new QAction (tr("Last Time"), QIconSet(is.getLastTime), tr("&Last Time"), 0, dlg);
410
connect (actLast, SIGNAL(activated()), SLOT(actionGetLast()));
411
actVersion = new QAction (tr("Get Version"), QIconSet(is.getVersion), tr("Get &Version"), 0, dlg);
412
connect (actVersion, SIGNAL(activated()), SLOT(actionVersion()));
413
actMessage = new QAction (tr("Send Message"), QIconSet(is.message.base()), tr("Send &Message"), 0, dlg);
414
connect (actMessage, SIGNAL(activated()), SLOT(actionMessage()));
415
actChat = new QAction (tr("Open Chat"), QIconSet(is.chat.base()), tr("Open &Chat"), 0, dlg);
416
connect (actChat, SIGNAL(activated()), SLOT(actionChat()));
417
actAdd = new QAction (tr("Add to roster"), QIconSet(is.add), tr("Add to &roster"), 0, dlg);
418
connect (actAdd, SIGNAL(activated()), SLOT(actionAdd()));*/
420
actBack = new IconAction (tr("Back"), "psi/arrowLeft", tr("&Back"), 0, dlg);
421
connect (actBack, SIGNAL(activated()), SLOT(actionBack()));
422
actForward = new IconAction (tr("Forward"), "psi/arrowRight", tr("&Forward"), 0, dlg);
423
connect (actForward, SIGNAL(activated()), SLOT(actionForward()));
425
toolBar = new QToolBar(dlg);
427
actBack->addTo(toolBar);
428
actBrowse->addTo(toolBar);
429
actForward->addTo(toolBar);
431
toolBar->addSeparator();
432
actRefresh->addTo(toolBar);
433
actStop->addTo(toolBar);
435
toolBar->addSeparator();
436
actRegister->addTo(toolBar);
437
actSearch->addTo(toolBar);
438
actJoin->addTo(toolBar);
440
toolBar->addSeparator();
441
actInfo->addTo(toolBar);
442
/*actVersion->addTo(toolBar);
443
actTime->addTo(toolBar);
444
actLast->addTo(toolBar);
446
toolBar->addSeparator();
447
actMessage->addTo(toolBar);
448
actChat->addTo(toolBar);
450
toolBar->addSeparator();
451
actAdd->addTo(toolBar);*/
453
toolBar->setStretchableWidget(new StretchWidget(toolBar));
454
pa->accountLabel(toolBar, true);
456
backHistory.setAutoDelete(true);
457
forwardHistory.setAutoDelete(true);
460
actStop->setEnabled(false); // stop action is not handled by disableButtons()
461
updateBackForward(); // same applies to back & forward
470
void objectDestroyed(QObject *obj)
478
BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
482
doBrowse( it->item().jid().full() );
487
BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
496
actStop->setEnabled(false);
502
void actionRegister()
504
BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
508
emit dlg->signalRegister( it->item().jid() );
513
BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
517
emit dlg->signalSearch( it->item().jid() );
522
BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
526
emit dlg->signalJoin( it->item().jid() );
531
BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
535
data.pa->actionInfo( it->item().jid() );
538
/*void actionGetTime()
540
BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
549
BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
559
// add current selection to forward history
560
QListViewItem *item = dlg->lv_browse->firstChild();
562
dlg->lv_browse->takeItem( item );
564
forwardHistory.append( new History(jid, item) );
566
// now, take info from back history...
567
History *h = backHistory.last();
569
QListViewItem *i = h->takeItem();
570
backHistory.removeLast();
573
doBrowse(j.full(), true, i);
578
// add current selection to back history
579
QListViewItem *item = dlg->lv_browse->firstChild();
581
dlg->lv_browse->takeItem( item );
583
backHistory.append( new History(jid, item) );
585
// now, take info from forward history...
586
History *h = forwardHistory.last();
588
QListViewItem *i = h->takeItem();
589
forwardHistory.removeLast();
592
doBrowse(j.full(), true, i);
596
void itemUpdateStarted()
598
actStop->setEnabled(true);
603
void itemUpdateFinished()
605
actStop->setEnabled(false);
610
void doBrowse(const QString &host = QString::null, bool backForwardClicked = false, QListViewItem *rootItem = 0)
612
PsiAccount *pa = data.pa;
613
if(!pa->checkConnected(dlg))
620
h = dlg->cb_browse->currentText();
622
j = h.stripWhiteSpace();
627
if ( !backForwardClicked && jid.full() != j.full() ) {
628
QListViewItem *item = dlg->lv_browse->firstChild();
630
dlg->lv_browse->takeItem( item );
632
backHistory.append( new History(jid, item) );
633
forwardHistory.clear();
639
pa->psi()->recentBrowseAdd( jid.full() );
640
dlg->cb_browse->clear();
641
dlg->cb_browse->insertStringList(pa->psi()->recentBrowseList());
643
data.tasks->clear(); // also will call all all necessary functions
647
dlg->lv_browse->clear();
651
BrowserItem *root = new BrowserItem (ai, &data, dlg->lv_browse);
652
root->setVisible (false); // don't confuse users
654
root->setOpen(true); // begin browsing
657
dlg->lv_browse->insertItem( rootItem );
659
// fixes multiple selection bug
660
QListViewItemIterator it( dlg->lv_browse );
661
while ( it.current() ) {
662
QListViewItem *item = it.current();
665
if ( item->isSelected() )
666
for (int i = 0; i <= 1; i++) // its boring to write same line twice :)
667
dlg->lv_browse->setSelected(item, (bool)i);
679
QAction *actBrowse, *actRegister, *actSearch,
680
*actJoin, *actInfo, /* *actTime, *actVersion,
681
*actLast,*/ *actRefresh, *actStop,
682
*actMessage, *actChat, *actAdd;
684
QAction *actBack, *actForward;
686
// helper class for use in toolbar
687
class StretchWidget : public QWidget
690
StretchWidget(QWidget *parent)
693
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
697
// helper class to store browser history
703
History(Jid jj, QListViewItem *it) {
713
QListViewItem *takeItem() {
714
QListViewItem *i = item;
724
typedef QPtrList<History> HistoryList;
725
HistoryList backHistory, forwardHistory;
727
void disableButtons()
729
actRefresh->setEnabled(false);
730
//actStop->setEnabled(false);
732
actRegister->setEnabled(false);
733
actSearch->setEnabled(false);
734
actJoin->setEnabled(false);
736
actBrowse->setEnabled(false);
737
actInfo->setEnabled(false);
739
/*actTime->setEnabled(false);
740
actLast->setEnabled(false);
741
actVersion->setEnabled(false);
742
actMessage->setEnabled(false);
743
actChat->setEnabled(false);
744
actAdd->setEnabled(false);*/
747
void enableButtons(const AgentItem &agent)
749
const Features &f = agent.features();
751
actBrowse->setEnabled(true);
752
actRefresh->setEnabled(true);
753
//actStop->setEnabled(false);
754
actRegister->setEnabled( f.canRegister() );
755
actSearch->setEnabled( f.canSearch() );
756
actJoin->setEnabled( f.canGroupchat() );
757
actInfo->setEnabled(true);
758
/*actTime->setEnabled(false);
759
actLast->setEnabled(false);*/
762
bool eventFilter (QObject *object, QEvent *event)
764
if ( object == dlg->lv_browse ) {
765
if ( event->type() == QEvent::ContextMenu ) {
766
QContextMenuEvent *e = (QContextMenuEvent *)event;
768
BrowserItem *item = (BrowserItem *)dlg->lv_browse->selectedItem();
771
enableButtons ( item->item() );
775
actBrowse->addTo (&p);
776
actRefresh->addTo (&p);
780
actRegister->addTo (&p);
781
actSearch->addTo (&p);
786
/*actVersion->addTo (&p);
791
actMessage->addTo (&p);
795
actAdd->addTo (&p);*/
798
p.exec ( e->globalPos() );
807
void updateBackForward()
809
actBack->setEnabled( !backHistory.isEmpty() );
810
actForward->setEnabled( !forwardHistory.isEmpty() );
814
BrowserDlg::BrowserDlg (const Jid &j, PsiAccount *pa)
815
: BrowserUI (0, 0, WDestructiveClose)
817
d = new Private (this, pa);
818
pa->dialogRegister(this);
821
setCaption(CAP(caption()));
822
setIcon(is->transportStatus("transport", STATUS_ONLINE));
824
cb_browse->setInsertionPolicy(QComboBox::NoInsertion);
825
cb_browse->insertStringList(pa->psi()->recentBrowseList());
826
cb_browse->setFocus();
827
connect(cb_browse, SIGNAL(activated(const QString &)), d, SLOT(doBrowse()));
829
pb_browse->setDefault(true);
830
cb_browse->setCurrentText(d->jid.full());
832
connect (pb_browse, SIGNAL(clicked()), d, SLOT(doBrowse()));
834
lv_browse->installEventFilter (d);
838
X11WM_CLASS("browser");
844
BrowserDlg::~BrowserDlg()
846
d->data.pa->dialogUnregister(this);
849
void BrowserDlg::doBrowse(const QString &host)
851
d->doBrowse(host, true);
854
void BrowserDlg::itemSelected (QListViewItem *item)
856
BrowserItem *it = (BrowserItem *)item;
862
const AgentItem agent = it->item();
863
d->enableButtons ( agent );
866
void BrowserDlg::itemDoubleclicked (QListViewItem *item)
868
BrowserItem *it = (BrowserItem *)item;
872
const AgentItem a = it->item();
873
const Features &f = a.features();
875
// set the prior state of item
876
// FIXME: causes minor flickering
877
if ( f.canGroupchat() || f.canRegister() || f.canSearch() ) {
878
if ( !it->isOpen() ) {
879
if ( it->isExpandable() || it->childCount() )
883
it->setOpen( false );
887
// trigger default action
888
if ( f.canGroupchat() ) {
892
bool searchFirst = a.category() == "service" && a.type() == "jud";
894
if ( !searchFirst ) {
895
if ( f.canRegister() )
897
else if ( f.canSearch() )
903
else if ( f.canRegister() )
909
#include "browserdlg.moc"