X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%2Fsystemtray.cpp;h=c85e7bf22df7c4f1b8ed3a7e47da1b24ac441497;hp=8c34655044f9fb3eb676995f9b4949b657c15004;hb=f9efdde7f3a6004af8f834c409cfa6ae1d877692;hpb=d76bb125c8dd275095409edd3426700a98d89f3a diff --git a/src/qtui/systemtray.cpp b/src/qtui/systemtray.cpp index 8c346550..c85e7bf2 100644 --- a/src/qtui/systemtray.cpp +++ b/src/qtui/systemtray.cpp @@ -1,199 +1,306 @@ /*************************************************************************** -* Copyright (C) 2005-09 by the Quassel Project * -* devel@quassel-irc.org * -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) version 3. * -* * -* This program is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU General Public License for more details. * -* * -* You should have received a copy of the GNU General Public License * -* along with this program; if not, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ - -#ifndef QT_NO_SYSTEMTRAYICON - -#include + * Copyright (C) 2005-2018 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This file is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License (LGPL) * + * as published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ #include "systemtray.h" +#include +#include + +#include "action.h" #include "actioncollection.h" -#include "iconloader.h" +#include "client.h" +#include "icon.h" #include "qtui.h" -#include "qtuisettings.h" - -SystemTray::SystemTray(QObject *parent) -: QObject(parent), - _state(Inactive), - _alert(false), - _inhibitActivation(false), - _currentIdx(0) -{ - loadAnimations(); - _currentIdx = _idxOffEnd; - -#ifndef HAVE_KDE - _trayIcon = new QSystemTrayIcon(_phases.at(_currentIdx), QtUi::mainWindow()); -#else - _trayIcon = new KSystemTrayIcon(_phases.at(_currentIdx), QtUi::mainWindow()); - // We don't want to trigger a minimize if a highlight is pending, so we brutally remove the internal connection for that - disconnect(_trayIcon, SIGNAL(activated( QSystemTrayIcon::ActivationReason)), - _trayIcon, SLOT(activateOrHide(QSystemTrayIcon::ActivationReason))); -#endif - - _animationTimer.setInterval(150); - _animationTimer.setSingleShot(false); - connect(&_animationTimer, SIGNAL(timeout()), SLOT(nextPhase())); - - ActionCollection *coll = QtUi::actionCollection("General"); - _trayMenu = _trayIcon->contextMenu(); - if (!_trayMenu) - _trayMenu = new QMenu(); - _trayMenu->addAction(coll->action("ConnectCore")); - _trayMenu->addAction(coll->action("DisconnectCore")); - _trayMenu->addAction(coll->action("CoreInfo")); -#ifndef HAVE_KDE - _trayMenu->addSeparator(); - _trayMenu->addAction(coll->action("Quit")); -#endif /* HAVE_KDE */ - - _trayIcon->setContextMenu(_trayMenu); - - QtUiSettings s; - if(s.value("UseSystemTrayIcon", QVariant(true)).toBool()) { - _trayIcon->show(); - } - - qApp->installEventFilter(this); - -#ifndef Q_WS_MAC - connect(_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), SLOT(on_activated(QSystemTrayIcon::ActivationReason))); -#endif - connect(_trayIcon, SIGNAL(messageClicked()), SIGNAL(messageClicked())); -} - -SystemTray::~SystemTray() { - _trayMenu->deleteLater(); -} - -void SystemTray::loadAnimations() { -// system tray icon size -#ifdef Q_WS_WIN - const int size = 16; -#elif defined Q_WS_MAC - const int size = 128; -#else - const int size = 22; -#endif - - _phases.clear(); - -#ifdef HAVE_KDE - KIconLoader *loader = KIconLoader::global(); -#else - IconLoader *loader = IconLoader::global(); -#endif - - _idxOffStart = 0; - QString fadeOffName("quassel_tray-fade-off-%1"); - for(int i = 2; i <= 10; i++) - _phases.append(loader->loadIcon(fadeOffName.arg(i), IconLoader::Panel, size)); - _idxOffEnd = _idxOnStart = _phases.count() - 1; - - QString fadeOnName("quassel_tray-fade-on-%1"); - for(int i = 2; i <= 15; i++) - _phases.append(loader->loadIcon(fadeOnName.arg(i), IconLoader::Panel, size)); - _idxOnEnd = _idxAlertStart = _phases.count() - 1; - - QString alertName("quassel_tray-alert-%1"); - for(int i = 1; i <= 10; i++) - _phases.append(loader->loadIcon(alertName.arg(i), IconLoader::Panel, size)); -} - -void SystemTray::nextPhase() { - if(_currentIdx == _idxOnEnd && !_alert && _state == Inactive) - _currentIdx = _idxOffStart; // skip alert phases - - else if(++_currentIdx >= _phases.count()) { - if(_alert) - _currentIdx = _idxAlertStart; - else - if(_state == Active) - _currentIdx = _idxOnEnd; - else - _currentIdx = _idxOffStart; - } - _trayIcon->setIcon(_phases.at(_currentIdx)); +SystemTray::SystemTray(QWidget *parent) + : QObject(parent), + _associatedWidget(parent) +{ + Q_ASSERT(parent); + + NotificationSettings{}.initAndNotify("Systray/ChangeColor", this, SLOT(enableChangeColorChanged(QVariant)), true); + NotificationSettings{}.initAndNotify("Systray/Animate", this, SLOT(enableBlinkChanged(QVariant)), false); + UiStyleSettings{}.initAndNotify("Icons/InvertTray", this, SLOT(invertTrayIconChanged(QVariant)), false); - if(_alert) - return; + ActionCollection *coll = QtUi::actionCollection("General"); + _minimizeRestoreAction = new Action(tr("&Minimize"), this, this, &SystemTray::minimizeRestore); - if((_state == Active && _currentIdx == _idxOnEnd) || (_state == Inactive && _currentIdx == _idxOffEnd)) - _animationTimer.stop(); + _trayMenu = new QMenu(associatedWidget()); + _trayMenu->setTitle("Quassel IRC"); + _trayMenu->setAttribute(Qt::WA_Hover); + + _trayMenu->addAction(coll->action("ConnectCore")); + _trayMenu->addAction(coll->action("DisconnectCore")); + _trayMenu->addAction(coll->action("CoreInfo")); + _trayMenu->addSeparator(); + _trayMenu->addAction(_minimizeRestoreAction); + _trayMenu->addAction(coll->action("Quit")); + connect(_trayMenu, &QMenu::aboutToShow, this, &SystemTray::trayMenuAboutToShow); + + connect(QtUi::instance(), &QtUi::iconThemeRefreshed, this, &SystemTray::iconsChanged); + + _blinkTimer.setInterval(1000); + _blinkTimer.setSingleShot(false); + connect(&_blinkTimer, &QTimer::timeout, this, &SystemTray::onBlinkTimeout); } -void SystemTray::setState(State state) { - if(_state != state) { - _state = state; - if(state == Inactive && _alert) - _alert = false; - if(!_animationTimer.isActive()) - _animationTimer.start(); - } + +SystemTray::~SystemTray() +{ + _trayMenu->deleteLater(); } -void SystemTray::setAlert(bool alert) { - if(_alert != alert) { - _alert = alert; - if(!_animationTimer.isActive()) - _animationTimer.start(); - } + +QWidget *SystemTray::associatedWidget() const +{ + return _associatedWidget; } -void SystemTray::setIconVisible(bool visible) { - if(visible) - _trayIcon->show(); - else - _trayIcon->hide(); + +bool SystemTray::isSystemTrayAvailable() const +{ + return false; } -void SystemTray::setToolTip(const QString &tip) { - _trayIcon->setToolTip(tip); + +bool SystemTray::isVisible() const +{ + return _isVisible; } -void SystemTray::showMessage(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon icon, int millisecondsTimeoutHint) { - _trayIcon->showMessage(title, message, icon, millisecondsTimeoutHint); + +void SystemTray::setVisible(bool visible) +{ + if (visible != _isVisible) { + _isVisible = visible; + emit visibilityChanged(visible); + } } -bool SystemTray::eventFilter(QObject *obj, QEvent *event) { - Q_UNUSED(obj); - if(event->type() == QEvent::MouseButtonRelease) { - _inhibitActivation = false; - } - return false; + +SystemTray::Mode SystemTray::mode() const +{ + return _mode; } -void SystemTray::on_activated(QSystemTrayIcon::ActivationReason reason) { - emit activated(reason); - if(reason == QSystemTrayIcon::Trigger && !_inhibitActivation) { +void SystemTray::setMode(Mode mode) +{ + if (mode != _mode) { + _mode = mode; + emit modeChanged(mode); + } +} -# ifdef HAVE_KDE - // the slot is private, but meh, who cares :) - QMetaObject::invokeMethod(_trayIcon, "activateOrHide", Q_ARG(QSystemTrayIcon::ActivationReason, QSystemTrayIcon::Trigger)); -# else - QtUi::mainWindow()->toggleMinimizedToTray(); -# endif - } +SystemTray::State SystemTray::state() const +{ + return _state; } -#endif /* QT_NO_SYSTEMTRAYICON */ + +void SystemTray::setState(State state) +{ + if (_state != state) { + _state = state; + emit stateChanged(state); + + if (state == NeedsAttention && _attentionBehavior == AttentionBehavior::Blink) { + _blinkTimer.start(); + _blinkState = true; + } + else { + _blinkTimer.stop(); + _blinkState = false; + } + emit currentIconNameChanged(); + } +} + + +QString SystemTray::iconName(State state) const +{ + QString name; + switch (state) { + case State::Passive: + name = "inactive-quassel-tray"; + break; + case State::Active: + name = "active-quassel-tray"; + break; + case State::NeedsAttention: + name = "message-quassel-tray"; + break; + } + + if (_trayIconInverted) { + name += "-inverted"; + } + + return name; +} + + +QString SystemTray::currentIconName() const +{ + if (state() == State::NeedsAttention) { + if (_attentionBehavior == AttentionBehavior::ChangeColor) { + return iconName(State::NeedsAttention); + } + if (_attentionBehavior == AttentionBehavior::Blink && _blinkState) { + return iconName(State::NeedsAttention); + } + return iconName(State::Active); + } + else { + return iconName(state()); + } +} + + +QString SystemTray::currentAttentionIconName() const +{ + if (state() == State::NeedsAttention && _attentionBehavior == AttentionBehavior::Blink && !_blinkState) { + return iconName(State::Active); + } + return iconName(State::NeedsAttention); +} + + +bool SystemTray::isAlerted() const +{ + return state() == State::NeedsAttention; +} + + +void SystemTray::setAlert(bool alerted) +{ + if (alerted) { + setState(NeedsAttention); + } + else { + setState(Client::isConnected() ? Active : Passive); + } +} + + +void SystemTray::onBlinkTimeout() +{ + _blinkState = !_blinkState; + emit currentIconNameChanged(); +} + + +QMenu *SystemTray::trayMenu() const +{ + return _trayMenu; +} + + +void SystemTray::trayMenuAboutToShow() +{ + if (GraphicalUi::isMainWidgetVisible()) + _minimizeRestoreAction->setText(tr("&Minimize")); + else + _minimizeRestoreAction->setText(tr("&Restore")); +} + + +void SystemTray::enableChangeColorChanged(const QVariant &v) +{ + if (v.toBool()) { + _attentionBehavior = AttentionBehavior::ChangeColor; + } + else { + if (_attentionBehavior == AttentionBehavior::ChangeColor) { + _attentionBehavior = AttentionBehavior::DoNothing; + } + } + emit currentIconNameChanged(); +} + + +void SystemTray::enableBlinkChanged(const QVariant &v) +{ + if (v.toBool()) { + _attentionBehavior = AttentionBehavior::Blink; + } + else { + if (_attentionBehavior == AttentionBehavior::Blink) { + _attentionBehavior = AttentionBehavior::DoNothing; + } + } + emit currentIconNameChanged(); +} + + +void SystemTray::invertTrayIconChanged(const QVariant &v) +{ + _trayIconInverted = v.toBool(); + emit iconsChanged(); +} + + +QString SystemTray::toolTipTitle() const +{ + return _toolTipTitle; +} + + +QString SystemTray::toolTipSubTitle() const +{ + return _toolTipSubTitle; +} + + +void SystemTray::setToolTip(const QString &title, const QString &subtitle) +{ + _toolTipTitle = title; + _toolTipSubTitle = subtitle; + emit toolTipChanged(title, subtitle); +} + + +void SystemTray::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint, uint id) +{ + Q_UNUSED(title) + Q_UNUSED(message) + Q_UNUSED(icon) + Q_UNUSED(millisecondsTimeoutHint) + Q_UNUSED(id) +} + + +void SystemTray::closeMessage(uint notificationId) +{ + Q_UNUSED(notificationId) +} + + +void SystemTray::activate(SystemTray::ActivationReason reason) +{ + emit activated(reason); +} + + +void SystemTray::minimizeRestore() +{ + GraphicalUi::toggleMainWidget(); +}