~ubuntu-branches/ubuntu/jaunty/psi/jaunty

« back to all changes in this revision

Viewing changes to src/tabdlg.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2008-04-14 18:57:30 UTC
  • mfrom: (2.1.9 hardy)
  • Revision ID: james.westby@ubuntu.com-20080414185730-528re3zp0m2hdlhi
Tags: 0.11-8
* added CONFIG -= link_prl to .pro files and removed dependencies
  which are made unnecessary by this change
* Fix segfault when closing last chat tab with qt4.4
  (This is from upstream svn, rev. 1101) (Closes: Bug#476122)

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 */
20
20
 
21
21
#include "tabdlg.h"
22
 
#include "chatdlg.h"
 
22
 
23
23
#include "iconwidget.h"
24
24
#include "iconset.h"
25
25
#include "common.h"
26
26
#include "psicon.h"
27
27
#include <qmenubar.h>
28
28
#include <qcursor.h>
29
 
#include <qdragobject.h>
 
29
#include <q3dragobject.h>
 
30
#include <QVBoxLayout>
 
31
#include <QDragMoveEvent>
 
32
#include <QResizeEvent>
 
33
#include <QKeyEvent>
 
34
#include <Q3PopupMenu>
 
35
#include <QDropEvent>
 
36
#include <QCloseEvent>
30
37
#include "psitabwidget.h"
 
38
#include "psioptions.h"
 
39
#include "shortcutmanager.h"
 
40
#include "chatdlg.h"
31
41
 
32
42
#ifdef Q_WS_WIN
33
 
#include<windows.h>
 
43
#include <windows.h>
34
44
#endif
35
45
 
36
46
//----------------------------------------------------------------------------
38
48
//----------------------------------------------------------------------------
39
49
TabDlg::TabDlg(PsiCon *psiCon)
40
50
{
 
51
        if ( option.brushedMetal )
 
52
                setAttribute(Qt::WA_MacMetalStyle);
41
53
        psi=psiCon;
42
54
 
43
 
        //this->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
44
 
 
45
 
        tabMenu = new QPopupMenu( this );
46
 
        connect( tabMenu, SIGNAL( aboutToShow() ), SLOT( buildTabMenu() ) );
47
 
        
48
 
        tabs = new KTabWidget (this);
49
 
 
50
 
        closeCross = new QPushButton(this);
51
 
        //closeCross->setText("x");
52
 
        closeCross->setIconSet(IconsetFactory::icon("psi/closetab").iconSet());
53
 
        closeCross->hide();
54
 
        if (option.usePerTabCloseButton)
55
 
          tabs->setHoverCloseButton(true);
56
 
        else
57
 
        {
58
 
                tabs->setHoverCloseButton(false);
59
 
                tabs->setCornerWidget( closeCross );
60
 
                closeCross->show();
61
 
        }
62
 
        
63
 
        tabs->setHoverCloseButtonDelayed(false); //people may want this enabled, but it's currently horribly broken.
64
 
        tabs->setTabReorderingEnabled(true);
65
 
        tabs->setCloseIcon(IconsetFactory::icon("psi/closetab").iconSet());
66
 
        connect (tabs, SIGNAL( mouseDoubleClick( QWidget* ) ), SLOT( detachChat( QWidget* ) ) ); 
67
 
        connect (tabs, SIGNAL( testCanDecode(const QDragMoveEvent*, bool&) ), SLOT( tabTestCanDecode(const QDragMoveEvent*, bool&) ) );
68
 
        connect (tabs, SIGNAL( receivedDropEvent( QDropEvent* ) ), SLOT( tabReceivedDropEvent( QDropEvent* ) ) );
69
 
        connect (tabs, SIGNAL( receivedDropEvent( QWidget*, QDropEvent* ) ), SLOT( tabReceivedDropEvent( QWidget*, QDropEvent* ) ) );
70
 
        connect (tabs, SIGNAL( initiateDrag( QWidget* ) ), SLOT( startDrag( QWidget* ) ) );
71
 
        connect (tabs, SIGNAL( closeRequest( QWidget* ) ), SLOT( closeChat( QWidget* ) ) );
 
55
        tabMenu = new QMenu( this );
 
56
        
 
57
        tabs = new PsiTabWidget (this);
 
58
        tabs->setCloseIcon(IconsetFactory::icon("psi/closetab").icon());
 
59
        //tabs->setCloseIcon(IconsetFactory::icon("psi/closetab").iconSet());
 
60
        connect (tabs, SIGNAL( mouseDoubleClickTab( QWidget* ) ), SLOT( detachChat( QWidget* ) ) );
 
61
        connect (tabs, SIGNAL(aboutToShowMenu(QMenu *)), SLOT(tab_aboutToShowMenu(QMenu *)));
 
62
        connect (tabs, SIGNAL(tabContextMenu(int,QPoint,QContextMenuEvent*)), SLOT(showTabMenu(int,QPoint,QContextMenuEvent*)));
 
63
 
 
64
        //connect (tabs, SIGNAL( testCanDecode(const QDragMoveEvent*, bool&) ), SLOT( tabTestCanDecode(const QDragMoveEvent*, bool&) ) );
 
65
        //connect (tabs, SIGNAL( receivedDropEvent( QDropEvent* ) ), SLOT( tabReceivedDropEvent( QDropEvent* ) ) );
 
66
        //connect (tabs, SIGNAL( receivedDropEvent( QWidget*, QDropEvent* ) ), SLOT( tabReceivedDropEvent( QWidget*, QDropEvent* ) ) );
 
67
        //connect (tabs, SIGNAL( initiateDrag( QWidget* ) ), SLOT( startDrag( QWidget* ) ) );
 
68
        //connect (tabs, SIGNAL( closeRequest( QWidget* ) ), SLOT( closeChat( QWidget* ) ) );
72
69
        
73
70
                
74
71
        QVBoxLayout *vert1 = new QVBoxLayout( this, 1);
76
73
        chats.setAutoDelete( FALSE );
77
74
        X11WM_CLASS("chat");
78
75
        
79
 
        connect( closeCross, SIGNAL( clicked() ), SLOT( closeChat() ) );
 
76
        connect( tabs, SIGNAL( closeButtonClicked() ), SLOT( closeChat() ) );
80
77
        connect( tabs, SIGNAL( currentChanged( QWidget* ) ), SLOT( tabSelected( QWidget* ) ) ); 
81
78
 
82
79
        setAcceptDrops(TRUE);
83
80
 
84
 
 
85
 
 
86
 
        
87
81
        setLooks();
88
82
 
89
83
        resize(option.sizeTabDlg);
 
84
 
 
85
        act_close = new QAction(this);
 
86
        addAction(act_close);
 
87
        connect(act_close,SIGNAL(activated()), SLOT(closeChat()));
 
88
        act_prev = new QAction(this);
 
89
        addAction(act_prev);
 
90
        connect(act_prev,SIGNAL(activated()), SLOT(previousTab()));
 
91
        act_next = new QAction(this);
 
92
        addAction(act_next);
 
93
        connect(act_next,SIGNAL(activated()), SLOT(nextTab()));
 
94
 
 
95
        setShortcuts();
90
96
}
91
97
 
92
98
TabDlg::~TabDlg()
94
100
 
95
101
}
96
102
 
 
103
 
 
104
Q_DECLARE_METATYPE ( TabDlg* );
 
105
 
 
106
 
 
107
void TabDlg::setShortcuts()
 
108
{
 
109
        //act_close->setShortcuts(ShortcutManager::instance()->shortcuts("common.close"));
 
110
        act_prev->setShortcuts(ShortcutManager::instance()->shortcuts("chat.previous-tab"));
 
111
        act_next->setShortcuts(ShortcutManager::instance()->shortcuts("chat.next-tab"));
 
112
}
 
113
 
97
114
void TabDlg::resizeEvent(QResizeEvent *e)
98
115
{
99
116
  if(option.keepSizes)
100
117
        option.sizeTabDlg = e->size();
101
118
}
102
119
 
103
 
void TabDlg::buildTabMenu()
 
120
void TabDlg::showTabMenu(int tab, QPoint pos, QContextMenuEvent * event)
104
121
{
105
122
        tabMenu->clear();
106
 
        tabMenu->insertItem( tr("Detach Current Tab"), this, SLOT( detachChat() ) );
107
 
        tabMenu->insertItem( tr("Close Current Tab"), this, SLOT( closeChat() ) );
108
 
 
109
 
        QPopupMenu* sendTo = new QPopupMenu(tabMenu);
 
123
 
 
124
        if (tab!=-1) {
 
125
                QAction *d = tabMenu->addAction(tr("Detach Tab"));
 
126
                QAction *c = tabMenu->addAction(tr("Close Tab"));
 
127
 
 
128
                QMenu* sendTo = new QMenu(tabMenu);
 
129
                sendTo->setTitle(tr("Send Tab to"));
 
130
                QMap<QAction*, TabDlg*> sentTos;
 
131
                for (uint i = 0; i < psi->getTabSets()->count(); ++i)
 
132
                {
 
133
                        TabDlg* tabSet= psi->getTabSets()->at(i);
 
134
                        QAction *act = sendTo->addAction( tabSet->getName());
 
135
                        if (tabSet == this) act->setEnabled(false);
 
136
                        sentTos[act] = tabSet;
 
137
                }
 
138
                tabMenu->addMenu(sendTo);
 
139
 
 
140
                QAction *act = tabMenu->exec(pos);
 
141
                if (!act) return;
 
142
                if (act == c) {
 
143
                        closeChat(getTab(tab));
 
144
                } else if (act == d) {
 
145
                        detachChat(getTab(tab));
 
146
                } else {
 
147
                        TabDlg* target = sentTos[act];
 
148
                        if (target) queuedSendChatTo(getTab(tab), target);
 
149
                }
 
150
        }
 
151
}
 
152
 
 
153
void TabDlg::tab_aboutToShowMenu(QMenu *menu)
 
154
{
 
155
        menu->addSeparator ();
 
156
        menu->addAction( tr("Detach Current Tab"), this, SLOT( detachChat() ) );
 
157
        menu->addAction( tr("Close Current Tab"), this, SLOT( closeChat() ) );
 
158
 
 
159
        QMenu* sendTo = new QMenu(menu);
 
160
        sendTo->setTitle(tr("Send Current Tab to"));
 
161
        int tabdlgmetatype = qRegisterMetaType<TabDlg*>("TabDlg*");
110
162
        for (uint i = 0; i < psi->getTabSets()->count(); ++i)
111
163
        {
112
164
                TabDlg* tabSet= psi->getTabSets()->at(i);
113
 
                sendTo->insertItem( tabSet->getName(), this, SLOT( sendChatTo( tabSet ) ) );
 
165
                QAction *act = sendTo->addAction( tabSet->getName());
 
166
                act->setData(QVariant(tabdlgmetatype, &tabSet));
 
167
                if (tabSet == this) act->setEnabled(false);
114
168
        }
115
 
        tabMenu->insertItem( tr("Sent Current Tab to"), sendTo);
 
169
        connect(sendTo, SIGNAL(triggered(QAction*)), SLOT(menu_sendChatTo(QAction*)));
 
170
        menu->addMenu(sendTo);
 
171
}
 
172
 
 
173
void TabDlg::menu_sendChatTo(QAction *act)
 
174
{
 
175
        queuedSendChatTo(tabs->currentPage(), act->data().value<TabDlg*>());
116
176
}
117
177
 
118
178
void TabDlg::sendChatTo(QWidget* chatw, TabDlg* otherTabs)
124
184
        otherTabs->addChat(chat);
125
185
}
126
186
 
 
187
void TabDlg::queuedSendChatTo(QWidget* chat, TabDlg *dest)
 
188
{
 
189
        qRegisterMetaType<TabDlg*>("TabDlg*");
 
190
        QMetaObject::invokeMethod(this, "sendChatTo",  Qt::QueuedConnection, Q_ARG(QWidget*, chat), Q_ARG(TabDlg*, dest));
 
191
}
 
192
 
 
193
void TabDlg::optionsUpdate()
 
194
{
 
195
        setShortcuts();
 
196
}
 
197
 
127
198
void TabDlg::setLooks()
128
199
{
129
200
        //set the widget icon
130
201
#ifndef Q_WS_MAC
131
 
        setIcon(IconsetFactory::icon("psi/start-chat"));
 
202
        setWindowIcon(IconsetFactory::icon("psi/start-chat").icon());
132
203
#endif
133
204
        tabs->setTabPosition(QTabWidget::Top);
134
205
        if (option.putTabsAtBottom)
135
206
                tabs->setTabPosition(QTabWidget::Bottom);
136
207
 
137
 
#if QT_VERSION >= 0x030300
138
 
        setWindowOpacity(double(option.chatOpacity)/100);
139
 
#endif
 
208
        setWindowOpacity(double(qMax(MINIMUM_OPACITY,PsiOptions::instance()->getOption("options.ui.chat.opacity").toInt()))/100);
140
209
}
141
210
 
142
211
QString TabDlg::getName()
146
215
 
147
216
void TabDlg::tabSelected(QWidget* chat)
148
217
{
 
218
        if (!chat) return; // FIXME
149
219
        ((ChatDlg*)chat)->activated(); //is this still necessary?
150
220
        updateCaption();
151
221
}
167
237
void TabDlg::addChat(ChatDlg* chat)
168
238
{
169
239
        chats.append(chat);
170
 
        tabs->addTab(chat, chat->getDisplayNick());
171
 
        tabs->setTabIconSet(chat, IconsetFactory::icon("psi/start-chat"));
 
240
        QString tablabel = chat->getDisplayNick();
 
241
        tablabel.replace("&", "&&");
 
242
        tabs->addTab(chat, tablabel);
 
243
        //tabs->setTabIconSet(chat, IconsetFactory::icon("psi/start-chat").icon());
172
244
 
173
 
        tabs->showPage(chat);
 
245
        //tabs->showPage(chat);
174
246
        connect ( chat, SIGNAL( captionChanged( ChatDlg*) ), SLOT( updateTab( ChatDlg* ) ) );
175
 
        connect ( chat, SIGNAL( contactIsComposing(ChatDlg*, bool) ), SLOT( setTabComposing( ChatDlg*, bool) ) );
 
247
        connect ( chat, SIGNAL( contactStateChanged( XMPP::ChatState ) ), SLOT( setTabState( XMPP::ChatState ) ) );
176
248
        connect ( chat, SIGNAL( unreadMessageUpdate(ChatDlg*, int) ), SLOT( setTabHasMessages(ChatDlg*, int) ) );
177
249
        
178
250
        this->show();
190
262
        if (tabs->count()==1)
191
263
                return;
192
264
        
 
265
        if (!chat) { // fail gracefully this is delayed/signaled user input.
 
266
                return;
 
267
        }
 
268
 
193
269
        TabDlg *newTab = psi->newTabs();
194
270
        sendChatTo(chat, newTab);
195
271
}
200
276
        closeChat(chat);
201
277
}
202
278
 
 
279
/**
 
280
 * Removes the chat from the tabset, 'closing' it if specified.
 
281
 * The method is used without closing tabs when transferring from one
 
282
 * tabset to another.
 
283
 * \param chat Chat to remove.
 
284
 * \param doclose Whether the chat is 'closed' while removing it.
 
285
 */ 
203
286
void TabDlg::closeChat(ChatDlg* chat, bool doclose=true)
204
287
{
 
288
        if (doclose && !chat->readyToHide()) {
 
289
                return;
 
290
        }
205
291
        chat->hide();
206
292
        disconnect ( chat, SIGNAL( captionChanged( ChatDlg*) ), this, SLOT( updateTab( ChatDlg* ) ) );
207
 
        disconnect ( chat, SIGNAL( contactIsComposing(ChatDlg*, bool) ), this, SLOT( setTabComposing( ChatDlg*, bool) ) );
 
293
        disconnect ( chat, SIGNAL( contactStateChanged( XMPP::ChatState ) ), this, SLOT( setTabState( XMPP::ChatState ) ) );
208
294
        disconnect ( chat, SIGNAL( unreadMessageUpdate(ChatDlg*, int) ), this, SLOT( setTabHasMessages(ChatDlg*, int) ) );
209
295
        tabs->removePage(chat);
210
296
        tabIsComposing.erase(chat);
211
297
        tabHasMessages.erase(chat);
212
298
        chats.remove(chat);
213
299
        chat->reparent(0,QPoint());
214
 
        if (doclose)
 
300
        if (doclose && chat->testAttribute(Qt::WA_DeleteOnClose))
215
301
                chat->close();
216
302
        if (tabs->count()>0)
217
303
                updateCaption();
255
341
{
256
342
        QString cap = "";
257
343
        uint pending=0;
258
 
        for ( uint i=0; i<tabHasMessages.count(); ++i)
 
344
        for ( int i=0; i<tabHasMessages.count(); ++i)
259
345
        {
260
346
                pending+=tabHasMessages.values()[i];
261
347
        }
265
351
                        cap += QString("[%1] ").arg(pending);
266
352
        }
267
353
        cap += getName();
268
 
 
269
 
        setCaption(cap);
 
354
        if (tabIsComposing[(ChatDlg*)(tabs->currentPage())])
 
355
                cap += tr(" is composing");
 
356
        setWindowTitle(cap);
270
357
}
271
358
 
272
359
void TabDlg::closeEvent(QCloseEvent* closeEvent)
284
371
        //we do not delete it here, let the PsiCon do that, they create, they destroy.
285
372
}
286
373
 
 
374
 
 
375
ChatDlg *TabDlg::getTab(int i)
 
376
{
 
377
        return ((ChatDlg*)tabs->page(i));
 
378
}
 
379
 
 
380
 
287
381
ChatDlg* TabDlg::getChatPointer(QString fullJid)
288
382
{
289
383
        for (int i=0; i < tabs->count() ; i++)
290
384
        {
291
 
                if (((ChatDlg*)tabs->page(i))->jid().full()==fullJid)
 
385
                if (getTab(i)->jid().full()==fullJid)
292
386
                {
293
 
                        return (ChatDlg*)(tabs->page(i));
 
387
                        return getTab(i);
294
388
                }
295
389
        }
296
390
        return false;
314
408
        }
315
409
 
316
410
        label=prefix+chat->getDisplayNick();
 
411
        label.replace("&", "&&");
317
412
        tabs->setTabLabel( chat, label );
318
413
        //now set text colour based upon whether there are new messages/composing etc
 
414
 
319
415
        if (tabIsComposing[chat])
320
 
                tabs->setTabColor( chat, Qt::darkGreen );
 
416
                tabs->setTabTextColor( chat, Qt::darkGreen );
321
417
        else if (tabHasMessages[chat])
322
418
        {
323
 
                tabs->setTabColor( chat, Qt::red );
324
 
                doFlash(true);
 
419
                tabs->setTabTextColor( chat, Qt::red );
 
420
                if (PsiOptions::instance()->getOption("options.ui.flash-windows").toBool())
 
421
                        doFlash(true);
325
422
        }
326
423
        else
327
 
                tabs->setTabColor( chat, Qt::black );
 
424
                tabs->setTabTextColor( chat, Qt::black );
328
425
        updateCaption();
329
426
}
330
427
 
331
 
void TabDlg::setTabComposing(ChatDlg* chat, bool composing)
 
428
void TabDlg::setTabState( XMPP::ChatState state )
332
429
{
333
 
        tabIsComposing[chat]=composing;
 
430
        ChatDlg* chat = (ChatDlg*) sender();
 
431
        if ( state == XMPP::StateComposing )
 
432
                tabIsComposing[chat] = true;
 
433
        else
 
434
                tabIsComposing[chat] = false;
334
435
        updateTab(chat);
335
436
}
336
437
 
356
457
        tabs->setCurrentPage( page );
357
458
}
358
459
 
359
 
void TabDlg::tabTestCanDecode(const QDragMoveEvent* e, bool &b){
360
 
        QString jid;
361
 
        QCString type;
362
 
        if ( QTextDrag::canDecode(e) && QTextDrag::decode(e, jid, type) && type=="psichatwindow" )
363
 
        {
364
 
                b=true;
365
 
        }
366
 
        else
367
 
        {
368
 
                b=false;
369
 
        }
370
 
}
371
 
 
372
 
void TabDlg::tabReceivedDropEvent(QDropEvent* e){
373
 
    ChatDlg* chat;
374
 
                QString  jid;
375
 
                QCString type;
376
 
    if ( QTextDrag::decode(e, jid, type) && type=="psichatwindow" ) {
377
 
                        chat=psi->getChatInTabs(jid);
378
 
                        if (chat)
379
 
                        {
380
 
                                TabDlg *dlg=psi->getManagingTabs(chat);
381
 
                                dlg->sendChatTo(chat, this);
382
 
                        }
383
 
    }   
384
 
}
385
 
 
386
 
void TabDlg::tabReceivedDropEvent(QWidget* w, QDropEvent* e){
387
 
        Q_UNUSED(w);
388
 
        tabReceivedDropEvent(e);
389
 
}
390
 
 
391
 
 
392
 
void TabDlg::startDrag(QWidget* w)
393
 
{
394
 
    QDragObject *d = new QTextDrag( ((ChatDlg*)w)->jid().full(), this );
395
 
                ((QTextDrag*)d)->setSubtype("psichatwindow");
396
 
    d->dragCopy();
397
 
    // do NOT delete d.
398
 
}
399
 
 
400
460
void TabDlg::keyPressEvent(QKeyEvent *e)
401
461
{
402
 
        if (e->key() == Key_Escape)
403
 
        {
404
 
                closeChat();
405
 
        }
406
 
        else if ( e->key() == Key_W && (e->state() & ControlButton) )
407
 
        {
408
 
                closeChat();
409
 
        }
410
 
        else if ( e->key() == Key_PageUp && (e->state() & ControlButton) )
 
462
        if (e->key() == Qt::Key_Escape)
 
463
        {
 
464
                closeChat();
 
465
        }
 
466
        else if ( e->key() == Qt::Key_W && (e->modifiers() & Qt::ControlModifier) )
 
467
        {
 
468
                closeChat();
 
469
        }
 
470
        else if ( e->key() == Qt::Key_PageUp && (e->modifiers() & Qt::ControlModifier) )
411
471
        {
412
472
                previousTab();
413
473
        }
414
 
        else if ( e->key() == Key_PageDown && (e->state() & ControlButton) )
 
474
        else if ( e->key() == Qt::Key_PageDown && (e->modifiers() & Qt::ControlModifier) )
415
475
        {
416
476
                nextTab();
417
477
        }
420
480
        
421
481
}
422
482
 
 
483
void TabDlg::dragEnterEvent(QDragEnterEvent *event)
 
484
{
 
485
        if ( event->mimeData()->hasFormat("psiTabDrag") ) {
 
486
                event->setDropAction(Qt::MoveAction);
 
487
                event->accept();
 
488
        }
 
489
}
 
490
 
 
491
void TabDlg::dropEvent(QDropEvent *event)
 
492
{
 
493
        QByteArray data;
 
494
        if (event->mimeData()->hasFormat("psiTabDrag")) {
 
495
                data = event->mimeData()->data("psiTabDrag");
 
496
        } else {
 
497
                return;
 
498
        }
 
499
        int remoteTab = data.toInt();
 
500
        event->acceptProposedAction();
 
501
        //the event's been and gone, now do something about it
 
502
        PsiTabBar* source = dynamic_cast<PsiTabBar*> (event->source());
 
503
        if (source)
 
504
        {
 
505
                PsiTabWidget* barParent = source->psiTabWidget();
 
506
                QWidget* widget = barParent->widget(remoteTab);
 
507
                ChatDlg* chat=dynamic_cast<ChatDlg*>(widget);
 
508
                TabDlg *dlg=psi->getManagingTabs(chat);
 
509
                if (!chat || !dlg)
 
510
                        return;
 
511
                dlg->queuedSendChatTo(chat, this);
 
512
        } 
 
513
        
 
514
}