qtui: Rework the attention state behavior of the tray icon
[quassel.git] / src / qtui / systemtray.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This file is free software; you can redistribute it and/or modify     *
6  *   it under the terms of the GNU Library General Public License (LGPL)   *
7  *   as published by the Free Software Foundation; either version 2 of the *
8  *   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 program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include <QApplication>
22 #include <QMenu>
23
24 #include "systemtray.h"
25
26 #include "action.h"
27 #include "actioncollection.h"
28 #include "client.h"
29 #include "qtui.h"
30
31 #ifdef HAVE_KDE4
32 #  include <KMenu>
33 #  include <KWindowInfo>
34 #  include <KWindowSystem>
35 #endif
36
37 SystemTray::SystemTray(QWidget *parent)
38     : QObject(parent),
39     _associatedWidget(parent)
40 {
41     Q_ASSERT(parent);
42
43     NotificationSettings{}.initAndNotify("Systray/ChangeColor", this, SLOT(enableChangeColorChanged(QVariant)), true);
44     NotificationSettings{}.initAndNotify("Systray/Animate", this, SLOT(enableBlinkChanged(QVariant)), false);
45     UiStyleSettings{}.initAndNotify("Icons/InvertTray", this, SLOT(invertTrayIconChanged(QVariant)), false);
46
47     ActionCollection *coll = QtUi::actionCollection("General");
48     _minimizeRestoreAction = new Action(tr("&Minimize"), this, this, SLOT(minimizeRestore()));
49
50 #ifdef HAVE_KDE4
51     KMenu *kmenu;
52     _trayMenu = kmenu = new KMenu();
53     kmenu->addTitle(_activeIcon, "Quassel IRC");
54 #else
55     _trayMenu = new QMenu(associatedWidget());
56 #endif
57
58     _trayMenu->setTitle("Quassel IRC");
59
60 #ifndef HAVE_KDE4
61     _trayMenu->setAttribute(Qt::WA_Hover);
62 #endif
63
64     _trayMenu->addAction(coll->action("ConnectCore"));
65     _trayMenu->addAction(coll->action("DisconnectCore"));
66     _trayMenu->addAction(coll->action("CoreInfo"));
67     _trayMenu->addSeparator();
68     _trayMenu->addAction(_minimizeRestoreAction);
69     _trayMenu->addAction(coll->action("Quit"));
70     connect(_trayMenu, SIGNAL(aboutToShow()), SLOT(trayMenuAboutToShow()));
71
72     connect(QtUi::instance(), SIGNAL(iconThemeRefreshed()), this, SIGNAL(iconsChanged()));
73
74     _blinkTimer.setInterval(1000);
75     _blinkTimer.setSingleShot(false);
76     connect(&_blinkTimer, SIGNAL(timeout()), SLOT(onBlinkTimeout()));
77 }
78
79
80 SystemTray::~SystemTray()
81 {
82     _trayMenu->deleteLater();
83 }
84
85
86 QWidget *SystemTray::associatedWidget() const
87 {
88     return _associatedWidget;
89 }
90
91
92 bool SystemTray::isSystemTrayAvailable() const
93 {
94     return false;
95 }
96
97
98 bool SystemTray::isVisible() const
99 {
100     return _isVisible;
101 }
102
103
104 void SystemTray::setVisible(bool visible)
105 {
106     if (visible != _isVisible) {
107         _isVisible = visible;
108         emit visibilityChanged(visible);
109     }
110 }
111
112
113 SystemTray::Mode SystemTray::mode() const
114 {
115     return _mode;
116 }
117
118
119 void SystemTray::setMode(Mode mode)
120 {
121     if (mode != _mode) {
122         _mode = mode;
123 #ifdef HAVE_KDE4
124         if (_trayMenu) {
125             if (mode == Mode::Legacy) {
126                 _trayMenu->setWindowFlags(Qt::Popup);
127             }
128             else {
129                 _trayMenu->setWindowFlags(Qt::Window);
130             }
131         }
132 #endif
133         emit modeChanged(mode);
134     }
135 }
136
137
138 SystemTray::State SystemTray::state() const
139 {
140     return _state;
141 }
142
143
144 void SystemTray::setState(State state)
145 {
146     if (_state != state) {
147         _state = state;
148         emit stateChanged(state);
149
150         if (state == NeedsAttention && _attentionBehavior == AttentionBehavior::Blink) {
151             _blinkTimer.start();
152             _blinkState = true;
153         }
154         else {
155             _blinkTimer.stop();
156             _blinkState = false;
157         }
158         emit currentIconNameChanged();
159     }
160 }
161
162
163 QString SystemTray::iconName(State state) const
164 {
165     QString name;
166     switch (state) {
167     case State::Passive:
168         name = "inactive-quassel-tray";
169         break;
170     case State::Active:
171         name = "active-quassel-tray";
172         break;
173     case State::NeedsAttention:
174         name = "message-quassel-tray";
175         break;
176     }
177
178     if (_trayIconInverted) {
179         name += "-inverted";
180     }
181
182     return name;
183 }
184
185
186 QString SystemTray::currentIconName() const
187 {
188     if (state() == State::NeedsAttention) {
189         if (_attentionBehavior == AttentionBehavior::ChangeColor) {
190             return iconName(State::NeedsAttention);
191         }
192         if (_attentionBehavior == AttentionBehavior::Blink && _blinkState) {
193             return iconName(State::NeedsAttention);
194         }
195         return iconName(State::Active);
196     }
197     else {
198         return iconName(state());
199     }
200 }
201
202
203 QString SystemTray::currentAttentionIconName() const
204 {
205     if (state() == State::NeedsAttention && _attentionBehavior == AttentionBehavior::Blink && !_blinkState) {
206         return iconName(State::Active);
207     }
208     return iconName(State::NeedsAttention);
209 }
210
211
212 bool SystemTray::isAlerted() const
213 {
214     return state() == State::NeedsAttention;
215 }
216
217
218 void SystemTray::setAlert(bool alerted)
219 {
220     if (alerted) {
221         setState(NeedsAttention);
222     }
223     else {
224         setState(Client::isConnected() ? Active : Passive);
225     }
226 }
227
228
229 void SystemTray::onBlinkTimeout()
230 {
231     _blinkState = !_blinkState;
232     emit currentIconNameChanged();
233 }
234
235
236 QMenu *SystemTray::trayMenu() const
237 {
238     return _trayMenu;
239 }
240
241
242 void SystemTray::trayMenuAboutToShow()
243 {
244     if (GraphicalUi::isMainWidgetVisible())
245         _minimizeRestoreAction->setText(tr("&Minimize"));
246     else
247         _minimizeRestoreAction->setText(tr("&Restore"));
248 }
249
250
251 void SystemTray::enableChangeColorChanged(const QVariant &v)
252 {
253     if (v.toBool()) {
254         _attentionBehavior = AttentionBehavior::ChangeColor;
255     }
256     else {
257         if (_attentionBehavior == AttentionBehavior::ChangeColor) {
258             _attentionBehavior = AttentionBehavior::DoNothing;
259         }
260     }
261     emit currentIconNameChanged();
262 }
263
264
265 void SystemTray::enableBlinkChanged(const QVariant &v)
266 {
267     if (v.toBool()) {
268         _attentionBehavior = AttentionBehavior::Blink;
269     }
270     else {
271         if (_attentionBehavior == AttentionBehavior::Blink) {
272             _attentionBehavior = AttentionBehavior::DoNothing;
273         }
274     }
275     emit currentIconNameChanged();
276 }
277
278
279 void SystemTray::invertTrayIconChanged(const QVariant &v)
280 {
281     _trayIconInverted = v.toBool();
282     emit iconsChanged();
283 }
284
285
286 QString SystemTray::toolTipTitle() const
287 {
288     return _toolTipTitle;
289 }
290
291
292 QString SystemTray::toolTipSubTitle() const
293 {
294     return _toolTipSubTitle;
295 }
296
297
298 void SystemTray::setToolTip(const QString &title, const QString &subtitle)
299 {
300     _toolTipTitle = title;
301     _toolTipSubTitle = subtitle;
302     emit toolTipChanged(title, subtitle);
303 }
304
305
306 void SystemTray::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint, uint id)
307 {
308     Q_UNUSED(title)
309     Q_UNUSED(message)
310     Q_UNUSED(icon)
311     Q_UNUSED(millisecondsTimeoutHint)
312     Q_UNUSED(id)
313 }
314
315
316 void SystemTray::closeMessage(uint notificationId)
317 {
318     Q_UNUSED(notificationId)
319 }
320
321
322 void SystemTray::activate(SystemTray::ActivationReason reason)
323 {
324     emit activated(reason);
325 }
326
327
328 void SystemTray::minimizeRestore()
329 {
330     GraphicalUi::toggleMainWidget();
331 }