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