2
* tabdlg.cpp - dialog for handling tabbed chats
3
* Copyright (C) 2005 Kevin Smith
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 "iconwidget.h"
29
#include <q3dragobject.h>
30
#include <QVBoxLayout>
31
#include <QDragMoveEvent>
32
#include <QResizeEvent>
34
#include <Q3PopupMenu>
36
#include <QCloseEvent>
37
#include "psitabwidget.h"
38
#include "psioptions.h"
39
#include "shortcutmanager.h"
46
//----------------------------------------------------------------------------
48
//----------------------------------------------------------------------------
49
TabDlg::TabDlg(PsiCon *psiCon)
51
if ( option.brushedMetal )
52
setAttribute(Qt::WA_MacMetalStyle);
55
tabMenu = new QMenu( this );
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*)));
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* ) ) );
71
QVBoxLayout *vert1 = new QVBoxLayout( this, 1);
72
vert1->addWidget(tabs);
73
chats.setAutoDelete( FALSE );
76
connect( tabs, SIGNAL( closeButtonClicked() ), SLOT( closeChat() ) );
77
connect( tabs, SIGNAL( currentChanged( QWidget* ) ), SLOT( tabSelected( QWidget* ) ) );
83
resize(option.sizeTabDlg);
85
act_close = new QAction(this);
87
connect(act_close,SIGNAL(activated()), SLOT(closeChat()));
88
act_prev = new QAction(this);
90
connect(act_prev,SIGNAL(activated()), SLOT(previousTab()));
91
act_next = new QAction(this);
93
connect(act_next,SIGNAL(activated()), SLOT(nextTab()));
104
Q_DECLARE_METATYPE ( TabDlg* );
107
void TabDlg::setShortcuts()
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"));
114
void TabDlg::resizeEvent(QResizeEvent *e)
117
option.sizeTabDlg = e->size();
120
void TabDlg::showTabMenu(int tab, QPoint pos, QContextMenuEvent * event)
125
QAction *d = tabMenu->addAction(tr("Detach Tab"));
126
QAction *c = tabMenu->addAction(tr("Close Tab"));
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)
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;
138
tabMenu->addMenu(sendTo);
140
QAction *act = tabMenu->exec(pos);
143
closeChat(getTab(tab));
144
} else if (act == d) {
145
detachChat(getTab(tab));
147
TabDlg* target = sentTos[act];
148
if (target) queuedSendChatTo(getTab(tab), target);
153
void TabDlg::tab_aboutToShowMenu(QMenu *menu)
155
menu->addSeparator ();
156
menu->addAction( tr("Detach Current Tab"), this, SLOT( detachChat() ) );
157
menu->addAction( tr("Close Current Tab"), this, SLOT( closeChat() ) );
159
QMenu* sendTo = new QMenu(menu);
160
sendTo->setTitle(tr("Send Current Tab to"));
161
int tabdlgmetatype = qRegisterMetaType<TabDlg*>("TabDlg*");
162
for (uint i = 0; i < psi->getTabSets()->count(); ++i)
164
TabDlg* tabSet= psi->getTabSets()->at(i);
165
QAction *act = sendTo->addAction( tabSet->getName());
166
act->setData(QVariant(tabdlgmetatype, &tabSet));
167
if (tabSet == this) act->setEnabled(false);
169
connect(sendTo, SIGNAL(triggered(QAction*)), SLOT(menu_sendChatTo(QAction*)));
170
menu->addMenu(sendTo);
173
void TabDlg::menu_sendChatTo(QAction *act)
175
queuedSendChatTo(tabs->currentPage(), act->data().value<TabDlg*>());
178
void TabDlg::sendChatTo(QWidget* chatw, TabDlg* otherTabs)
182
ChatDlg* chat = (ChatDlg*)chatw;
183
closeChat(chat, false);
184
otherTabs->addChat(chat);
187
void TabDlg::queuedSendChatTo(QWidget* chat, TabDlg *dest)
189
qRegisterMetaType<TabDlg*>("TabDlg*");
190
QMetaObject::invokeMethod(this, "sendChatTo", Qt::QueuedConnection, Q_ARG(QWidget*, chat), Q_ARG(TabDlg*, dest));
193
void TabDlg::optionsUpdate()
198
void TabDlg::setLooks()
200
//set the widget icon
202
setWindowIcon(IconsetFactory::icon("psi/start-chat").icon());
204
tabs->setTabPosition(QTabWidget::Top);
205
if (option.putTabsAtBottom)
206
tabs->setTabPosition(QTabWidget::Bottom);
208
setWindowOpacity(double(qMax(MINIMUM_OPACITY,PsiOptions::instance()->getOption("options.ui.chat.opacity").toInt()))/100);
211
QString TabDlg::getName()
213
return ((ChatDlg*)(tabs->currentPage()))->getDisplayNick();
216
void TabDlg::tabSelected(QWidget* chat)
218
if (!chat) return; // FIXME
219
((ChatDlg*)chat)->activated(); //is this still necessary?
223
bool TabDlg::managesChat(ChatDlg* chat)
225
if ( chats.contains(chat) )
230
bool TabDlg::chatOnTop(ChatDlg* chat)
232
if ( tabs->currentPage() == chat )
237
void TabDlg::addChat(ChatDlg* chat)
240
QString tablabel = chat->getDisplayNick();
241
tablabel.replace("&", "&&");
242
tabs->addTab(chat, tablabel);
243
//tabs->setTabIconSet(chat, IconsetFactory::icon("psi/start-chat").icon());
245
//tabs->showPage(chat);
246
connect ( chat, SIGNAL( captionChanged( ChatDlg*) ), SLOT( updateTab( ChatDlg* ) ) );
247
connect ( chat, SIGNAL( contactStateChanged( XMPP::ChatState ) ), SLOT( setTabState( XMPP::ChatState ) ) );
248
connect ( chat, SIGNAL( unreadMessageUpdate(ChatDlg*, int) ), SLOT( setTabHasMessages(ChatDlg*, int) ) );
254
void TabDlg::detachChat()
256
detachChat(tabs->currentPage());
259
void TabDlg::detachChat(QWidget* chat)
261
//don't detach singleton chats, fix for flyspray #477
262
if (tabs->count()==1)
265
if (!chat) { // fail gracefully this is delayed/signaled user input.
269
TabDlg *newTab = psi->newTabs();
270
sendChatTo(chat, newTab);
273
void TabDlg::closeChat()
275
ChatDlg* chat = (ChatDlg*)(tabs->currentPage());
280
* Removes the chat from the tabset, 'closing' it if specified.
281
* The method is used without closing tabs when transferring from one
283
* \param chat Chat to remove.
284
* \param doclose Whether the chat is 'closed' while removing it.
286
void TabDlg::closeChat(ChatDlg* chat, bool doclose=true)
288
if (doclose && !chat->readyToHide()) {
292
disconnect ( chat, SIGNAL( captionChanged( ChatDlg*) ), this, SLOT( updateTab( ChatDlg* ) ) );
293
disconnect ( chat, SIGNAL( contactStateChanged( XMPP::ChatState ) ), this, SLOT( setTabState( XMPP::ChatState ) ) );
294
disconnect ( chat, SIGNAL( unreadMessageUpdate(ChatDlg*, int) ), this, SLOT( setTabHasMessages(ChatDlg*, int) ) );
295
tabs->removePage(chat);
296
tabIsComposing.erase(chat);
297
tabHasMessages.erase(chat);
299
chat->reparent(0,QPoint());
300
if (doclose && chat->testAttribute(Qt::WA_DeleteOnClose))
307
void TabDlg::closeChat(QWidget* chat)
309
closeChat((ChatDlg*)chat);
312
void TabDlg::selectTab(ChatDlg* chat)
314
tabs->showPage(chat);
317
void TabDlg::checkHasChats()
324
void TabDlg::windowActivationChange(bool oldstate)
326
QWidget::windowActivationChange(oldstate);
328
// if we're bringing it to the front, get rid of the '*' if necessary
329
if( isActiveWindow() ) {
334
void TabDlg::activated()
340
void TabDlg::updateCaption()
344
for ( int i=0; i<tabHasMessages.count(); ++i)
346
pending+=tabHasMessages.values()[i];
351
cap += QString("[%1] ").arg(pending);
354
if (tabIsComposing[(ChatDlg*)(tabs->currentPage())])
355
cap += tr(" is composing");
359
void TabDlg::closeEvent(QCloseEvent* closeEvent)
361
Q_UNUSED(closeEvent);
362
int count=tabs->count();
363
for (int i=0;i<count;++i) {
368
void TabDlg::closeMe()
371
//we do not delete it here, let the PsiCon do that, they create, they destroy.
375
ChatDlg *TabDlg::getTab(int i)
377
return ((ChatDlg*)tabs->page(i));
381
ChatDlg* TabDlg::getChatPointer(QString fullJid)
383
for (int i=0; i < tabs->count() ; i++)
385
if (getTab(i)->jid().full()==fullJid)
393
void TabDlg::updateTab( ChatDlg* chat)
395
QString label, prefix;
396
int num=tabHasMessages[chat];
407
prefix=QString("[%1] ").arg(num);
410
label=prefix+chat->getDisplayNick();
411
label.replace("&", "&&");
412
tabs->setTabLabel( chat, label );
413
//now set text colour based upon whether there are new messages/composing etc
415
if (tabIsComposing[chat])
416
tabs->setTabTextColor( chat, Qt::darkGreen );
417
else if (tabHasMessages[chat])
419
tabs->setTabTextColor( chat, Qt::red );
420
if (PsiOptions::instance()->getOption("options.ui.flash-windows").toBool())
424
tabs->setTabTextColor( chat, Qt::black );
428
void TabDlg::setTabState( XMPP::ChatState state )
430
ChatDlg* chat = (ChatDlg*) sender();
431
if ( state == XMPP::StateComposing )
432
tabIsComposing[chat] = true;
434
tabIsComposing[chat] = false;
438
void TabDlg::setTabHasMessages(ChatDlg* chat, int messages)
440
tabHasMessages[chat]=messages;
444
void TabDlg::nextTab()
446
int page = tabs->currentPageIndex()+1;
447
if ( page >= tabs->count() )
449
tabs->setCurrentPage( page );
452
void TabDlg::previousTab()
454
int page = tabs->currentPageIndex()-1;
456
page = tabs->count() - 1;
457
tabs->setCurrentPage( page );
460
void TabDlg::keyPressEvent(QKeyEvent *e)
462
if (e->key() == Qt::Key_Escape)
466
else if ( e->key() == Qt::Key_W && (e->modifiers() & Qt::ControlModifier) )
470
else if ( e->key() == Qt::Key_PageUp && (e->modifiers() & Qt::ControlModifier) )
474
else if ( e->key() == Qt::Key_PageDown && (e->modifiers() & Qt::ControlModifier) )
483
void TabDlg::dragEnterEvent(QDragEnterEvent *event)
485
if ( event->mimeData()->hasFormat("psiTabDrag") ) {
486
event->setDropAction(Qt::MoveAction);
491
void TabDlg::dropEvent(QDropEvent *event)
494
if (event->mimeData()->hasFormat("psiTabDrag")) {
495
data = event->mimeData()->data("psiTabDrag");
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());
505
PsiTabWidget* barParent = source->psiTabWidget();
506
QWidget* widget = barParent->widget(remoteTab);
507
ChatDlg* chat=dynamic_cast<ChatDlg*>(widget);
508
TabDlg *dlg=psi->getManagingTabs(chat);
511
dlg->queuedSendChatTo(chat, this);