1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qtwidgetsglobal.h"
41 #if QT_CONFIG(label)
42 #include "qlabel.h"
43 #endif
44 #include "qpainter.h"
45 #include "qpixmap.h"
46 #include "qbitmap.h"
47 #include "qevent.h"
48 #include "qapplication.h"
49 #include "qlist.h"
50 #if QT_CONFIG(menu)
51 #include "qmenu.h"
52 #endif
53 #include "qtimer.h"
54 #include "qsystemtrayicon_p.h"
55 #include "qpaintengine.h"
56 #include <qwindow.h>
57 #include <qguiapplication.h>
58 #include <qscreen.h>
59 #include <qbackingstore.h>
60 #include <qpa/qplatformnativeinterface.h>
61 #include <qpa/qplatformsystemtrayicon.h>
62 #include <qpa/qplatformtheme.h>
63 #include <private/qguiapplication_p.h>
64 #include <qdebug.h>
65
66 #ifndef QT_NO_SYSTEMTRAYICON
67 QT_BEGIN_NAMESPACE
68
locateSystemTray()69 static inline unsigned long locateSystemTray()
70 {
71 return (unsigned long)QGuiApplication::platformNativeInterface()->nativeResourceForScreen(QByteArrayLiteral("traywindow"), QGuiApplication::primaryScreen());
72 }
73
74 // System tray widget. Could be replaced by a QWindow with
75 // a backing store if it did not need tooltip handling.
76 class QSystemTrayIconSys : public QWidget
77 {
78 Q_OBJECT
79 public:
80 explicit QSystemTrayIconSys(QSystemTrayIcon *q);
81
updateIcon()82 inline void updateIcon() { update(); }
systemTrayIcon() const83 inline QSystemTrayIcon *systemTrayIcon() const { return q; }
84
85 QRect globalGeometry() const;
86
87 protected:
88 virtual void mousePressEvent(QMouseEvent *ev) override;
89 virtual void mouseDoubleClickEvent(QMouseEvent *ev) override;
90 virtual bool event(QEvent *) override;
91 virtual void paintEvent(QPaintEvent *) override;
92 virtual void resizeEvent(QResizeEvent *) override;
93 virtual void moveEvent(QMoveEvent *) override;
94
95 private:
96 QSystemTrayIcon *q;
97 };
98
QSystemTrayIconSys(QSystemTrayIcon * qIn)99 QSystemTrayIconSys::QSystemTrayIconSys(QSystemTrayIcon *qIn)
100 : QWidget(nullptr, Qt::Window | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint)
101 , q(qIn)
102 {
103 setObjectName(QStringLiteral("QSystemTrayIconSys"));
104 #if QT_CONFIG(tooltip)
105 setToolTip(q->toolTip());
106 #endif
107 setAttribute(Qt::WA_AlwaysShowToolTips, true);
108 setAttribute(Qt::WA_QuitOnClose, false);
109 const QSize size(22, 22); // Gnome, standard size
110 setGeometry(QRect(QPoint(0, 0), size));
111 setMinimumSize(size);
112 setAttribute(Qt::WA_TranslucentBackground);
113 setMouseTracking(true);
114 }
115
globalGeometry() const116 QRect QSystemTrayIconSys::globalGeometry() const
117 {
118 return QRect(mapToGlobal(QPoint(0, 0)), size());
119 }
120
mousePressEvent(QMouseEvent * ev)121 void QSystemTrayIconSys::mousePressEvent(QMouseEvent *ev)
122 {
123 QPoint globalPos = ev->globalPos();
124 #ifndef QT_NO_CONTEXTMENU
125 if (ev->button() == Qt::RightButton && q->contextMenu())
126 q->contextMenu()->popup(globalPos);
127 #else
128 Q_UNUSED(globalPos)
129 #endif // QT_NO_CONTEXTMENU
130
131 if (QBalloonTip::isBalloonVisible()) {
132 emit q->messageClicked();
133 QBalloonTip::hideBalloon();
134 }
135
136 if (ev->button() == Qt::LeftButton)
137 emit q->activated(QSystemTrayIcon::Trigger);
138 else if (ev->button() == Qt::RightButton)
139 emit q->activated(QSystemTrayIcon::Context);
140 else if (ev->button() == Qt::MiddleButton)
141 emit q->activated(QSystemTrayIcon::MiddleClick);
142 }
143
mouseDoubleClickEvent(QMouseEvent * ev)144 void QSystemTrayIconSys::mouseDoubleClickEvent(QMouseEvent *ev)
145 {
146 if (ev->button() == Qt::LeftButton)
147 emit q->activated(QSystemTrayIcon::DoubleClick);
148 }
149
event(QEvent * e)150 bool QSystemTrayIconSys::event(QEvent *e)
151 {
152 switch (e->type()) {
153 case QEvent::ToolTip:
154 QCoreApplication::sendEvent(q, e);
155 break;
156 #if QT_CONFIG(wheelevent)
157 case QEvent::Wheel:
158 return QCoreApplication::sendEvent(q, e);
159 #endif
160 default:
161 break;
162 }
163 return QWidget::event(e);
164 }
165
paintEvent(QPaintEvent *)166 void QSystemTrayIconSys::paintEvent(QPaintEvent *)
167 {
168 const QRect rect(QPoint(0, 0), geometry().size());
169 QPainter painter(this);
170
171 q->icon().paint(&painter, rect);
172 }
173
moveEvent(QMoveEvent * event)174 void QSystemTrayIconSys::moveEvent(QMoveEvent *event)
175 {
176 QWidget::moveEvent(event);
177 if (QBalloonTip::isBalloonVisible())
178 QBalloonTip::updateBalloonPosition(globalGeometry().center());
179 }
180
resizeEvent(QResizeEvent * event)181 void QSystemTrayIconSys::resizeEvent(QResizeEvent *event)
182 {
183 update();
184 QWidget::resizeEvent(event);
185 if (QBalloonTip::isBalloonVisible())
186 QBalloonTip::updateBalloonPosition(globalGeometry().center());
187 }
188 ////////////////////////////////////////////////////////////////////////////
189
190 class QSystemTrayWatcher: public QObject
191 {
192 Q_OBJECT
193 public:
QSystemTrayWatcher(QSystemTrayIcon * trayIcon)194 QSystemTrayWatcher(QSystemTrayIcon *trayIcon)
195 : QObject(trayIcon)
196 , mTrayIcon(trayIcon)
197 {
198 // This code uses string-based syntax because we want to connect to a signal
199 // which is defined in XCB plugin - QXcbNativeInterface::systemTrayWindowChanged().
200 connect(qGuiApp->platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*)),
201 this, SLOT(systemTrayWindowChanged(QScreen*)));
202 }
203
204 private slots:
systemTrayWindowChanged(QScreen *)205 void systemTrayWindowChanged(QScreen *)
206 {
207 auto icon = static_cast<QSystemTrayIconPrivate *>(QObjectPrivate::get(mTrayIcon));
208 icon->destroyIcon();
209 if (icon->visible && locateSystemTray()) {
210 icon->sys = new QSystemTrayIconSys(mTrayIcon);
211 icon->sys->show();
212 }
213 }
214
215 private:
216 QSystemTrayIcon *mTrayIcon = nullptr;
217 };
218 ////////////////////////////////////////////////////////////////////////////
219
QSystemTrayIconPrivate()220 QSystemTrayIconPrivate::QSystemTrayIconPrivate()
221 : sys(nullptr),
222 qpa_sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon()),
223 visible(false),
224 trayWatcher(nullptr)
225 {
226 }
227
~QSystemTrayIconPrivate()228 QSystemTrayIconPrivate::~QSystemTrayIconPrivate()
229 {
230 delete qpa_sys;
231 }
232
install_sys()233 void QSystemTrayIconPrivate::install_sys()
234 {
235 Q_Q(QSystemTrayIcon);
236
237 if (qpa_sys) {
238 install_sys_qpa();
239 return;
240 }
241
242 if (!sys) {
243 if (!trayWatcher)
244 trayWatcher = new QSystemTrayWatcher(q);
245
246 if (locateSystemTray()) {
247 sys = new QSystemTrayIconSys(q);
248 sys->show();
249 }
250 }
251 }
252
geometry_sys() const253 QRect QSystemTrayIconPrivate::geometry_sys() const
254 {
255 if (qpa_sys)
256 return qpa_sys->geometry();
257 if (!sys)
258 return QRect();
259 return sys->globalGeometry();
260 }
261
remove_sys()262 void QSystemTrayIconPrivate::remove_sys()
263 {
264 if (qpa_sys) {
265 remove_sys_qpa();
266 return;
267 }
268
269 destroyIcon();
270 }
271
destroyIcon()272 void QSystemTrayIconPrivate::destroyIcon()
273 {
274 if (!sys)
275 return;
276 QBalloonTip::hideBalloon();
277 sys->hide();
278 delete sys;
279 sys = nullptr;
280 }
281
282
updateIcon_sys()283 void QSystemTrayIconPrivate::updateIcon_sys()
284 {
285 if (qpa_sys) {
286 qpa_sys->updateIcon(icon);
287 return;
288 }
289 if (sys)
290 sys->updateIcon();
291 }
292
updateMenu_sys()293 void QSystemTrayIconPrivate::updateMenu_sys()
294 {
295 #if QT_CONFIG(menu)
296 if (qpa_sys && menu) {
297 addPlatformMenu(menu);
298 qpa_sys->updateMenu(menu->platformMenu());
299 }
300 #endif
301 }
302
updateToolTip_sys()303 void QSystemTrayIconPrivate::updateToolTip_sys()
304 {
305 if (qpa_sys) {
306 qpa_sys->updateToolTip(toolTip);
307 return;
308 }
309 if (!sys)
310 return;
311 #ifndef QT_NO_TOOLTIP
312 sys->setToolTip(toolTip);
313 #endif
314 }
315
isSystemTrayAvailable_sys()316 bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys()
317 {
318 QScopedPointer<QPlatformSystemTrayIcon> sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon());
319 if (sys && sys->isSystemTrayAvailable())
320 return true;
321
322 // no QPlatformSystemTrayIcon so fall back to default xcb platform behavior
323 const QString platform = QGuiApplication::platformName();
324 if (platform.compare(QLatin1String("xcb"), Qt::CaseInsensitive) == 0)
325 return locateSystemTray();
326 return false;
327 }
328
supportsMessages_sys()329 bool QSystemTrayIconPrivate::supportsMessages_sys()
330 {
331 QScopedPointer<QPlatformSystemTrayIcon> sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon());
332 if (sys)
333 return sys->supportsMessages();
334
335 // no QPlatformSystemTrayIcon so fall back to default xcb platform behavior
336 return true;
337 }
338
showMessage_sys(const QString & title,const QString & message,const QIcon & icon,QSystemTrayIcon::MessageIcon msgIcon,int msecs)339 void QSystemTrayIconPrivate::showMessage_sys(const QString &title, const QString &message,
340 const QIcon &icon, QSystemTrayIcon::MessageIcon msgIcon, int msecs)
341 {
342 if (qpa_sys) {
343 qpa_sys->showMessage(title, message, icon,
344 static_cast<QPlatformSystemTrayIcon::MessageIcon>(msgIcon), msecs);
345 return;
346 }
347 if (!sys)
348 return;
349 QBalloonTip::showBalloon(icon, title, message, sys->systemTrayIcon(),
350 sys->globalGeometry().center(),
351 msecs);
352 }
353
354 QT_END_NAMESPACE
355
356 #include "qsystemtrayicon_x11.moc"
357
358 #endif //QT_NO_SYSTEMTRAYICON
359