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