1 /*
2 * tabdlg.cpp - dialog for handling tabbed chats
3 * Copyright (C) 2005 Kevin Smith
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #include "tabdlg.h"
22
23 #include "iconwidget.h"
24 #include "iconset.h"
25 #include "psicon.h"
26
27 #include <QMenuBar>
28 #include <QCursor>
29 #include <QVBoxLayout>
30 #include <QDragMoveEvent>
31 #include <QResizeEvent>
32 #include <QKeyEvent>
33 #include <QDropEvent>
34 #include <QCloseEvent>
35 #include <QSignalMapper>
36 #include <QTimer>
37 #include <QMimeData>
38
39 #include "psitabwidget.h"
40 #include "psioptions.h"
41 #include "shortcutmanager.h"
42 #include "chatdlg.h"
43 #include "tabmanager.h"
44
45 #ifdef Q_OS_WIN
46 #include <windows.h>
47 #endif
48
49 //----------------------------------------------------------------------------
50 // TabDlgDelegate
51 //----------------------------------------------------------------------------
52
TabDlgDelegate(QObject * parent)53 TabDlgDelegate::TabDlgDelegate(QObject *parent)
54 : QObject(parent)
55 {
56 }
57
~TabDlgDelegate()58 TabDlgDelegate::~TabDlgDelegate()
59 {
60 }
61
initWindowFlags() const62 Qt::WindowFlags TabDlgDelegate::initWindowFlags() const
63 {
64 return (Qt::WindowFlags)0;
65 }
66
create(QWidget *)67 void TabDlgDelegate::create(QWidget *)
68 {
69 }
70
destroy(QWidget *)71 void TabDlgDelegate::destroy(QWidget *)
72 {
73 }
74
tabWidgetCreated(QWidget *,PsiTabWidget *)75 void TabDlgDelegate::tabWidgetCreated(QWidget *, PsiTabWidget *)
76 {
77 }
78
paintEvent(QWidget *,QPaintEvent *)79 bool TabDlgDelegate::paintEvent(QWidget *, QPaintEvent *)
80 {
81 return false;
82 }
83
resizeEvent(QWidget *,QResizeEvent *)84 bool TabDlgDelegate::resizeEvent(QWidget *, QResizeEvent *)
85 {
86 return false;
87 }
88
mousePressEvent(QWidget *,QMouseEvent *)89 bool TabDlgDelegate::mousePressEvent(QWidget *, QMouseEvent *)
90 {
91 return false;
92 }
93
mouseMoveEvent(QWidget *,QMouseEvent *)94 bool TabDlgDelegate::mouseMoveEvent(QWidget *, QMouseEvent *)
95 {
96 return false;
97 }
98
mouseReleaseEvent(QWidget *,QMouseEvent *)99 bool TabDlgDelegate::mouseReleaseEvent(QWidget *, QMouseEvent *)
100 {
101 return false;
102 }
103
changeEvent(QWidget *,QEvent *)104 bool TabDlgDelegate::changeEvent(QWidget *, QEvent *)
105 {
106 return false;
107 }
108
tabEvent(QWidget *,QEvent *)109 bool TabDlgDelegate::tabEvent(QWidget *, QEvent *)
110 {
111 return false;
112 }
113
tabEventFilter(QWidget *,QObject *,QEvent *)114 bool TabDlgDelegate::tabEventFilter(QWidget *, QObject *, QEvent *)
115 {
116 return false;
117 }
118
119 //----------------------------------------------------------------------------
120 // TabDlg
121 //----------------------------------------------------------------------------
122
123 /**
124 * Constructs a TabDlg
125 *
126 * \param tabManager The tabManager that will manage this TabDlg
127 * \param delegate If non-zero, this is a pointer to a TabDlgDelegate that
128 * will manage some aspects of the TabDlg behavior. Ownership is not
129 * passed.
130 */
TabDlg(TabManager * tabManager,const QString & geometryOption,TabDlgDelegate * delegate)131 TabDlg::TabDlg(TabManager* tabManager, const QString& geometryOption, TabDlgDelegate *delegate)
132 : AdvancedWidget<QWidget>(0, delegate ? delegate->initWindowFlags() : (Qt::WindowFlags)0)
133 , delegate_(delegate)
134 , tabWidget_(0)
135 , detachButton_(0)
136 , closeButton_(0)
137 , closeCross_(0)
138 , tabMenu_(new QMenu(this))
139 , act_close_(0)
140 , act_next_(0)
141 , act_prev_(0)
142 , tabManager_(tabManager)
143 , userManagement_(true)
144 , tabBarSingles_(true)
145 , simplifiedCaption_(false)
146 , activateTabMapper_(0)
147 {
148 if (delegate_) {
149 delegate_->create(this);
150 }
151
152 // FIXME
153 qRegisterMetaType<TabDlg*>("TabDlg*");
154 qRegisterMetaType<TabbableWidget*>("TabbableWidget*");
155
156 tabWidget_ = new PsiTabWidget(this);
157 tabWidget_->setCloseIcon(IconsetFactory::icon("psi/closetab").icon());
158 connect(tabWidget_, SIGNAL(mouseDoubleClickTab(QWidget*)), SLOT(mouseDoubleClickTab(QWidget*)));
159 connect(tabWidget_, SIGNAL(mouseMiddleClickTab(QWidget*)), SLOT(mouseMiddleClickTab(QWidget*)));
160 connect(tabWidget_, SIGNAL(aboutToShowMenu(QMenu*)), SLOT(tab_aboutToShowMenu(QMenu*)));
161 connect(tabWidget_, SIGNAL(tabContextMenu(int, QPoint, QContextMenuEvent*)), SLOT(showTabMenu(int, QPoint, QContextMenuEvent*)));
162 connect(tabWidget_, SIGNAL(closeButtonClicked()), SLOT(closeCurrentTab()));
163 connect(tabWidget_, SIGNAL(currentChanged(QWidget*)), SLOT(tabSelected(QWidget*)));
164 connect(tabWidget_, SIGNAL(tabCloseRequested(int)), SLOT(tabCloseRequested(int)));
165
166 if(delegate_)
167 delegate_->tabWidgetCreated(this, tabWidget_);
168
169 QVBoxLayout *vert1 = new QVBoxLayout(this);
170 vert1->setMargin(1);
171 vert1->addWidget(tabWidget_);
172
173 setAcceptDrops(true);
174
175 X11WM_CLASS("tabs");
176 setLooks();
177
178 act_close_ = new QAction(this);
179 addAction(act_close_);
180 connect(act_close_,SIGNAL(triggered()), SLOT(closeCurrentTab()));
181 act_prev_ = new QAction(this);
182 addAction(act_prev_);
183 connect(act_prev_,SIGNAL(triggered()), SLOT(previousTab()));
184 act_next_ = new QAction(this);
185 addAction(act_next_);
186 connect(act_next_,SIGNAL(triggered()), SLOT(nextTab()));
187
188 setShortcuts();
189
190 if(!PsiOptions::instance()->getOption("options.ui.tabs.grouping").toString().contains('A'))
191 setGeometryOptionPath(geometryOption);
192 }
193
~TabDlg()194 TabDlg::~TabDlg()
195 {
196 // TODO: make sure that TabDlg is properly closed and its closeEvent() is invoked,
197 // so it could cancel an application quit
198 // Q_ASSERT(tabs_.isEmpty());
199
200 // ensure all tabs are closed at this moment
201 foreach(TabbableWidget* tab, tabs_) {
202 delete tab;
203 }
204
205 if (delegate_) {
206 delegate_->destroy(this);
207 }
208 }
209
210 #ifndef HAVE_QT5
211 // FIXME: This is a bad idea to store pointers in QMimeData
212 Q_DECLARE_METATYPE(TabDlg*);
213 Q_DECLARE_METATYPE(TabbableWidget*);
214 #endif
215
setShortcuts()216 void TabDlg::setShortcuts()
217 {
218 act_close_->setShortcuts(ShortcutManager::instance()->shortcuts("common.close"));
219 act_prev_->setShortcuts(ShortcutManager::instance()->shortcuts("chat.previous-tab"));
220 act_next_->setShortcuts(ShortcutManager::instance()->shortcuts("chat.next-tab"));
221
222 bool useTabShortcuts = PsiOptions::instance()->getOption("options.ui.tabs.use-tab-shortcuts").toBool();
223 if (useTabShortcuts && !activateTabMapper_) {
224 activateTabMapper_ = new QSignalMapper(this);
225 connect(activateTabMapper_, SIGNAL(mapped(int)), tabWidget_, SLOT(setCurrentPage(int)));
226 for (int i = 0; i < 10; ++i) {
227 QAction* action = new QAction(this);
228 connect(action, SIGNAL(triggered()), activateTabMapper_, SLOT(map()));
229 action->setShortcuts(QList<QKeySequence>() << QKeySequence(QString("Ctrl+%1").arg(i))
230 << QKeySequence(QString("Alt+%1").arg(i)));
231 activateTabMapper_->setMapping(action, (i > 0 ? i : 10) - 1);
232 tabMapperActions_ += action;
233 addAction(action);
234 }
235 }
236 else if (!useTabShortcuts && activateTabMapper_) {
237 qDeleteAll(tabMapperActions_);
238 tabMapperActions_.clear();
239 delete activateTabMapper_;
240 activateTabMapper_ = 0;
241 }
242 }
243
resizeEvent(QResizeEvent * e)244 void TabDlg::resizeEvent(QResizeEvent *e)
245 {
246 AdvancedWidget<QWidget>::resizeEvent(e);
247
248 emit resized(e->size());
249
250 // delegate may want to act on resize event
251 if (delegate_) {
252 delegate_->resizeEvent(this, e);
253 }
254 }
255
showTabMenu(int tab,QPoint pos,QContextMenuEvent * event)256 void TabDlg::showTabMenu(int tab, QPoint pos, QContextMenuEvent * event)
257 {
258 Q_UNUSED(event);
259 clearMenu(tabMenu_);
260
261 if (tab != -1) {
262 QAction *d = 0;
263 QAction *h = tabMenu_->addAction(tr("Hide Tab"));
264 if(userManagement_) {
265 d = tabMenu_->addAction(tr("Detach Tab"));
266 }
267
268 QAction *c = tabMenu_->addAction(tr("Close Tab"));
269
270 QMap<QAction*, TabDlg*> sentTos;
271 if(userManagement_) {
272 QMenu* sendTo = new QMenu(tabMenu_);
273 sendTo->setTitle(tr("Send Tab To"));
274 foreach(TabDlg* tabSet, tabManager_->tabSets()) {
275 QAction *act = sendTo->addAction(tabSet->desiredCaption());
276 if (tabSet == this)
277 act->setEnabled(false);
278 sentTos[act] = tabSet;
279 }
280 tabMenu_->addMenu(sendTo);
281 }
282
283 QAction *act = tabMenu_->exec(pos);
284 if (!act)
285 return;
286 if (act == c) {
287 closeTab(getTab(tab));
288 }
289 else if (act == d) {
290 detachTab(getTab(tab));
291 }
292 else if(act == h) {
293 hideTab(getTab(tab));
294 }
295 else {
296 TabDlg* target = sentTos[act];
297 if (target)
298 queuedSendTabTo(getTab(tab), target);
299 }
300 }
301 }
302
tab_aboutToShowMenu(QMenu * menu)303 void TabDlg::tab_aboutToShowMenu(QMenu *menu)
304 {
305 menu->addSeparator();
306 menu->addAction(tr("Hide Current Tab"), this, SLOT(hideCurrentTab()));
307 menu->addAction(tr("Hide All Tabs"), this, SLOT(hideAllTab()));
308 menu->addAction(tr("Detach Current Tab"), this, SLOT(detachCurrentTab()));
309 menu->addAction(tr("Close Current Tab"), this, SLOT(closeCurrentTab()));
310
311 QMenu* sendTo = new QMenu(menu);
312 sendTo->setTitle(tr("Send Current Tab To"));
313 int tabDlgMetaType = qRegisterMetaType<TabDlg*>("TabDlg*");
314 foreach(TabDlg* tabSet, tabManager_->tabSets()) {
315 QAction *act = sendTo->addAction(tabSet->desiredCaption());
316 act->setData(QVariant(tabDlgMetaType, &tabSet));
317 act->setEnabled(tabSet != this);
318 }
319 connect(sendTo, SIGNAL(triggered(QAction*)), SLOT(menu_sendTabTo(QAction*)));
320 menu->addMenu(sendTo);
321 menu->addSeparator();
322
323 QAction *act;
324 act = menu->addAction(tr("Use for New Chats"), this, SLOT(setAsDefaultForChat()));
325 act->setCheckable(true);
326 act->setChecked(tabManager_->preferredTabsForKind('C') == this);
327 act = menu->addAction(tr("Use for New Mucs"), this, SLOT(setAsDefaultForMuc()));
328 act->setCheckable(true);
329 act->setChecked(tabManager_->preferredTabsForKind('M') == this);
330 }
331
setAsDefaultForChat()332 void TabDlg::setAsDefaultForChat() {
333 tabManager_->setPreferredTabsForKind('C', this);
334 }
setAsDefaultForMuc()335 void TabDlg::setAsDefaultForMuc() {
336 tabManager_->setPreferredTabsForKind('M', this);
337 }
338
339
menu_sendTabTo(QAction * act)340 void TabDlg::menu_sendTabTo(QAction *act)
341 {
342 queuedSendTabTo(static_cast<TabbableWidget*>(tabWidget_->currentPage()), act->data().value<TabDlg*>());
343 }
344
sendTabTo(TabbableWidget * tab,TabDlg * otherTabs)345 void TabDlg::sendTabTo(TabbableWidget* tab, TabDlg* otherTabs)
346 {
347 Q_ASSERT(otherTabs);
348 if (otherTabs == this)
349 return;
350 closeTab(tab, false);
351 otherTabs->addTab(tab);
352 }
353
queuedSendTabTo(TabbableWidget * tab,TabDlg * dest)354 void TabDlg::queuedSendTabTo(TabbableWidget* tab, TabDlg *dest)
355 {
356 Q_ASSERT(tab);
357 Q_ASSERT(dest);
358 QMetaObject::invokeMethod(this, "sendTabTo", Qt::QueuedConnection, Q_ARG(TabbableWidget*, tab), Q_ARG(TabDlg*, dest));
359 }
360
optionsUpdate()361 void TabDlg::optionsUpdate()
362 {
363 setShortcuts();
364 }
365
setLooks()366 void TabDlg::setLooks()
367 {
368 //set the widget icon
369 #ifndef Q_OS_MAC
370 setWindowIcon(IconsetFactory::icon("psi/start-chat").icon());
371 #endif
372 tabWidget_->setTabPosition(QTabWidget::North);
373 if (PsiOptions::instance()->getOption("options.ui.tabs.put-tabs-at-bottom").toBool())
374 tabWidget_->setTabPosition(QTabWidget::South);
375
376 setWindowOpacity(double(qMax(MINIMUM_OPACITY,PsiOptions::instance()->getOption("options.ui.chat.opacity").toInt()))/100);
377
378 const QString css = PsiOptions::instance()->getOption("options.ui.chat.css").toString();
379 if (!css.isEmpty()) {
380 setStyleSheet(css);
381 }
382 }
383
tabSelected(QWidget * _selected)384 void TabDlg::tabSelected(QWidget* _selected)
385 {
386 // _selected could be null when TabDlg is closing and deleting all its tabs
387 TabbableWidget* selected = _selected ? qobject_cast<TabbableWidget*>(_selected) : 0;
388 if (!selectedTab_.isNull()) {
389 selectedTab_->deactivated();
390 }
391
392 selectedTab_ = selected;
393 if (selected) {
394 selected->activated();
395 }
396
397 updateCaption();
398 }
399
managesTab(const TabbableWidget * tab) const400 bool TabDlg::managesTab(const TabbableWidget* tab) const
401 {
402 return tabs_.contains(const_cast<TabbableWidget*>(tab));
403 }
404
tabOnTop(const TabbableWidget * tab) const405 bool TabDlg::tabOnTop(const TabbableWidget* tab) const
406 {
407 return tabWidget_->currentPage() == tab;
408 }
409
addTab(TabbableWidget * tab)410 void TabDlg::addTab(TabbableWidget* tab)
411 {
412 setUpdatesEnabled(false);
413 tabs_.append(tab);
414 tabWidget_->addTab(tab, captionForTab(tab), tab->icon());
415
416 connect(tab, SIGNAL(invalidateTabInfo()), SLOT(updateTab()));
417 connect(tab, SIGNAL(updateFlashState()), SLOT(updateFlashState()));
418
419 updateTab(tab);
420 setUpdatesEnabled(true);
421 QTimer::singleShot(0, this, SLOT(showTabWithoutActivation()));
422 }
423
showTabWithoutActivation()424 void TabDlg::showTabWithoutActivation()
425 {
426 showWithoutActivation();
427 }
428
hideCurrentTab()429 void TabDlg::hideCurrentTab()
430 {
431 hideTab(static_cast<TabbableWidget*>(tabWidget_->currentPage()));
432 }
433
hideTab(TabbableWidget * tab)434 void TabDlg::hideTab(TabbableWidget* tab)
435 {
436 closeTab(tab, false);
437 }
438
hideAllTab()439 void TabDlg::hideAllTab()
440 {
441 foreach(TabbableWidget* tab, tabs_)
442 hideTab(tab);
443 }
444
detachCurrentTab()445 void TabDlg::detachCurrentTab()
446 {
447 detachTab(static_cast<TabbableWidget*>(tabWidget_->currentPage()));
448 }
449
mouseDoubleClickTab(QWidget * widget)450 void TabDlg::mouseDoubleClickTab(QWidget* widget)
451 {
452 const QString act = PsiOptions::instance()->getOption("options.ui.tabs.mouse-doubleclick-action").toString();
453 if(act == "hide")
454 hideTab(static_cast<TabbableWidget*>(widget));
455 else if(act == "close")
456 closeTab(static_cast<TabbableWidget*>(widget));
457 else if(act == "detach" && userManagement_)
458 detachTab(static_cast<TabbableWidget*>(widget));
459 }
460
mouseMiddleClickTab(QWidget * widget)461 void TabDlg::mouseMiddleClickTab(QWidget* widget) {
462 const QString act = PsiOptions::instance()->getOption("options.ui.tabs.mouse-middle-button").toString();
463 if(act == "hide")
464 hideTab(static_cast<TabbableWidget*>(widget));
465 else if(act == "close")
466 closeTab(static_cast<TabbableWidget*>(widget));
467 else if(act == "detach" && userManagement_)
468 detachTab(static_cast<TabbableWidget*>(widget));
469 }
470
detachTab(TabbableWidget * tab)471 void TabDlg::detachTab(TabbableWidget* tab)
472 {
473 if (tabWidget_->count() == 1 || !tab)
474 return;
475
476 TabDlg *newTab = tabManager_->newTabs(tab);
477 sendTabTo(tab, newTab);
478 }
479
480 /**
481 * Call this when you want a tab to be removed immediately with no readiness checks
482 * or reparenting, hiding etc (Such as on tab destruction).
483 */
removeTabWithNoChecks(TabbableWidget * tab)484 void TabDlg::removeTabWithNoChecks(TabbableWidget *tab)
485 {
486 disconnect(tab, SIGNAL(invalidateTabInfo()), this, SLOT(updateTab()));
487 disconnect(tab, SIGNAL(updateFlashState()), this, SLOT(updateFlashState()));
488
489 tabs_.removeAll(tab);
490 tabWidget_->removePage(tab);
491 checkHasChats();
492 }
493
494 /**
495 * Removes the chat from the tabset, 'closing' it if specified.
496 * The method is used without closing tabs when transferring from one
497 * tabset to another.
498 * \param chat Chat to remove.
499 * \param doclose Whether the chat is 'closed' while removing it.
500 */
closeTab(TabbableWidget * chat,bool doclose)501 void TabDlg::closeTab(TabbableWidget* chat, bool doclose)
502 {
503 if (!chat || (doclose && !chat->readyToHide())) {
504 return;
505 }
506 setUpdatesEnabled(false);
507 chat->hide();
508 removeTabWithNoChecks(chat);
509 chat->setParent(0);
510 chat->deactivated();
511 if (tabWidget_->count() > 0) {
512 updateCaption();
513 }
514 //moved to NoChecks
515 //checkHasChats();
516 if (doclose && chat->testAttribute(Qt::WA_DeleteOnClose)) {
517 chat->close();
518 }
519 setUpdatesEnabled(true);
520 }
521
selectTab(TabbableWidget * chat)522 void TabDlg::selectTab(TabbableWidget* chat)
523 {
524 setUpdatesEnabled(false);
525 tabWidget_->showPage(chat);
526 setUpdatesEnabled(true);
527 }
528
checkHasChats()529 void TabDlg::checkHasChats()
530 {
531 if (tabWidget_->count() > 0 || this != window())
532 return;
533 if (tabs_.count() > 0) {
534 hide();
535 return;
536 }
537 deleteLater();
538 }
539
activated()540 void TabDlg::activated()
541 {
542 updateCaption();
543 extinguishFlashingTabs();
544 }
545
desiredCaption() const546 QString TabDlg::desiredCaption() const
547 {
548 QString cap = "";
549 uint pending = 0;
550 foreach(TabbableWidget* tab, tabs_) {
551 pending += tab->unreadMessageCount();
552 }
553 if (pending > 0) {
554 cap += "* ";
555 if (!simplifiedCaption_ && pending > 1) {
556 cap += QString("[%1] ").arg(pending);
557 }
558 }
559
560 if (tabWidget_->currentPage()) {
561 if (simplifiedCaption_ && tabs_.count() > 1) {
562 cap += tr("%1 Conversations").arg(tabs_.count());
563 } else {
564 cap += qobject_cast<TabbableWidget*>(tabWidget_->currentPage())->getDisplayName();
565 if (qobject_cast<TabbableWidget*>(tabWidget_->currentPage())->state() == TabbableWidget::StateComposing) {
566 cap += tr(" is composing");
567 }
568 }
569 }
570 return cap;
571 }
572
updateCaption()573 void TabDlg::updateCaption()
574 {
575 setWindowTitle(desiredCaption());
576
577 // FIXME: this probably shouldn't be in here, but it works easily
578 updateTabBar();
579 }
580
closeEvent(QCloseEvent * closeEvent)581 void TabDlg::closeEvent(QCloseEvent* closeEvent)
582 {
583 foreach(TabbableWidget* tab, tabs_) {
584 if (!tab->readyToHide()) {
585 closeEvent->ignore();
586 return;
587 }
588 }
589 if(PsiOptions::instance()->getOption("options.ui.chat.hide-when-closing").toBool()) {
590 hide();
591 }
592 else {
593 foreach(TabbableWidget* tab, tabs_) {
594 closeTab(tab);
595 }
596 }
597 }
598
getTab(int i) const599 TabbableWidget *TabDlg::getTab(int i) const
600 {
601 return static_cast<TabbableWidget*>(tabWidget_->page(i));
602 }
603
getTabPointer(PsiAccount * account,QString fullJid)604 TabbableWidget* TabDlg::getTabPointer(PsiAccount* account, QString fullJid)
605 {
606 foreach(TabbableWidget* tab, tabs_) {
607 if (tab->jid().full() == fullJid && tab->account() == account) {
608 return tab;
609 }
610 }
611
612 return 0;
613 }
614
updateTab()615 void TabDlg::updateTab()
616 {
617 TabbableWidget *tab = qobject_cast<TabbableWidget*>(sender());
618 updateTab(tab);
619 }
620
captionForTab(TabbableWidget * tab) const621 QString TabDlg::captionForTab(TabbableWidget* tab) const
622 {
623 QString label, prefix;
624 if (!tab->unreadMessageCount()) {
625 prefix = "";
626 }
627 else if (tab->unreadMessageCount() == 1) {
628 prefix = "* ";
629 }
630 else {
631 prefix = QString("[%1] ").arg(tab->unreadMessageCount());
632 }
633
634 label = prefix + tab->getDisplayName();
635 label.replace("&", "&&");
636 return label;
637 }
638
updateTab(TabbableWidget * chat)639 void TabDlg::updateTab(TabbableWidget* chat)
640 {
641 tabWidget_->setTabText(chat, captionForTab(chat));
642 //now set text colour based upon whether there are new messages/composing etc
643
644 if (chat->state() == TabbableWidget::StateComposing) {
645 tabWidget_->setTabTextColor(chat, Qt::darkGreen);
646 tabWidget_->setTabIcon(chat, IconsetFactory::iconPtr("psi/typing")->icon());
647 }
648 else if (chat->unreadMessageCount()) {
649 tabWidget_->setTabTextColor(chat, Qt::red);
650 tabWidget_->setTabIcon(chat, IconsetFactory::iconPtr("psi/chat")->icon());
651 }
652 else {
653 tabWidget_->setTabTextColor(chat, palette().windowText().color());
654 tabWidget_->setTabIcon(chat, chat->icon());
655 }
656 updateCaption();
657 }
658
nextTab()659 void TabDlg::nextTab()
660 {
661 int page = tabWidget_->currentPageIndex()+1;
662 if ( page >= tabWidget_->count() )
663 page = 0;
664 tabWidget_->setCurrentPage( page );
665 }
666
previousTab()667 void TabDlg::previousTab()
668 {
669 int page = tabWidget_->currentPageIndex()-1;
670 if ( page < 0 )
671 page = tabWidget_->count() - 1;
672 tabWidget_->setCurrentPage( page );
673 }
674
closeCurrentTab()675 void TabDlg::closeCurrentTab()
676 {
677 closeTab(static_cast<TabbableWidget*>(tabWidget_->currentPage()));
678 }
679
dragEnterEvent(QDragEnterEvent * event)680 void TabDlg::dragEnterEvent(QDragEnterEvent *event)
681 {
682 if (event->mimeData()->hasFormat(PSITABDRAGMIMETYPE)) {
683 event->setDropAction(Qt::MoveAction);
684 event->accept();
685 }
686 }
687
dropEvent(QDropEvent * event)688 void TabDlg::dropEvent(QDropEvent *event)
689 {
690 if (!event->mimeData()->hasFormat(PSITABDRAGMIMETYPE)) {
691 return;
692 }
693 QByteArray data = event->mimeData()->data(PSITABDRAGMIMETYPE);
694
695 int remoteTab = data.toInt();
696 event->acceptProposedAction();
697 //the event's been and gone, now do something about it
698 PsiTabBar* source = dynamic_cast<PsiTabBar*>(event->source());
699 if (source) {
700 PsiTabWidget* barParent = source->psiTabWidget();
701 if (remoteTab >= barParent->count()) return;
702 QWidget* widget = barParent->widget(remoteTab);
703 TabbableWidget* chat = dynamic_cast<TabbableWidget*>(widget);
704 TabDlg *dlg = tabManager_->getManagingTabs(chat);
705 if (!chat || !dlg)
706 return;
707 dlg->queuedSendTabTo(chat, this);
708 }
709 }
710
extinguishFlashingTabs()711 void TabDlg::extinguishFlashingTabs()
712 {
713 foreach(TabbableWidget* tab, tabs_) {
714 if (tab->flashing()) {
715 tab->blockSignals(true);
716 tab->doFlash(false);
717 tab->blockSignals(false);
718 }
719 }
720
721 updateFlashState();
722 }
723
updateFlashState()724 void TabDlg::updateFlashState()
725 {
726 bool flash = false;
727 foreach(TabbableWidget* tab, tabs_) {
728 if (tab->flashing()) {
729 flash = true;
730 break;
731 }
732 }
733
734 flash = flash && !isActiveWindow();
735 doFlash(flash);
736 }
737
paintEvent(QPaintEvent * event)738 void TabDlg::paintEvent(QPaintEvent *event)
739 {
740 // delegate if possible, otherwise use default
741 if (delegate_ && delegate_->paintEvent(this, event)) {
742 return;
743 } else {
744 AdvancedWidget<QWidget>::paintEvent(event);
745 }
746 }
747
mousePressEvent(QMouseEvent * event)748 void TabDlg::mousePressEvent(QMouseEvent *event)
749 {
750 // delegate if possible, otherwise use default
751 if (delegate_ && delegate_->mousePressEvent(this, event)) {
752 return;
753 } else {
754 AdvancedWidget<QWidget>::mousePressEvent(event);
755 }
756 }
757
mouseMoveEvent(QMouseEvent * event)758 void TabDlg::mouseMoveEvent(QMouseEvent *event)
759 {
760 // delegate if possible, otherwise use default
761 if (delegate_ && delegate_->mouseMoveEvent(this, event)) {
762 return;
763 } else {
764 AdvancedWidget<QWidget>::mouseMoveEvent(event);
765 }
766 }
767
mouseReleaseEvent(QMouseEvent * event)768 void TabDlg::mouseReleaseEvent(QMouseEvent *event)
769 {
770 // delegate if possible, otherwise use default
771 if (delegate_ && delegate_->mouseReleaseEvent(this, event)) {
772 return;
773 } else {
774 AdvancedWidget<QWidget>::mouseReleaseEvent(event);
775 }
776 }
777
changeEvent(QEvent * event)778 void TabDlg::changeEvent(QEvent *event)
779 {
780 if (event->type() == QEvent::ActivationChange ||
781 event->type() == QEvent::WindowStateChange)
782 {
783 if (tabWidget_->currentPage()) {
784 QCoreApplication::sendEvent(tabWidget_->currentPage(), event);
785 }
786
787 if (isActiveWindow()) {
788 activated();
789 }
790 }
791
792 // delegate if possible, otherwise use default
793 if (delegate_ && delegate_->changeEvent(this, event)) {
794 return;
795 }
796 else {
797 AdvancedWidget<QWidget>::changeEvent(event);
798 }
799 }
800
event(QEvent * event)801 bool TabDlg::event(QEvent *event)
802 {
803 // delegate if possible, otherwise use default
804 if (delegate_ && delegate_->tabEvent(this, event)) {
805 return true;
806 } else {
807 return AdvancedWidget<QWidget>::event(event);
808 }
809 }
810
eventFilter(QObject * obj,QEvent * event)811 bool TabDlg::eventFilter(QObject *obj, QEvent *event)
812 {
813 // delegate if possible, otherwise use default
814 if (delegate_ && delegate_->tabEventFilter(this, obj, event)) {
815 return true;
816 } else {
817 return AdvancedWidget<QWidget>::eventFilter(obj, event);
818 }
819 }
820
tabCount() const821 int TabDlg::tabCount() const
822 {
823 return tabs_.count();
824 }
825
setUserManagementEnabled(bool enabled)826 void TabDlg::setUserManagementEnabled(bool enabled)
827 {
828 if (userManagement_ == enabled) {
829 return;
830 }
831
832 userManagement_ = enabled;
833 tabWidget_->setTabButtonsShown(enabled);
834 tabWidget_->setDragsEnabled(enabled);
835 }
836
setTabBarShownForSingles(bool enabled)837 void TabDlg::setTabBarShownForSingles(bool enabled)
838 {
839 if (tabBarSingles_ == enabled) {
840 return;
841 }
842
843 tabBarSingles_ = enabled;
844 updateTabBar();
845 }
846
updateTabBar()847 void TabDlg::updateTabBar()
848 {
849 if (tabBarSingles_) {
850 tabWidget_->setTabBarShown(true);
851 } else {
852 if (tabWidget_->count() > 1)
853 tabWidget_->setTabBarShown(true);
854 else
855 tabWidget_->setTabBarShown(false);
856 }
857 }
858
setSimplifiedCaptionEnabled(bool enabled)859 void TabDlg::setSimplifiedCaptionEnabled(bool enabled)
860 {
861 if (simplifiedCaption_ == enabled) {
862 return;
863 }
864
865 simplifiedCaption_ = enabled;
866 updateCaption();
867 }
868
869 /**
870 * the slot is invoked, when small close button is clicked on a tab
871 * dont close tabs, that are not active.
872 * \param tab number requested to close
873 */
tabCloseRequested(int i)874 void TabDlg::tabCloseRequested(int i)
875 {
876 if (tabWidget_->currentPageIndex() != i)
877 if (!PsiOptions::instance()->getOption("options.ui.tabs.can-close-inactive-tab").toBool()) {
878 selectTab(static_cast<TabbableWidget*>(tabWidget_->page(i)));
879 return;
880 }
881
882 if (PsiOptions::instance()->getOption("options.ui.chat.hide-when-closing").toBool())
883 hideTab(static_cast<TabbableWidget*>(tabWidget_->page(i)));
884 else
885 closeTab(static_cast<TabbableWidget*>(tabWidget_->page(i)));
886 }
887
888 /**
889 * Set the icon of the tab.
890 */
setTabIcon(QWidget * widget,const QIcon & icon)891 void TabDlg::setTabIcon(QWidget *widget,const QIcon &icon)
892 {
893 tabWidget_->setTabIcon(widget, icon);
894 }
895
getCurrentTab() const896 TabbableWidget* TabDlg::getCurrentTab() const
897 {
898 return dynamic_cast<TabbableWidget*>(tabWidget_->currentPage());
899 }
900