~ubuntu-branches/ubuntu/quantal/psi/quantal

« back to all changes in this revision

Viewing changes to src/discodlg.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2005-01-10 17:41:43 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050110174143-ltocv5zapl6blf5d
Tags: 0.9.3-1
* New upstream release
* Cleaned up debian/rules (some things are done by upstream Makefiles now)
* Fixed some lintian warnings:
  - removed executable bit from some .png files
  - moved psi.desktop to /usr/share/applications
* Updated menu files

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * discodlg.cpp - main dialog for the Service Discovery protocol
 
3
 * Copyright (C) 2003  Michail Pishchagin
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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
 
18
 *
 
19
 */
 
20
 
 
21
#include "discodlg.h"
 
22
 
 
23
#include <qlistview.h>
 
24
#include <qcombobox.h>
 
25
#include <qcheckbox.h>
 
26
#include <qmessagebox.h>
 
27
#include <qaction.h>
 
28
#include <qstatusbar.h>
 
29
#include <qpopupmenu.h>
 
30
#include <qsignalmapper.h>
 
31
#include <qpushbutton.h>
 
32
#include <qdragobject.h>
 
33
#include <qtooltip.h>
 
34
#include <qtoolbutton.h>
 
35
 
 
36
#include "im.h"
 
37
#include "xmpp_tasks.h"
 
38
 
 
39
#include "psitoolbar.h"
 
40
#include "tasklist.h"
 
41
#include "psiaccount.h"
 
42
#include "psicon.h"
 
43
#include "busywidget.h"
 
44
#include "common.h"
 
45
#include "iconaction.h"
 
46
 
 
47
//----------------------------------------------------------------------------
 
48
// DiscoData -- a shared data struct
 
49
//----------------------------------------------------------------------------
 
50
 
 
51
class DiscoListItem;
 
52
class DiscoConnector : public QObject
 
53
{
 
54
        Q_OBJECT
 
55
public:
 
56
        DiscoConnector(QObject *parent)
 
57
        : QObject(parent) {}
 
58
 
 
59
signals:
 
60
        void itemUpdated(QListViewItem *);
 
61
 
 
62
private:
 
63
        friend class DiscoListItem;
 
64
};
 
65
 
 
66
struct DiscoData {
 
67
        PsiAccount *pa;
 
68
        TaskList *tasks;
 
69
        DiscoConnector *d;
 
70
 
 
71
        enum Protocol {
 
72
                Auto,
 
73
 
 
74
                Disco,
 
75
                Browse,
 
76
                Agents
 
77
        };
 
78
        Protocol protocol;
 
79
};
 
80
 
 
81
//----------------------------------------------------------------------------
 
82
// ProtocolAction
 
83
//----------------------------------------------------------------------------
 
84
 
 
85
class ProtocolAction : public QAction
 
86
{
 
87
        Q_OBJECT
 
88
public:
 
89
        ProtocolAction(QString text, QString toolTip, QObject *parent, QSignalMapper *sm, int parm);
 
90
 
 
91
        bool addTo(QWidget *w);
 
92
 
 
93
public slots:
 
94
        void setOn(bool);
 
95
private:
 
96
        QToolButton *btn;
 
97
};
 
98
 
 
99
ProtocolAction::ProtocolAction(QString text, QString toolTip, QObject *parent, QSignalMapper *sm, int parm)
 
100
: QAction(text, text, 0, parent)
 
101
{
 
102
        setText( text );
 
103
        setToggleAction(true);
 
104
        setToolTip(toolTip);
 
105
        connect(this, SIGNAL(activated()), sm, SLOT(map()));
 
106
        sm->setMapping(this, parm);
 
107
        btn = 0;
 
108
}
 
109
 
 
110
bool ProtocolAction::addTo(QWidget *w)
 
111
{
 
112
        if ( w->inherits("QToolBar") ) {
 
113
                if ( btn )
 
114
                        delete btn;
 
115
                QCString bname = name() + QCString("_action_button");
 
116
                btn = new QToolButton ( w, bname );
 
117
 
 
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());
 
124
 
 
125
                connect(btn, SIGNAL(toggled(bool)), this, SLOT(setOn(bool)));
 
126
 
 
127
                return true;
 
128
        }
 
129
 
 
130
        return false;
 
131
}
 
132
 
 
133
void ProtocolAction::setOn(bool b)
 
134
{
 
135
        if ( btn ) {
 
136
                if ( b )
 
137
                        emit activated();
 
138
                btn->setOn(b);
 
139
        }
 
140
 
 
141
        QAction::setOn(b);
 
142
}
 
143
 
 
144
//----------------------------------------------------------------------------
 
145
// DiscoListItem
 
146
//----------------------------------------------------------------------------
 
147
 
 
148
class DiscoListItem : public QObject, public QListViewItem
 
149
{
 
150
        Q_OBJECT
 
151
public:
 
152
        DiscoListItem(DiscoItem it, DiscoData *d, QListView *parent);
 
153
        DiscoListItem(DiscoItem it, DiscoData *d, QListViewItem *parent);
 
154
        ~DiscoListItem();
 
155
 
 
156
        QString text(int columns) const;
 
157
        void setOpen(bool open);
 
158
        const DiscoItem &item() const;
 
159
 
 
160
        void itemSelected();
 
161
 
 
162
public slots: // the two are used internally by class, and also called by DiscoDlg::Private::refresh()
 
163
        void updateInfo();
 
164
        void updateItems(bool parentAutoItems = false);
 
165
 
 
166
private slots:
 
167
        void discoItemsFinished();
 
168
        void discoInfoFinished();
 
169
 
 
170
        void doBrowse(bool parentAutoItems = false);
 
171
        void doAgents(bool parentAutoItems = false);
 
172
        void browseFinished();
 
173
        void agentsFinished();
 
174
 
 
175
private:
 
176
        DiscoItem di;
 
177
        DiscoData *d;
 
178
        bool isRoot;
 
179
        bool alreadyItems, alreadyInfo;
 
180
        bool autoItems; // used in updateItemsFinished
 
181
        bool autoInfo;
 
182
 
 
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 );
 
189
 
 
190
        // helper functions
 
191
        void init(DiscoItem it, DiscoData *dd);
 
192
 
 
193
        bool autoItemsEnabled() const;
 
194
        bool autoInfoEnabled() const;
 
195
        DiscoDlg *dlg() const;
 
196
};
 
197
 
 
198
DiscoListItem::DiscoListItem(DiscoItem it, DiscoData *_d, QListView *parent)
 
199
: QListViewItem (parent)
 
200
{
 
201
        isRoot = true;
 
202
 
 
203
        init(it, _d);
 
204
}
 
205
 
 
206
DiscoListItem::DiscoListItem(DiscoItem it, DiscoData *_d, QListViewItem *parent)
 
207
: QListViewItem (parent)
 
208
{
 
209
        isRoot = false;
 
210
 
 
211
        init(it, _d);
 
212
}
 
213
 
 
214
DiscoListItem::~DiscoListItem()
 
215
{
 
216
}
 
217
 
 
218
void DiscoListItem::init(DiscoItem _item, DiscoData *_d)
 
219
{
 
220
        d = _d;
 
221
        di = _item;
 
222
        copyItem(_item);
 
223
 
 
224
        alreadyItems = alreadyInfo = false;
 
225
 
 
226
        if ( !autoItemsEnabled() )
 
227
                setExpandable (true);
 
228
 
 
229
        autoInfo = false;
 
230
        if ( autoInfoEnabled() || isRoot ) {
 
231
                updateInfo();
 
232
                if ( !isRoot )
 
233
                        autoInfo = true;
 
234
        }
 
235
 
 
236
        //setDragEnabled(true); // EXPERIMENTAL
 
237
}
 
238
 
 
239
void DiscoListItem::copyItem(const DiscoItem &it)
 
240
{
 
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() );
 
247
 
 
248
        di.setAction ( it.action() );
 
249
 
 
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() );
 
254
 
 
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 );
 
261
                }
 
262
 
 
263
                bool found = false;
 
264
                DiscoItem::Identities::ConstIterator it = di.identities().begin();
 
265
                for ( ; it != di.identities().end(); ++it) {
 
266
                        if ( (*it).category == "service" && (*it).type == "jud" ) {
 
267
                                found = true;
 
268
                                break;
 
269
                        }
 
270
                }
 
271
                if ( !found ) {
 
272
                        DiscoItem::Identity id;
 
273
                        id.category = "service";
 
274
                        id.type     = "jud";
 
275
                        DiscoItem::Identities ids;
 
276
                        ids << id;
 
277
                        di.setIdentities( ids );
 
278
                }
 
279
        }
 
280
 
 
281
        bool pixmapOk = false;
 
282
        if ( !di.identities().isEmpty() ) {
 
283
                DiscoItem::Identity id = di.identities().first();
 
284
 
 
285
                if ( !id.category.isEmpty() ) {
 
286
                        Icon ic = category2icon(id.category, id.type);
 
287
 
 
288
                        if ( !ic.impix().isNull() ) {
 
289
                                setPixmap (0, ic.impix().pixmap());
 
290
                                pixmapOk = true;
 
291
                        }
 
292
                }
 
293
        }
 
294
 
 
295
        if ( !pixmapOk )
 
296
                setPixmap (0, is->status(di.jid(), STATUS_ONLINE));
 
297
 
 
298
        repaint();
 
299
 
 
300
        if ( isSelected() ) // update actions
 
301
                emit d->d->itemUpdated( this );
 
302
}
 
303
 
 
304
QString DiscoListItem::text (int c) const
 
305
{
 
306
        if (c == 0)
 
307
                return di.name();
 
308
        else if (c == 1)
 
309
                return di.jid().full();
 
310
        else if (c == 2)
 
311
                return di.node();
 
312
        return "";
 
313
}
 
314
 
 
315
const DiscoItem &DiscoListItem::item() const
 
316
{
 
317
        return di;
 
318
}
 
319
 
 
320
DiscoDlg *DiscoListItem::dlg() const
 
321
{
 
322
        return (DiscoDlg *)listView()->parent()->parent();
 
323
}
 
324
 
 
325
bool DiscoListItem::autoItemsEnabled() const
 
326
{
 
327
        return dlg()->ck_autoItems->isChecked();
 
328
}
 
329
 
 
330
bool DiscoListItem::autoInfoEnabled() const
 
331
{
 
332
        return dlg()->ck_autoInfo->isChecked();
 
333
}
 
334
 
 
335
void DiscoListItem::setOpen (bool o)
 
336
{
 
337
        if ( o ) {
 
338
                if ( !alreadyItems )
 
339
                        updateItems();
 
340
                else
 
341
                        autoItemsChildren();
 
342
        }
 
343
 
 
344
        QListViewItem::setOpen(o);
 
345
}
 
346
 
 
347
void DiscoListItem::itemSelected()
 
348
{
 
349
        if ( !alreadyInfo )
 
350
                updateInfo();
 
351
}
 
352
 
 
353
void DiscoListItem::updateItems(bool parentAutoItems)
 
354
{
 
355
        if ( parentAutoItems ) {
 
356
                // save traffic
 
357
                if ( alreadyItems )
 
358
                        return;
 
359
 
 
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" )
 
364
                                return;
 
365
                }
 
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.") )
 
368
                        return;
 
369
        }
 
370
 
 
371
        autoItems = !parentAutoItems;
 
372
 
 
373
        if ( !autoItemsEnabled() )
 
374
                autoItems = false;
 
375
 
 
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());
 
380
                jt->go(true);
 
381
                d->tasks->append(jt);
 
382
        }
 
383
        else if ( d->protocol == DiscoData::Browse )
 
384
                doBrowse(parentAutoItems);
 
385
        else if ( d->protocol == DiscoData::Agents )
 
386
                doAgents(parentAutoItems);
 
387
}
 
388
 
 
389
void DiscoListItem::discoItemsFinished()
 
390
{
 
391
        JT_DiscoItems *jt = (JT_DiscoItems *)sender();
 
392
 
 
393
        if ( jt->success() ) {
 
394
                updateItemsFinished(jt->items());
 
395
        }
 
396
        else if ( d->protocol == DiscoData::Auto ) {
 
397
                doBrowse();
 
398
                return;
 
399
        }
 
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));
 
403
        }
 
404
 
 
405
        alreadyItems = true;
 
406
}
 
407
 
 
408
void DiscoListItem::doBrowse(bool parentAutoItems)
 
409
{
 
410
        if ( parentAutoItems ) {
 
411
                // save traffic
 
412
                if ( alreadyItems )
 
413
                        return;
 
414
 
 
415
                if ( item().identities().size() ) {
 
416
                        DiscoItem::Identity id = item().identities().first();
 
417
                        if ( id.category == "service" && id.type == "jud" )
 
418
                                return;
 
419
                }
 
420
        }
 
421
 
 
422
        autoItems = !parentAutoItems;
 
423
        if ( !autoItemsEnabled() )
 
424
                autoItems = false;
 
425
 
 
426
        JT_Browse *jt = new JT_Browse(d->pa->client()->rootTask());
 
427
        connect(jt, SIGNAL(finished()), SLOT(browseFinished()));
 
428
        jt->get(di.jid());
 
429
        jt->go(true);
 
430
        d->tasks->append(jt);
 
431
}
 
432
 
 
433
void DiscoListItem::browseFinished()
 
434
{
 
435
        JT_Browse *jt = (JT_Browse *)sender();
 
436
 
 
437
        if ( jt->success() ) {
 
438
                // update info
 
439
                DiscoItem root;
 
440
                root.fromAgentItem( jt->root() );
 
441
                updateInfo(root);
 
442
                alreadyInfo = true;
 
443
                autoInfo = false;
 
444
 
 
445
                // update items
 
446
                AgentList from = jt->agents();
 
447
                DiscoList to;
 
448
                AgentList::Iterator it = from.begin();
 
449
                for ( ; it != from.end(); ++it) {
 
450
                        DiscoItem item;
 
451
                        item.fromAgentItem( *it );
 
452
 
 
453
                        to.append( item );
 
454
                }
 
455
 
 
456
                updateItemsFinished(to);
 
457
        }
 
458
        else if ( d->protocol == DiscoData::Auto ) {
 
459
                doAgents();
 
460
                return;
 
461
        }
 
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));
 
465
        }
 
466
 
 
467
        alreadyItems = true;
 
468
}
 
469
 
 
470
void DiscoListItem::doAgents(bool parentAutoItems)
 
471
{
 
472
        if ( parentAutoItems ) {
 
473
                // save traffic
 
474
                if ( alreadyItems )
 
475
                        return;
 
476
 
 
477
                if ( item().identities().size() ) {
 
478
                        DiscoItem::Identity id = item().identities().first();
 
479
                        if ( id.category == "service" && id.type == "jud" )
 
480
                                return;
 
481
                }
 
482
        }
 
483
 
 
484
        autoItems = !parentAutoItems;
 
485
        if ( !autoItemsEnabled() )
 
486
                autoItems = false;
 
487
 
 
488
        JT_GetServices *jt = new JT_GetServices(d->pa->client()->rootTask());
 
489
        connect(jt, SIGNAL(finished()), SLOT(agentsFinished()));
 
490
        jt->get(di.jid());
 
491
        jt->go(true);
 
492
        d->tasks->append(jt);
 
493
}
 
494
 
 
495
void DiscoListItem::agentsFinished()
 
496
{
 
497
        JT_GetServices *jt = (JT_GetServices *)sender();
 
498
 
 
499
        if ( jt->success() ) {
 
500
                // update info
 
501
                DiscoItem root;
 
502
                DiscoItem::Identity id;
 
503
                id.name     = tr("Jabber Service");
 
504
                id.category = "service";
 
505
                id.type     = "jabber";
 
506
                DiscoItem::Identities ids;
 
507
                ids.append(id);
 
508
                root.setIdentities(ids);
 
509
                updateInfo(root);
 
510
                alreadyInfo = true;
 
511
                autoInfo = false;
 
512
 
 
513
                // update items
 
514
                AgentList from = jt->agents();
 
515
                DiscoList to;
 
516
                AgentList::Iterator it = from.begin();
 
517
                for ( ; it != from.end(); ++it) {
 
518
                        DiscoItem item;
 
519
                        item.fromAgentItem( *it );
 
520
 
 
521
                        to.append( item );
 
522
                }
 
523
 
 
524
                updateItemsFinished(to);
 
525
        }
 
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));
 
529
        }
 
530
 
 
531
        alreadyItems = true;
 
532
}
 
533
 
 
534
QString DiscoListItem::computeHash( QString jid, QString node )
 
535
{
 
536
        QString ret = jid.replace( '@', "\\@" );
 
537
        ret += "@";
 
538
        ret += node.replace( '@', "\\@" );
 
539
        return ret;
 
540
}
 
541
 
 
542
void DiscoListItem::updateItemsFinished(const DiscoList &list)
 
543
{
 
544
        QDict<DiscoListItem> children;
 
545
        DiscoListItem *child = (DiscoListItem *)firstChild();
 
546
        while ( child ) {
 
547
                children.insert( child->hash(), child );
 
548
 
 
549
                child = (DiscoListItem *)child->nextSibling();
 
550
        }
 
551
 
 
552
        // add/update items
 
553
        for(DiscoList::ConstIterator it = list.begin(); it != list.end(); ++it) {
 
554
                const DiscoItem a = *it;
 
555
 
 
556
                QString key = computeHash(a.jid().full(), a.node());
 
557
                DiscoListItem *child = children[ key ];
 
558
 
 
559
                if ( child ) {
 
560
                        child->copyItem ( a );
 
561
                        children.remove( key );
 
562
                }
 
563
                else {
 
564
                        new DiscoListItem (a, d, this);
 
565
                }
 
566
        }
 
567
 
 
568
        // remove all items that are not on new DiscoList
 
569
        children.setAutoDelete( true );
 
570
        children.clear();
 
571
 
 
572
        if ( autoItems && isOpen() )
 
573
                autoItemsChildren();
 
574
 
 
575
        // don't forget to remove '+' (or '-') sign in case, that the child list is empty
 
576
        setExpandable ( !list.isEmpty() );
 
577
 
 
578
        repaint();
 
579
 
 
580
        // root item is initially hidden
 
581
        if ( isRoot && !isVisible() )
 
582
                setVisible (true);
 
583
}
 
584
 
 
585
void DiscoListItem::autoItemsChildren() const
 
586
{
 
587
        if ( !autoItemsEnabled() )
 
588
                return;
 
589
 
 
590
        DiscoListItem *child = (DiscoListItem *)firstChild();
 
591
        while ( child ) {
 
592
                child->updateItems(true);
 
593
 
 
594
                child = (DiscoListItem *)child->nextSibling();
 
595
        }
 
596
}
 
597
 
 
598
void DiscoListItem::updateInfo()
 
599
{
 
600
        if ( d->protocol != DiscoData::Auto && d->protocol != DiscoData::Disco )
 
601
                return;
 
602
 
 
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());
 
606
        jt->go(true);
 
607
        d->tasks->append(jt);
 
608
}
 
609
 
 
610
void DiscoListItem::discoInfoFinished()
 
611
{
 
612
        JT_DiscoInfo *jt = (JT_DiscoInfo *)sender();
 
613
 
 
614
        if ( jt->success() ) {
 
615
                updateInfo( jt->item() );
 
616
        }
 
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));
 
620
        }
 
621
 
 
622
        alreadyInfo = true;
 
623
        autoInfo = false;
 
624
}
 
625
 
 
626
void DiscoListItem::updateInfo(const DiscoItem &item)
 
627
{
 
628
        copyItem( item );
 
629
 
 
630
        if ( isRoot && !isVisible() )
 
631
                setVisible (true);
 
632
}
 
633
 
 
634
//----------------------------------------------------------------------------
 
635
// DiscoList
 
636
//----------------------------------------------------------------------------
 
637
 
 
638
class DiscoListView : public QListView, public QToolTip
 
639
{
 
640
        Q_OBJECT
 
641
public:
 
642
        DiscoListView(QWidget *parent);
 
643
 
 
644
protected:
 
645
        void maybeTip(const QPoint &);
 
646
        QDragObject *dragObject();
 
647
};
 
648
 
 
649
DiscoListView::DiscoListView(QWidget *parent)
 
650
: QListView(parent), QToolTip(viewport())
 
651
{
 
652
        addColumn( tr( "Name" ) );
 
653
        addColumn( tr( "JID" ) );
 
654
        addColumn( tr( "Node" ) );
 
655
}
 
656
 
 
657
void DiscoListView::maybeTip(const QPoint &pos)
 
658
{
 
659
        DiscoListItem *i = (DiscoListItem *)itemAt(pos);
 
660
        if(!i)
 
661
                return;
 
662
 
 
663
        // NAME <JID> (Node "NODE")
 
664
        //
 
665
        // Identities:
 
666
        // (icon) NAME (Category "CATEGORY"; Type "TYPE")
 
667
        // (icon) NAME (Category "CATEGORY"; Type "TYPE")
 
668
        //
 
669
        // Features:
 
670
        // NAME (http://jabber.org/feature)
 
671
        // NAME (http://jabber.org/feature)
 
672
 
 
673
        // top row
 
674
        QString text = "<qt><nobr>";
 
675
        DiscoItem item = i->item();
 
676
 
 
677
        if ( !item.name().isEmpty() )
 
678
                text += item.name() + " ";
 
679
 
 
680
        text += "&lt;" + item.jid().full() + "&gt;";
 
681
 
 
682
        if ( !item.node().isEmpty() )
 
683
                text += " (" + tr("Node") + " \"" + item.node() + "\")";
 
684
 
 
685
        text += "<nobr>";
 
686
 
 
687
        if ( !item.identities().isEmpty() || !item.features().list().isEmpty() )
 
688
                text += "<br>\n";
 
689
 
 
690
        // identities
 
691
        if ( !item.identities().isEmpty() ) {
 
692
                text += "<br>\n<b>" + tr("Identities:") + "</b>\n";
 
693
 
 
694
                DiscoItem::Identities::ConstIterator it = item.identities().begin();
 
695
                for ( ; it != item.identities().end(); ++it) {
 
696
                        text += "<br>";
 
697
                        Icon icon( category2icon((*it).category, (*it).type) );
 
698
                        if ( !icon.name().isEmpty() )
 
699
                                text += "<icon name=\"" + icon.name() + "\"> ";
 
700
                        text += (*it).name;
 
701
                        text += " (" + tr("Category") + " \"" + (*it).category + "\"; " + tr("Type") + " \"" + (*it).type + "\")\n";
 
702
                }
 
703
 
 
704
                if ( !item.features().list().isEmpty() )
 
705
                        text += "<br>\n";
 
706
        }
 
707
 
 
708
        // features
 
709
        if ( !item.features().list().isEmpty() ) {
 
710
                text += "<br>\n<b>" + tr("Features:") + "</b>\n";
 
711
 
 
712
                QStringList features = item.features().list();
 
713
                QStringList::ConstIterator it = features.begin();
 
714
                for ( ; it != features.end(); ++it) {
 
715
                        Features f( *it );
 
716
                        text += "\n<br>";
 
717
                        if ( f.id() > Features::FID_None )
 
718
                                text += f.name() + " (";
 
719
                        text += *it;
 
720
                        if ( f.id() > Features::FID_None )
 
721
                                text += ")";
 
722
                }
 
723
        }
 
724
 
 
725
        text += "</qt>";
 
726
        QRect r( itemRect(i) );
 
727
        tip(r, text);
 
728
}
 
729
 
 
730
QDragObject *DiscoListView::dragObject()
 
731
{
 
732
        DiscoListItem *i = (DiscoListItem *)selectedItem();
 
733
        if(!i)
 
734
                return 0;
 
735
 
 
736
        QDragObject *d = new QTextDrag(i->item().jid().full(), this);
 
737
        d->setPixmap(IconsetFactory::icon("status/online"), QPoint(8,8));
 
738
        return d;
 
739
}
 
740
 
 
741
//----------------------------------------------------------------------------
 
742
// DiscoDlg::Private
 
743
//----------------------------------------------------------------------------
 
744
 
 
745
class DiscoDlg::Private : public QObject
 
746
{
 
747
        Q_OBJECT
 
748
 
 
749
private:
 
750
        // helper class for use in toolbar
 
751
        class StretchWidget : public QWidget
 
752
        {
 
753
        public:
 
754
                StretchWidget(QWidget *parent)
 
755
                : QWidget(parent)
 
756
                {
 
757
                        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
 
758
                }
 
759
        };
 
760
 
 
761
        // helper class to store browser history
 
762
        class History {
 
763
        private:
 
764
                QListViewItem *item;
 
765
        public:
 
766
                History(QListViewItem *it) {
 
767
                        item = it;
 
768
                }
 
769
 
 
770
                ~History() {
 
771
                        if ( item )
 
772
                                delete item;
 
773
                }
 
774
 
 
775
                QListViewItem *takeItem() {
 
776
                        QListViewItem *i = item;
 
777
                        item = 0;
 
778
                        return i;
 
779
                }
 
780
        };
 
781
 
 
782
public: // data
 
783
        DiscoDlg *dlg;
 
784
        Jid jid;
 
785
        QString node;
 
786
 
 
787
        DiscoData data;
 
788
 
 
789
        PsiToolBar *toolBar;
 
790
        IconAction *actBrowse, *actBack, *actForward, *actRefresh, *actStop;
 
791
 
 
792
        // custom actions, that will be added to toolbar and context menu
 
793
        IconAction *actRegister, *actSearch, *actJoin, *actVCard, *actAdd;
 
794
 
 
795
        typedef QPtrList<History> HistoryList;
 
796
        HistoryList backHistory, forwardHistory;
 
797
 
 
798
        BusyWidget *busy;
 
799
 
 
800
public: // functions
 
801
        Private(DiscoDlg *parent, PsiAccount *pa);
 
802
        ~Private();
 
803
 
 
804
public slots:
 
805
        void doDisco(QString host = QString::null, QString node = QString::null);
 
806
 
 
807
        void actionStop();
 
808
        void actionRefresh();
 
809
        void actionBrowse();
 
810
 
 
811
        void actionBack();
 
812
        void actionForward();
 
813
        void updateBackForward();
 
814
        void backForwardHelper(QListViewItem *);
 
815
 
 
816
        void updateComboBoxes(Jid j, QString node);
 
817
 
 
818
        void itemUpdateStarted();
 
819
        void itemUpdateFinished();
 
820
 
 
821
        void disableButtons();
 
822
        void enableButtons(const DiscoItem &);
 
823
 
 
824
        void itemSelected (QListViewItem *);
 
825
        void itemDoubleclicked (QListViewItem *);
 
826
        bool eventFilter (QObject *, QEvent *);
 
827
 
 
828
        void setProtocol(int);
 
829
 
 
830
        // features...
 
831
        void actionActivated(int);
 
832
 
 
833
        void objectDestroyed(QObject *);
 
834
 
 
835
private:
 
836
        friend class DiscoListItem;
 
837
};
 
838
 
 
839
DiscoDlg::Private::Private(DiscoDlg *parent, PsiAccount *pa)
 
840
{
 
841
        dlg = parent;
 
842
        data.pa = 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;
 
849
 
 
850
        backHistory.setAutoDelete(true);
 
851
        forwardHistory.setAutoDelete(true);
 
852
 
 
853
        // mess with widgets
 
854
        busy = parent->busy;
 
855
        connect(busy, SIGNAL(destroyed(QObject *)), SLOT(objectDestroyed(QObject *)));
 
856
 
 
857
        QListView *lv_discoOld = dlg->lv_disco;
 
858
        dlg->lv_disco = new DiscoListView(dlg->centralWidget());
 
859
        replaceWidget(lv_discoOld, dlg->lv_disco);
 
860
 
 
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 *)));;
 
864
 
 
865
        // protocol actions
 
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);
 
870
 
 
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);
 
876
 
 
877
        // create actions
 
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()));
 
888
 
 
889
        // custom actions
 
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);
 
907
 
 
908
        // create toolbar
 
909
        toolBar = new PsiToolBar(dlg);
 
910
        toolBar->setCustomizeable( false );
 
911
 
 
912
        actBack->addTo(toolBar);
 
913
        actBrowse->addTo(toolBar);
 
914
        actForward->addTo(toolBar);
 
915
 
 
916
        toolBar->addSeparator();
 
917
        actRefresh->addTo(toolBar);
 
918
        actStop->addTo(toolBar);
 
919
 
 
920
        // custom actions
 
921
        toolBar->addSeparator();
 
922
        actRegister->addTo(toolBar);
 
923
        actSearch->addTo(toolBar);
 
924
        actJoin->addTo(toolBar);
 
925
 
 
926
        toolBar->addSeparator();
 
927
        actAdd->addTo(toolBar);
 
928
        actVCard->addTo(toolBar);
 
929
 
 
930
        // select protocol
 
931
        toolBar->addSeparator();
 
932
        autoProtocol->addTo(toolBar);
 
933
        discoProtocol->addTo(toolBar);
 
934
        browseProtocol->addTo(toolBar);
 
935
        agentsProtocol->addTo(toolBar);
 
936
 
 
937
        toolBar->setStretchableWidget(new StretchWidget(toolBar));
 
938
        pa->accountLabel(toolBar, true);
 
939
 
 
940
        // misc stuff
 
941
        disableButtons();
 
942
        actStop->setEnabled(false);     // stop action is not handled by disableButtons()
 
943
        updateBackForward();            // same applies to back & forward
 
944
}
 
945
 
 
946
DiscoDlg::Private::~Private()
 
947
{
 
948
        delete data.tasks;
 
949
}
 
950
 
 
951
void DiscoDlg::Private::doDisco(QString _host, QString _node)
 
952
{
 
953
        PsiAccount *pa = data.pa;
 
954
        if ( !pa->checkConnected(dlg) )
 
955
                return;
 
956
 
 
957
        // Strip whitespace
 
958
        Jid j;
 
959
        QString host = _host;
 
960
        if ( host.isEmpty() )
 
961
                host = dlg->cb_address->currentText();
 
962
        j = host.stripWhiteSpace();
 
963
        if ( !j.isValid() )
 
964
                return;
 
965
 
 
966
        QString n = _node.stripWhiteSpace();
 
967
        if ( n.isEmpty() )
 
968
                n = dlg->cb_node->currentText().stripWhiteSpace();
 
969
 
 
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
 
973
 
 
974
                if ( item ) {
 
975
                        dlg->lv_disco->takeItem( item );
 
976
 
 
977
                        backHistory.append( new History(item) );
 
978
                        forwardHistory.clear();
 
979
                }
 
980
        }
 
981
 
 
982
        jid  = j;
 
983
        node = n;
 
984
 
 
985
        updateComboBoxes(jid, node);
 
986
 
 
987
        data.tasks->clear(); // also will call all all necessary functions
 
988
        disableButtons();
 
989
        updateBackForward();
 
990
 
 
991
        dlg->lv_disco->clear();
 
992
 
 
993
        // create new root item
 
994
        DiscoItem di;
 
995
        di.setJid( jid );
 
996
        di.setNode( node );
 
997
 
 
998
        DiscoListItem *root = new DiscoListItem (di, &data, dlg->lv_disco);
 
999
        root->setVisible (false); // don't confuse users with empty root
 
1000
 
 
1001
        root->setOpen(true); // begin browsing
 
1002
}
 
1003
 
 
1004
void DiscoDlg::Private::updateComboBoxes(Jid j, QString n)
 
1005
{
 
1006
        data.pa->psi()->recentBrowseAdd( j.full() );
 
1007
        dlg->cb_address->clear();
 
1008
        dlg->cb_address->insertStringList(data.pa->psi()->recentBrowseList());
 
1009
 
 
1010
        data.pa->psi()->recentNodeAdd( n );
 
1011
        dlg->cb_node->clear();
 
1012
        dlg->cb_node->insertStringList(data.pa->psi()->recentNodeList());
 
1013
}
 
1014
 
 
1015
void DiscoDlg::Private::actionStop()
 
1016
{
 
1017
        data.tasks->clear();
 
1018
}
 
1019
 
 
1020
void DiscoDlg::Private::actionRefresh()
 
1021
{
 
1022
        DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
 
1023
        if ( !it )
 
1024
                return;
 
1025
 
 
1026
        it->updateItems();
 
1027
        it->updateInfo();
 
1028
}
 
1029
 
 
1030
void DiscoDlg::Private::actionBrowse()
 
1031
{
 
1032
        DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
 
1033
        if ( !it )
 
1034
                return;
 
1035
 
 
1036
        doDisco(it->item().jid().full(), it->item().node());
 
1037
}
 
1038
 
 
1039
void DiscoDlg::Private::actionBack()
 
1040
{
 
1041
        // add current selection to forward history
 
1042
        QListViewItem *item = dlg->lv_disco->firstChild();
 
1043
        if ( item ) {
 
1044
                dlg->lv_disco->takeItem( item );
 
1045
 
 
1046
                forwardHistory.append( new History(item) );
 
1047
        }
 
1048
 
 
1049
        // now, take info from back history...
 
1050
        QListViewItem *i = backHistory.last()->takeItem();
 
1051
        backHistory.removeLast();
 
1052
 
 
1053
        // and restore view
 
1054
        backForwardHelper(i);
 
1055
}
 
1056
 
 
1057
void DiscoDlg::Private::actionForward()
 
1058
{
 
1059
        // add current selection to back history
 
1060
        QListViewItem *item = dlg->lv_disco->firstChild();
 
1061
        if ( item ) {
 
1062
                dlg->lv_disco->takeItem( item );
 
1063
 
 
1064
                backHistory.append( new History(item) );
 
1065
        }
 
1066
 
 
1067
        // now, take info from forward history...
 
1068
        QListViewItem *i = forwardHistory.last()->takeItem();
 
1069
        forwardHistory.removeLast();
 
1070
 
 
1071
        // and restore view
 
1072
        backForwardHelper(i);
 
1073
}
 
1074
 
 
1075
void DiscoDlg::Private::backForwardHelper(QListViewItem *root)
 
1076
{
 
1077
        DiscoListItem *i = (DiscoListItem *)root;
 
1078
 
 
1079
        jid  = i->item().jid();
 
1080
        node = i->item().node();
 
1081
 
 
1082
        updateComboBoxes(jid, node);
 
1083
 
 
1084
        data.tasks->clear(); // also will call all all necessary functions
 
1085
        disableButtons();
 
1086
        updateBackForward();
 
1087
 
 
1088
        dlg->lv_disco->insertItem( root );
 
1089
 
 
1090
        // fixes multiple selection bug
 
1091
        QListViewItemIterator it( dlg->lv_disco );
 
1092
        while ( it.current() ) {
 
1093
                QListViewItem *item = it.current();
 
1094
                ++it;
 
1095
 
 
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);
 
1099
        }
 
1100
}
 
1101
 
 
1102
void DiscoDlg::Private::updateBackForward()
 
1103
{
 
1104
        actBack->setEnabled ( !backHistory.isEmpty() );
 
1105
        actForward->setEnabled ( !forwardHistory.isEmpty() );
 
1106
}
 
1107
 
 
1108
void DiscoDlg::Private::itemUpdateStarted()
 
1109
{
 
1110
        actStop->setEnabled(true);
 
1111
        if ( busy )
 
1112
                busy->start();
 
1113
}
 
1114
 
 
1115
void DiscoDlg::Private::itemUpdateFinished()
 
1116
{
 
1117
        actStop->setEnabled(false);
 
1118
        if ( busy )
 
1119
                busy->stop();
 
1120
}
 
1121
 
 
1122
void DiscoDlg::Private::disableButtons()
 
1123
{
 
1124
        DiscoItem di;
 
1125
        enableButtons ( di );
 
1126
}
 
1127
 
 
1128
void DiscoDlg::Private::enableButtons(const DiscoItem &it)
 
1129
{
 
1130
        bool itemSelected = !it.jid().full().isEmpty();
 
1131
        actRefresh->setEnabled( itemSelected );
 
1132
        actBrowse->setEnabled( itemSelected );
 
1133
 
 
1134
        // custom actions
 
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() );
 
1141
}
 
1142
 
 
1143
void DiscoDlg::Private::itemSelected (QListViewItem *item)
 
1144
{
 
1145
        DiscoListItem *it = (DiscoListItem *)item;
 
1146
        if ( !it ) {
 
1147
                disableButtons();
 
1148
                return;
 
1149
        }
 
1150
 
 
1151
        it->itemSelected();
 
1152
 
 
1153
        const DiscoItem di = it->item();
 
1154
        enableButtons ( di );
 
1155
}
 
1156
 
 
1157
void DiscoDlg::Private::itemDoubleclicked (QListViewItem *item)
 
1158
{
 
1159
        DiscoListItem *it = (DiscoListItem *)item;
 
1160
        if ( !it )
 
1161
                return;
 
1162
 
 
1163
        const DiscoItem d = it->item();
 
1164
        const Features &f = d.features();
 
1165
 
 
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 );
 
1172
                }
 
1173
                else {
 
1174
                        it->setOpen( false );
 
1175
                }
 
1176
        }
 
1177
 
 
1178
        long id = 0;
 
1179
 
 
1180
        // trigger default action
 
1181
        if ( f.canGroupchat() ) {
 
1182
                id = Features::FID_Groupchat;
 
1183
        }
 
1184
        else {
 
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";
 
1188
 
 
1189
                if ( searchFirst && f.canSearch() ) {
 
1190
                        id = Features::FID_Search;
 
1191
                }
 
1192
                else {
 
1193
                        if ( f.canRegister() )
 
1194
                                id = Features::FID_Register;
 
1195
                }
 
1196
        }
 
1197
 
 
1198
        if ( id > 0 )
 
1199
                emit dlg->featureActivated( Features::feature(id), d.jid(), d.node() );
 
1200
}
 
1201
 
 
1202
bool DiscoDlg::Private::eventFilter (QObject *object, QEvent *event)
 
1203
{
 
1204
        if ( object == dlg->lv_disco ) {
 
1205
                if ( event->type() == QEvent::ContextMenu ) {
 
1206
                        QContextMenuEvent *e = (QContextMenuEvent *)event;
 
1207
 
 
1208
                        DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
 
1209
                        if ( !it )
 
1210
                                return true;
 
1211
 
 
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) {
 
1218
                                        Features f( *it );
 
1219
                                        if ( f.id() > Features::FID_None )
 
1220
                                                idFeatures.append( Features::id(*it) );
 
1221
                                }
 
1222
                                //qHeapSort(idFeatures);
 
1223
                        }
 
1224
 
 
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() ) {
 
1230
                                        bool found = false;
 
1231
 
 
1232
                                        for (it = idFeatures.begin(); it != idFeatures.end(); ++it) {
 
1233
                                                if ( id == *it ) {
 
1234
                                                        if ( !found ) {
 
1235
                                                                found = true;
 
1236
                                                                ids.append( id );
 
1237
                                                        }
 
1238
                                                        count++;
 
1239
                                                }
 
1240
                                        }
 
1241
                                        id++;
 
1242
                                }
 
1243
                        }
 
1244
 
 
1245
                        // prepare popup menu
 
1246
                        QPopupMenu p;
 
1247
 
 
1248
                        actBrowse->addTo (&p);
 
1249
                        actRefresh->addTo (&p);
 
1250
                        actStop->addTo (&p);
 
1251
 
 
1252
                        // custom actions
 
1253
                        p.insertSeparator();
 
1254
                        actRegister->addTo(&p);
 
1255
                        actSearch->addTo(&p);
 
1256
                        actJoin->addTo(&p);
 
1257
 
 
1258
                        p.insertSeparator();
 
1259
                        actAdd->addTo(&p);
 
1260
                        actVCard->addTo(&p);
 
1261
 
 
1262
                        // popup with all available features
 
1263
                        QPopupMenu *fm = new QPopupMenu(&p);
 
1264
                        {
 
1265
                                QValueList<long>::Iterator it = ids.begin();
 
1266
                                for ( ; it != ids.end(); ++it)
 
1267
                                        fm->insertItem(Features::name(*it), *it + 10000); // TODO: add pixmap
 
1268
                        }
 
1269
 
 
1270
                        //p.insertSeparator();
 
1271
                        //int menuId = p.insertItem(tr("Activate &Feature"), fm);
 
1272
                        //p.setItemEnabled(menuId, !ids.isEmpty());
 
1273
 
 
1274
                        // display popup
 
1275
                        e->accept();
 
1276
                        int r = p.exec ( e->globalPos() );
 
1277
 
 
1278
                        if ( r > 10000 )
 
1279
                                actionActivated(r-10000);
 
1280
 
 
1281
                        return true;
 
1282
                }
 
1283
        }
 
1284
 
 
1285
        return false;
 
1286
}
 
1287
 
 
1288
void DiscoDlg::Private::actionActivated(int id)
 
1289
{
 
1290
        DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
 
1291
        if ( !it )
 
1292
                return;
 
1293
 
 
1294
        emit dlg->featureActivated(Features::feature(id), it->item().jid(), it->item().node());
 
1295
}
 
1296
 
 
1297
void DiscoDlg::Private::objectDestroyed(QObject *obj)
 
1298
{
 
1299
        if ( obj == busy )
 
1300
                busy = 0;
 
1301
}
 
1302
 
 
1303
void DiscoDlg::Private::setProtocol(int p)
 
1304
{
 
1305
        data.protocol = (DiscoData::Protocol)p;
 
1306
}
 
1307
 
 
1308
//----------------------------------------------------------------------------
 
1309
// DiscoDlg
 
1310
//----------------------------------------------------------------------------
 
1311
 
 
1312
DiscoDlg::DiscoDlg(PsiAccount *pa, const Jid &jid, const QString &node)
 
1313
: DiscoUI (0, 0, WDestructiveClose)
 
1314
{
 
1315
        // restore options
 
1316
        ck_autoItems->setChecked(option.discoItems);
 
1317
        ck_autoInfo->setChecked(option.discoInfo);
 
1318
 
 
1319
        // initialize
 
1320
        d = new Private(this, pa);
 
1321
        d->jid  = jid;
 
1322
        d->node = node;
 
1323
 
 
1324
        setCaption(CAP(caption()));
 
1325
        setIcon(is->transportStatus("transport", STATUS_ONLINE));
 
1326
        X11WM_CLASS("disco");
 
1327
 
 
1328
        statusBar()->hide();
 
1329
 
 
1330
        pb_browse->setDefault(true);
 
1331
        connect (pb_browse, SIGNAL(clicked()), d, SLOT(doDisco()));
 
1332
 
 
1333
 
 
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());
 
1339
 
 
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);
 
1344
 
 
1345
        if ( pa->loggedIn() )
 
1346
                doDisco();
 
1347
}
 
1348
 
 
1349
DiscoDlg::~DiscoDlg()
 
1350
{
 
1351
        delete d;
 
1352
 
 
1353
        // save options
 
1354
        option.discoItems = ck_autoItems->isChecked();
 
1355
        option.discoInfo  = ck_autoInfo->isChecked();
 
1356
}
 
1357
 
 
1358
void DiscoDlg::doDisco(QString host, QString node)
 
1359
{
 
1360
        d->doDisco(host, node);
 
1361
}
 
1362
 
 
1363
#include "discodlg.moc"