X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%2Fsystemtray.cpp;h=61a21e7f00a2786715f3a7ae491bd775a5c0bbd7;hp=27aeb086c59bd44671c0204b045b1ad01d76fdcf;hb=HEAD;hpb=d452877910888c25d40590b5fff57eb8197ca9b0 diff --git a/src/qtui/systemtray.cpp b/src/qtui/systemtray.cpp index 27aeb086..61a21e7f 100644 --- a/src/qtui/systemtray.cpp +++ b/src/qtui/systemtray.cpp @@ -1,14 +1,11 @@ /*************************************************************************** - * Copyright (C) 2005-2010 by the Quassel Project * + * Copyright (C) 2005-2022 by the Quassel Project * * devel@quassel-irc.org * * * - * This contains code from KStatusNotifierItem, part of the KDE libs * - * Copyright (C) 2009 Marco Martin * - * * - * 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 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 * @@ -18,256 +15,265 @@ * 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. * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "systemtray.h" +#include +#include + +#include "action.h" #include "actioncollection.h" #include "client.h" -#include "iconloader.h" +#include "icon.h" #include "qtui.h" -#ifdef HAVE_KDE -# include -# include -#endif - -SystemTray::SystemTray(QWidget *parent) -: QObject(parent), - _mode(Invalid), - _state(Passive), - _inhibitActivation(false), - _passiveIcon(DesktopIcon("quassel_inactive")), - _activeIcon(DesktopIcon("quassel")), - _needsAttentionIcon(DesktopIcon("quassel_message")), - _trayMenu(0), - _associatedWidget(parent) +SystemTray::SystemTray(QWidget* parent) + : QObject(parent) + , _associatedWidget(parent) { - Q_ASSERT(parent); + Q_ASSERT(parent); -#ifdef Q_WS_WIN - _dwTickCount = 0; - associatedWidget()->installEventFilter(this); -#endif + NotificationSettings{}.initAndNotify("Systray/ChangeColor", this, &SystemTray::enableChangeColorChanged, true); + NotificationSettings{}.initAndNotify("Systray/Animate", this, &SystemTray::enableBlinkChanged, false); + UiStyleSettings{}.initAndNotify("Icons/InvertTray", this, &SystemTray::invertTrayIconChanged, false); - qApp->installEventFilter(this); -} + ActionCollection* coll = QtUi::actionCollection("General"); + _minimizeRestoreAction = new Action(tr("&Minimize"), this, this, &SystemTray::minimizeRestore); -SystemTray::~SystemTray() { -#ifdef Q_WS_WIN - associatedWidget()->removeEventFilter(this); -#endif + _trayMenu = new QMenu(associatedWidget()); + _trayMenu->setTitle("Quassel IRC"); + _trayMenu->setAttribute(Qt::WA_Hover); - _trayMenu->deleteLater(); -} + _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); -QWidget *SystemTray::associatedWidget() const { - return _associatedWidget; + _blinkTimer.setInterval(1000); + _blinkTimer.setSingleShot(false); + connect(&_blinkTimer, &QTimer::timeout, this, &SystemTray::onBlinkTimeout); } -void SystemTray::setTrayMenu(QMenu *menu) { - if(menu) - _trayMenu = menu; - else - _trayMenu = new QMenu(); - - ActionCollection *coll = QtUi::actionCollection("General"); - - _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 */ +SystemTray::~SystemTray() +{ + _trayMenu->deleteLater(); } -void SystemTray::setMode(Mode mode_) { - if(mode_ != _mode) { - _mode = mode_; - if(_mode == Legacy) { - _trayMenu->setWindowFlags(Qt::Popup); - } else { - _trayMenu->setWindowFlags(Qt::Window); - } - } +QWidget* SystemTray::associatedWidget() const +{ + return _associatedWidget; } -Icon SystemTray::stateIcon() const { - return stateIcon(state()); +bool SystemTray::isSystemTrayAvailable() const +{ + return false; } -Icon SystemTray::stateIcon(State state) const { - switch(state) { - case Passive: - return _passiveIcon; - case Active: - return _activeIcon; - case NeedsAttention: - return _needsAttentionIcon; - } - return Icon(); +bool SystemTray::isVisible() const +{ + return _isVisible; } -void SystemTray::setState(State state) { - if(_state != state) { - _state = state; - } +void SystemTray::setVisible(bool visible) +{ + if (visible != _isVisible) { + _isVisible = visible; + emit visibilityChanged(visible); + } } -void SystemTray::setAlert(bool alerted) { - if(alerted) - setState(NeedsAttention); - else - setState(Client::isConnected() ? Active : Passive); +SystemTray::Mode SystemTray::mode() const +{ + return _mode; } -void SystemTray::setVisible(bool visible) { - Q_UNUSED(visible) +void SystemTray::setMode(Mode mode) +{ + if (mode != _mode) { + _mode = mode; + emit modeChanged(mode); + } } -void SystemTray::setToolTip(const QString &title, const QString &subtitle) { - _toolTipTitle = title; - _toolTipSubTitle = subtitle; - emit toolTipChanged(title, subtitle); +SystemTray::State SystemTray::state() const +{ + return _state; } -void SystemTray::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint) { - Q_UNUSED(title) - Q_UNUSED(message) - Q_UNUSED(icon) - Q_UNUSED(millisecondsTimeoutHint) +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(); + } } -void SystemTray::activate(SystemTray::ActivationReason reason) { +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; + } - emit activated(reason); + if (_trayIconInverted) { + name += "-inverted"; + } - if(reason == Trigger && !isActivationInhibited()) { - toggleMainWidget(); - } + return name; } -bool SystemTray::eventFilter(QObject *obj, QEvent *event) { - if(event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonRelease) { - _inhibitActivation = false; - } -#ifdef Q_WS_WIN - if(obj == associatedWidget() && event->type() == QEvent::ActivationChange) { - _dwTickCount = GetTickCount(); - } -#endif - return QObject::eventFilter(obj, event); +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()); + } } -// Code taken from KStatusNotifierItem for handling minimize/restore - -bool SystemTray::checkVisibility(bool perform) { -#ifdef Q_WS_WIN - // the problem is that we lose focus when the systray icon is activated - // and we don't know the former active window - // therefore we watch for activation event and use our stopwatch :) - if(GetTickCount() - _dwTickCount < 300) { - // we were active in the last 300ms -> hide it - minimizeRestore(false); - } else { - minimizeRestore(true); - } - -#elif defined(HAVE_KDE) && defined(Q_WS_X11) - KWindowInfo info1 = KWindowSystem::windowInfo(associatedWidget()->winId(), NET::XAWMState | NET::WMState | NET::WMDesktop); - // mapped = visible (but possibly obscured) - bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized(); - - // - not mapped -> show, raise, focus - // - mapped - // - obscured -> raise, focus - // - not obscured -> hide - //info1.mappingState() != NET::Visible -> window on another desktop? - if(!mapped) { - if(perform) - minimizeRestore(true); - return true; - - } else { - QListIterator< WId > it (KWindowSystem::stackingOrder()); - it.toBack(); - while(it.hasPrevious()) { - WId id = it.previous(); - if(id == associatedWidget()->winId()) - break; - - KWindowInfo info2 = KWindowSystem::windowInfo(id, NET::WMDesktop | NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType); +QString SystemTray::currentAttentionIconName() const +{ + if (state() == State::NeedsAttention && _attentionBehavior == AttentionBehavior::Blink && !_blinkState) { + return iconName(State::Active); + } + return iconName(State::NeedsAttention); +} - if(info2.mappingState() != NET::Visible) - continue; // not visible on current desktop -> ignore +bool SystemTray::isAlerted() const +{ + return state() == State::NeedsAttention; +} - if(!info2.geometry().intersects(associatedWidget()->geometry())) - continue; // not obscuring the window -> ignore +void SystemTray::setAlert(bool alerted) +{ + if (alerted) { + setState(NeedsAttention); + } + else { + setState(Client::isConnected() ? Active : Passive); + } +} - if(!info1.hasState(NET::KeepAbove) && info2.hasState(NET::KeepAbove)) - continue; // obscured by window kept above -> ignore +void SystemTray::onBlinkTimeout() +{ + _blinkState = !_blinkState; + emit currentIconNameChanged(); +} - NET::WindowType type = info2.windowType(NET::NormalMask | NET::DesktopMask - | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask - | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask); +QMenu* SystemTray::trayMenu() const +{ + return _trayMenu; +} - if(type == NET::Dock || type == NET::TopMenu) - continue; // obscured by dock or topmenu -> ignore +void SystemTray::trayMenuAboutToShow() +{ + if (GraphicalUi::isMainWidgetVisible()) + _minimizeRestoreAction->setText(tr("&Minimize")); + else + _minimizeRestoreAction->setText(tr("&Restore")); +} - if(perform) { - KWindowSystem::raiseWindow(associatedWidget()->winId()); - KWindowSystem::activateWindow(associatedWidget()->winId()); - } - return true; +void SystemTray::enableChangeColorChanged(const QVariant& v) +{ + if (v.toBool()) { + _attentionBehavior = AttentionBehavior::ChangeColor; + } + else { + if (_attentionBehavior == AttentionBehavior::ChangeColor) { + _attentionBehavior = AttentionBehavior::DoNothing; + } } + emit currentIconNameChanged(); +} - //not on current desktop? - if(!info1.isOnCurrentDesktop()) { - if(perform) - KWindowSystem::activateWindow(associatedWidget()->winId()); - return true; +void SystemTray::enableBlinkChanged(const QVariant& v) +{ + if (v.toBool()) { + _attentionBehavior = AttentionBehavior::Blink; } + else { + if (_attentionBehavior == AttentionBehavior::Blink) { + _attentionBehavior = AttentionBehavior::DoNothing; + } + } + emit currentIconNameChanged(); +} - if(perform) - minimizeRestore(false); // hide - return false; - } -#else - - if(!associatedWidget()->isVisible() || associatedWidget()->isMinimized()) { - if(perform) - minimizeRestore(true); - return true; - } else { - if(perform) - minimizeRestore(false); - return false; - } +void SystemTray::invertTrayIconChanged(const QVariant& v) +{ + _trayIconInverted = v.toBool(); + emit iconsChanged(); +} -#endif +QString SystemTray::toolTipTitle() const +{ + return _toolTipTitle; +} - return true; +QString SystemTray::toolTipSubTitle() const +{ + return _toolTipSubTitle; } -void SystemTray::minimizeRestore(bool show) { - if(show) - GraphicalUi::activateMainWidget(); - else { - if(isSystemTrayAvailable()) { - if(!isVisible()) - setVisible(); - GraphicalUi::hideMainWidget(); - } - } +void SystemTray::setToolTip(const QString& title, const QString& subtitle) +{ + _toolTipTitle = title; + _toolTipSubTitle = subtitle; + emit toolTipChanged(title, subtitle); } -void SystemTray::hideMainWidget() { - minimizeRestore(false); +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::toggleMainWidget() { - checkVisibility(true); +void SystemTray::activate(SystemTray::ActivationReason reason) +{ + emit activated(reason); +} + +void SystemTray::minimizeRestore() +{ + GraphicalUi::toggleMainWidget(); }