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

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