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

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