/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtChatTabs.h" #include #include #include #include #include #include #include #include #include namespace Swift { QtChatTabs::QtChatTabs() : QWidget() { #ifndef Q_WS_MAC setWindowIcon(QIcon(":/logo-chat-16.png")); #endif tabs_ = new QtTabWidget(this); tabs_->setUsesScrollButtons(true); tabs_->setElideMode(Qt::ElideRight); #if QT_VERSION >= 0x040500 /*For Macs, change the tab rendering.*/ tabs_->setDocumentMode(true); /*Closable tabs are only in Qt4.5 and later*/ tabs_->setTabsClosable(true); connect(tabs_, SIGNAL(tabCloseRequested(int)), this, SLOT(handleTabCloseRequested(int))); #else #warning Qt 4.5 or later is needed. Trying anyway, some things will be disabled. #endif QVBoxLayout *layout = new QVBoxLayout; layout->setSpacing(0); layout->setContentsMargins(0, 3, 0, 0); layout->addWidget(tabs_); setLayout(layout); //resize(400, 300); } void QtChatTabs::closeEvent(QCloseEvent* event) { //Hide first to prevent flickering as each tab is removed. hide(); for (int i = tabs_->count() - 1; i >= 0; i--) { tabs_->widget(i)->close(); } event->accept(); } QtTabbable* QtChatTabs::getCurrentTab() { return qobject_cast(tabs_->currentWidget()); } void QtChatTabs::addTab(QtTabbable* tab) { QSizePolicy policy = sizePolicy(); /* Chat windows like to grow - don't let them */ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); tabs_->addTab(tab, tab->windowTitle()); connect(tab, SIGNAL(titleUpdated()), this, SLOT(handleTabTitleUpdated()), Qt::UniqueConnection); connect(tab, SIGNAL(countUpdated()), this, SLOT(handleTabTitleUpdated()), Qt::UniqueConnection); connect(tab, SIGNAL(windowClosing()), this, SLOT(handleTabClosing()), Qt::UniqueConnection); connect(tab, SIGNAL(windowOpening()), this, SLOT(handleWidgetShown()), Qt::UniqueConnection); connect(tab, SIGNAL(wantsToActivate()), this, SLOT(handleWantsToActivate()), Qt::UniqueConnection); connect(tab, SIGNAL(requestNextTab()), this, SLOT(handleRequestedNextTab()), Qt::UniqueConnection); connect(tab, SIGNAL(requestActiveTab()), this, SLOT(handleRequestedActiveTab()), Qt::UniqueConnection); connect(tab, SIGNAL(requestPreviousTab()), this, SLOT(handleRequestedPreviousTab()), Qt::UniqueConnection); connect(tab, SIGNAL(requestFlash()), this, SLOT(flash()), Qt::UniqueConnection); setSizePolicy(policy); } void QtChatTabs::handleWidgetShown() { QtTabbable* widget = qobject_cast(sender()); if (!widget) { return; } checkForFirstShow(); if (tabs_->indexOf(widget) >= 0) { return; } addTab(widget); show(); } void QtChatTabs::handleWantsToActivate() { QtTabbable* widget = qobject_cast(sender()); Q_ASSERT(widget); //Un-minimize and bring to front. setWindowState(windowState() & ~Qt::WindowMinimized); setWindowState(windowState() | Qt::WindowActive); show(); widget->show(); tabs_->setCurrentWidget(widget); widget->setFocus(); raise(); activateWindow(); } void QtChatTabs::handleTabClosing() { QWidget* widget = qobject_cast(sender()); if (!widget) { return; } int index = tabs_->indexOf(widget); if (index < 0) { return; } tabs_->removeTab(index); if (tabs_->count() == 0) { hide(); } handleTabTitleUpdated(tabs_->currentWidget()); } void QtChatTabs::handleRequestedPreviousTab() { int newIndex = tabs_->currentIndex() - 1; tabs_->setCurrentIndex(newIndex >= 0 ? newIndex : tabs_->count() - 1); } void QtChatTabs::handleRequestedNextTab() { int newIndex = tabs_->currentIndex() + 1; tabs_->setCurrentIndex(newIndex < tabs_->count() ? newIndex : 0); } void QtChatTabs::handleRequestedActiveTab() { QtTabbable::AlertType types[] = {QtTabbable::WaitingActivity, QtTabbable::ImpendingActivity}; bool finished = false; for (int j = 0; j < 2; j++) { bool looped = false; for (int i = tabs_->currentIndex() + 1; !finished && i != tabs_->currentIndex(); i++) { if (i >= tabs_->count()) { if (looped) { break; } looped = true; i = 0; } if (qobject_cast(tabs_->widget(i))->getWidgetAlertState() == types[j]) { tabs_->setCurrentIndex(i); finished = true; break; } } } } void QtChatTabs::handleTabCloseRequested(int index) { QWidget* widget = tabs_->widget(index); widget->close(); } void QtChatTabs::handleTabTitleUpdated() { QWidget* widget = qobject_cast(sender()); handleTabTitleUpdated(widget); } void QtChatTabs::handleTabTitleUpdated(QWidget* widget) { if (!widget) { return; } QtTabbable* tabbable = qobject_cast(widget); int index = tabs_->indexOf(widget); if (index < 0) { return; } QString tabText = tabbable->windowTitle().simplified(); // look for spectrum-generated and other long JID localparts, and // try to abbreviate the resulting long tab texts QRegExp hasTrailingGarbage( "^(.[-\\w\\s&]+)([^\\s\\w].*)$" ); if (hasTrailingGarbage.exactMatch(tabText) && hasTrailingGarbage.cap(1).simplified().length() >= 2 && hasTrailingGarbage.cap(2).length() >= 7) { // there may be some trailing garbage, and it's long // enough to be worth removing, and we'd leave at // least a couple of characters. tabText = hasTrailingGarbage.cap(1).simplified(); } // QTabBar interprets &, so escape that tabText.replace( "&", "&&" ); // see which alt[a-z] keys other tabs use bool accelsTaken[26]; int i = 0; while (i < 26) accelsTaken[i++] = false; int other = tabs_->tabBar()->count(); while (other >= 0) { other--; if ( other != index ) { QString t = tabs_->tabBar()->tabText(other).toLower(); int r = t.indexOf( '&' ); if ( r >= 0 && t[r+1] >= 'a' && t[r+1] <= 'z' ) accelsTaken[t[r+1].unicode()-'a'] = true; } } // then look to see which letters in tabText may be used i = 0; int accelPos = -1; while (i < tabText.length() && accelPos < 0) { if ( tabText[i] >= 'A' && tabText[i] <= 'Z' && !accelsTaken[tabText[i].unicode()-'A'] ) accelPos = i; if ( tabText[i] >= 'a' && tabText[i] <= 'z' && !accelsTaken[tabText[i].unicode()-'a'] ) accelPos = i; ++i; } if (accelPos >= 0) tabText = tabText.mid(0, accelPos) + "&" + tabText.mid(accelPos); // this could be improved on some european keyboards, such as // the German one (where alt-ß is available) and basically // doesn't work on Arabic/Indic keyboards (where Latin letters // aren't available), but I don't care to deal with those. tabs_->setTabText(index, tabbable->getCount() > 0 ? QString("(%1) %2").arg(tabbable->getCount()).arg(tabText) : tabText); QColor tabTextColor; switch (tabbable->getWidgetAlertState()) { case QtTabbable::WaitingActivity : tabTextColor = QColor(217, 20, 43); break; case QtTabbable::ImpendingActivity : tabTextColor = QColor(27, 171, 32); break; default : tabTextColor = QColor(); } tabs_->tabBar()->setTabTextColor(index, tabTextColor); int unread = 0; for (int i = 0; i < tabs_->count(); i++) { QtTabbable* tab = qobject_cast(tabs_->widget(i)); if (tab) { unread += tab->getCount(); } } QtTabbable* current = qobject_cast(tabs_->currentWidget()); setWindowTitle(unread > 0 ? QString("(%1) %2").arg(unread).arg(current->windowTitle()) : current->windowTitle()); } void QtChatTabs::flash() { #ifndef SWIFTEN_PLATFORM_MACOSX QApplication::alert(this, 3000); #endif } void QtChatTabs::resizeEvent(QResizeEvent*) { emit geometryChanged(); } void QtChatTabs::moveEvent(QMoveEvent*) { emit geometryChanged(); } void QtChatTabs::checkForFirstShow() { if (!isVisible()) { showMinimized(); } } }