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
67QT_BEGIN_NAMESPACE
68
69static inline unsigned long locateSystemTray()
70{
71 return (unsigned long)QGuiApplication::platformNativeInterface()->nativeResourceForScreen(QByteArrayLiteral("traywindow"), screen: 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.
76class QSystemTrayIconSys : public QWidget
77{
78 Q_OBJECT
79public:
80 explicit QSystemTrayIconSys(QSystemTrayIcon *q);
81
82 inline void updateIcon() { update(); }
83 inline QSystemTrayIcon *systemTrayIcon() const { return q; }
84
85 QRect globalGeometry() const;
86
87protected:
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
95private:
96 QSystemTrayIcon *q;
97};
98
99QSystemTrayIconSys::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, on: true);
108 setAttribute(Qt::WA_QuitOnClose, on: 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
116QRect QSystemTrayIconSys::globalGeometry() const
117{
118 return QRect(mapToGlobal(QPoint(0, 0)), size());
119}
120
121void 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(pos: 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(reason: QSystemTrayIcon::Trigger);
138 else if (ev->button() == Qt::RightButton)
139 emit q->activated(reason: QSystemTrayIcon::Context);
140 else if (ev->button() == Qt::MiddleButton)
141 emit q->activated(reason: QSystemTrayIcon::MiddleClick);
142}
143
144void QSystemTrayIconSys::mouseDoubleClickEvent(QMouseEvent *ev)
145{
146 if (ev->button() == Qt::LeftButton)
147 emit q->activated(reason: QSystemTrayIcon::DoubleClick);
148}
149
150bool QSystemTrayIconSys::event(QEvent *e)
151{
152 switch (e->type()) {
153 case QEvent::ToolTip:
154 QCoreApplication::sendEvent(receiver: q, event: e);
155 break;
156#if QT_CONFIG(wheelevent)
157 case QEvent::Wheel:
158 return QCoreApplication::sendEvent(receiver: q, event: e);
159#endif
160 default:
161 break;
162 }
163 return QWidget::event(event: e);
164}
165
166void QSystemTrayIconSys::paintEvent(QPaintEvent *)
167{
168 const QRect rect(QPoint(0, 0), geometry().size());
169 QPainter painter(this);
170
171 q->icon().paint(painter: &painter, rect);
172}
173
174void QSystemTrayIconSys::moveEvent(QMoveEvent *event)
175{
176 QWidget::moveEvent(event);
177 if (QBalloonTip::isBalloonVisible())
178 QBalloonTip::updateBalloonPosition(pos: globalGeometry().center());
179}
180
181void QSystemTrayIconSys::resizeEvent(QResizeEvent *event)
182{
183 update();
184 QWidget::resizeEvent(event);
185 if (QBalloonTip::isBalloonVisible())
186 QBalloonTip::updateBalloonPosition(pos: globalGeometry().center());
187}
188////////////////////////////////////////////////////////////////////////////
189
190class QSystemTrayWatcher: public QObject
191{
192 Q_OBJECT
193public:
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 receiver: this, SLOT(systemTrayWindowChanged(QScreen*)));
202 }
203
204private slots:
205 void systemTrayWindowChanged(QScreen *)
206 {
207 auto icon = static_cast<QSystemTrayIconPrivate *>(QObjectPrivate::get(o: mTrayIcon));
208 icon->destroyIcon();
209 if (icon->visible && locateSystemTray()) {
210 icon->sys = new QSystemTrayIconSys(mTrayIcon);
211 icon->sys->show();
212 }
213 }
214
215private:
216 QSystemTrayIcon *mTrayIcon = nullptr;
217};
218////////////////////////////////////////////////////////////////////////////
219
220QSystemTrayIconPrivate::QSystemTrayIconPrivate()
221 : sys(nullptr),
222 qpa_sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon()),
223 visible(false),
224 trayWatcher(nullptr)
225{
226}
227
228QSystemTrayIconPrivate::~QSystemTrayIconPrivate()
229{
230 delete qpa_sys;
231}
232
233void 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
253QRect 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
262void QSystemTrayIconPrivate::remove_sys()
263{
264 if (qpa_sys) {
265 remove_sys_qpa();
266 return;
267 }
268
269 destroyIcon();
270}
271
272void QSystemTrayIconPrivate::destroyIcon()
273{
274 if (!sys)
275 return;
276 QBalloonTip::hideBalloon();
277 sys->hide();
278 delete sys;
279 sys = nullptr;
280}
281
282
283void QSystemTrayIconPrivate::updateIcon_sys()
284{
285 if (qpa_sys) {
286 qpa_sys->updateIcon(icon);
287 return;
288 }
289 if (sys)
290 sys->updateIcon();
291}
292
293void QSystemTrayIconPrivate::updateMenu_sys()
294{
295#if QT_CONFIG(menu)
296 if (qpa_sys && menu) {
297 addPlatformMenu(menu);
298 qpa_sys->updateMenu(menu: menu->platformMenu());
299 }
300#endif
301}
302
303void QSystemTrayIconPrivate::updateToolTip_sys()
304{
305 if (qpa_sys) {
306 qpa_sys->updateToolTip(tooltip: toolTip);
307 return;
308 }
309 if (!sys)
310 return;
311#ifndef QT_NO_TOOLTIP
312 sys->setToolTip(toolTip);
313#endif
314}
315
316bool 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(other: QLatin1String("xcb"), cs: Qt::CaseInsensitive) == 0)
325 return locateSystemTray();
326 return false;
327}
328
329bool 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
339void 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, msg: message, icon,
344 iconType: static_cast<QPlatformSystemTrayIcon::MessageIcon>(msgIcon), msecs);
345 return;
346 }
347 if (!sys)
348 return;
349 QBalloonTip::showBalloon(icon, title, msg: message, trayIcon: sys->systemTrayIcon(),
350 pos: sys->globalGeometry().center(),
351 timeout: msecs);
352}
353
354QT_END_NAMESPACE
355
356#include "qsystemtrayicon_x11.moc"
357
358#endif //QT_NO_SYSTEMTRAYICON
359

source code of qtbase/src/widgets/util/qsystemtrayicon_x11.cpp