1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Labs Platform module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 
37 #include "qquickplatformsystemtrayicon_p.h"
38 #include "qquickplatformmenu_p.h"
39 #include "qquickplatformiconloader_p.h"
40 
41 #include <QtCore/qloggingcategory.h>
42 #include <QtGui/qpa/qplatformtheme.h>
43 #include <QtGui/private/qguiapplication_p.h>
44 
45 #include "widgets/qwidgetplatform_p.h"
46 
47 QT_BEGIN_NAMESPACE
48 
49 /*!
50     \qmltype SystemTrayIcon
51     \inherits QtObject
52 //!     \instantiates QQuickPlatformSystemTrayIcon
53     \inqmlmodule Qt.labs.platform
54     \since 5.8
55     \brief A system tray icon.
56 
57     The SystemTrayIcon type provides an icon for an application in the system tray.
58 
59     Many desktop platforms provide a special system tray or notification area,
60     where applications can display icons and notification messages.
61 
62     \image qtlabsplatform-systemtrayicon.png
63 
64     The following example shows how to create a system tray icon, and how to make
65     use of the \l activated() signal:
66 
67     \code
68     SystemTrayIcon {
69         visible: true
70         icon.source: "qrc:/images/tray-icon.png"
71 
72         onActivated: {
73             window.show()
74             window.raise()
75             window.requestActivate()
76         }
77     }
78     \endcode
79 
80     \section2 Tray menu
81 
82     SystemTrayIcon can have a menu that opens when the icon is activated.
83 
84     \image qtlabsplatform-systemtrayicon-menu.png
85 
86     The following example illustrates how to assign a \l Menu to a system tray icon:
87 
88     \code
89     SystemTrayIcon {
90         visible: true
91         icon.source: "qrc:/images/tray-icon.png"
92 
93         menu: Menu {
94             MenuItem {
95                 text: qsTr("Quit")
96                 onTriggered: Qt.quit()
97             }
98         }
99     }
100     \endcode
101 
102     \section2 Notification messages
103 
104     SystemTrayIcon can display notification messages.
105 
106     \image qtlabsplatform-systemtrayicon-message.png
107 
108     The following example presents how to show a notification message using
109     \l showMessage(), and how to make use of the \l messageClicked() signal:
110 
111     \code
112     SystemTrayIcon {
113         visible: true
114         icon.source: "qrc:/images/tray-icon.png"
115 
116         onMessageClicked: console.log("Message clicked")
117         Component.onCompleted: showMessage("Message title", "Something important came up. Click this to know more.")
118     }
119     \endcode
120 
121     \section2 Availability
122 
123     A native system tray icon is currently \l available on the following platforms:
124 
125     \list
126     \li All window managers and independent tray implementations for X11 that implement the
127         \l{http://standards.freedesktop.org/systemtray-spec/systemtray-spec-0.2.html}
128         {freedesktop.org XEmbed system tray specification}.
129     \li All desktop environments that implement the
130         \l{http://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem}
131         {freedesktop.org D-Bus StatusNotifierItem specification}, including recent versions of KDE and Unity.
132     \li All supported versions of macOS. Note that the Growl notification system must be installed
133         for showMessage() to display messages on OS X prior to 10.8 (Mountain Lion).
134     \endlist
135 
136     \input includes/widgets.qdocinc 1
137 
138     \labs
139 
140     \sa Menu
141 */
142 
143 /*!
144     \qmlsignal Qt.labs.platform::SystemTrayIcon::activated(ActivationReason reason)
145 
146     This signal is emitted when the system tray icon is activated by the user. The
147     \a reason argument specifies how the system tray icon was activated.
148 
149     Available reasons:
150 
151     \value SystemTrayIcon.Unknown     Unknown reason
152     \value SystemTrayIcon.Context     The context menu for the system tray icon was requested
153     \value SystemTrayIcon.DoubleClick The system tray icon was double clicked
154     \value SystemTrayIcon.Trigger     The system tray icon was clicked
155     \value SystemTrayIcon.MiddleClick The system tray icon was clicked with the middle mouse button
156 */
157 
158 /*!
159     \qmlsignal Qt.labs.platform::SystemTrayIcon::messageClicked()
160 
161     This signal is emitted when a notification message is clicked by the user.
162 
163     \sa showMessage()
164 */
165 
Q_DECLARE_LOGGING_CATEGORY(qtLabsPlatformTray)166 Q_DECLARE_LOGGING_CATEGORY(qtLabsPlatformTray)
167 
168 QQuickPlatformSystemTrayIcon::QQuickPlatformSystemTrayIcon(QObject *parent)
169     : QObject(parent),
170       m_complete(false),
171       m_visible(false),
172       m_menu(nullptr),
173       m_iconLoader(nullptr),
174       m_handle(nullptr)
175 {
176     m_handle = QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon();
177     if (!m_handle)
178         m_handle = QWidgetPlatform::createSystemTrayIcon(this);
179     qCDebug(qtLabsPlatformTray) << "SystemTrayIcon ->" << m_handle;
180 
181     if (m_handle) {
182         connect(m_handle, &QPlatformSystemTrayIcon::activated, this, &QQuickPlatformSystemTrayIcon::activated);
183         connect(m_handle, &QPlatformSystemTrayIcon::messageClicked, this, &QQuickPlatformSystemTrayIcon::messageClicked);
184     }
185 }
186 
~QQuickPlatformSystemTrayIcon()187 QQuickPlatformSystemTrayIcon::~QQuickPlatformSystemTrayIcon()
188 {
189     if (m_menu)
190         m_menu->setSystemTrayIcon(nullptr);
191     cleanup();
192     delete m_iconLoader;
193     m_iconLoader = nullptr;
194     delete m_handle;
195     m_handle = nullptr;
196 }
197 
handle() const198 QPlatformSystemTrayIcon *QQuickPlatformSystemTrayIcon::handle() const
199 {
200     return m_handle;
201 }
202 
203 /*!
204     \readonly
205     \qmlproperty bool Qt.labs.platform::SystemTrayIcon::available
206 
207     This property holds whether the system tray is available.
208 */
isAvailable() const209 bool QQuickPlatformSystemTrayIcon::isAvailable() const
210 {
211     return m_handle && m_handle->isSystemTrayAvailable();
212 }
213 
214 /*!
215     \readonly
216     \qmlproperty bool Qt.labs.platform::SystemTrayIcon::supportsMessages
217 
218     This property holds whether the system tray icon supports notification messages.
219 
220     \sa showMessage()
221 */
supportsMessages() const222 bool QQuickPlatformSystemTrayIcon::supportsMessages() const
223 {
224     return m_handle && m_handle->supportsMessages();
225 }
226 
227 /*!
228     \qmlproperty bool Qt.labs.platform::SystemTrayIcon::visible
229 
230     This property holds whether the system tray icon is visible.
231 
232     The default value is \c false.
233 */
isVisible() const234 bool QQuickPlatformSystemTrayIcon::isVisible() const
235 {
236     return m_visible;
237 }
238 
setVisible(bool visible)239 void QQuickPlatformSystemTrayIcon::setVisible(bool visible)
240 {
241     if (m_visible == visible)
242         return;
243 
244     if (m_handle && m_complete) {
245         if (visible)
246             init();
247         else
248             cleanup();
249     }
250 
251     m_visible = visible;
252     emit visibleChanged();
253 }
254 
255 /*!
256     \qmlproperty url Qt.labs.platform::SystemTrayIcon::iconSource
257     \deprecated Use icon.source instead.
258     \sa icon
259 */
iconSource() const260 QUrl QQuickPlatformSystemTrayIcon::iconSource() const
261 {
262     return icon().source();
263 }
264 
setIconSource(const QUrl & source)265 void QQuickPlatformSystemTrayIcon::setIconSource(const QUrl& source)
266 {
267     QQuickPlatformIcon newIcon = icon();
268     if (source == newIcon.source())
269         return;
270 
271     newIcon.setSource(source);
272     iconLoader()->setIcon(newIcon);
273     emit iconSourceChanged();
274 }
275 
276 /*!
277     \qmlproperty string Qt.labs.platform::SystemTrayIcon::iconName
278     \deprecated Use icon.name instead.
279     \sa icon
280 */
iconName() const281 QString QQuickPlatformSystemTrayIcon::iconName() const
282 {
283     return icon().name();
284 }
285 
setIconName(const QString & name)286 void QQuickPlatformSystemTrayIcon::setIconName(const QString& name)
287 {
288     QQuickPlatformIcon newIcon = icon();
289     if (name == newIcon.name())
290         return;
291 
292     newIcon.setName(name);
293     iconLoader()->setIcon(newIcon);
294     emit iconNameChanged();
295 }
296 
297 /*!
298     \qmlproperty string Qt.labs.platform::SystemTrayIcon::tooltip
299 
300     This property holds the tooltip of the system tray icon.
301 */
tooltip() const302 QString QQuickPlatformSystemTrayIcon::tooltip() const
303 {
304     return m_tooltip;
305 }
306 
setTooltip(const QString & tooltip)307 void QQuickPlatformSystemTrayIcon::setTooltip(const QString &tooltip)
308 {
309     if (m_tooltip == tooltip)
310         return;
311 
312     if (m_handle && m_complete)
313         m_handle->updateToolTip(tooltip);
314 
315     m_tooltip = tooltip;
316     emit tooltipChanged();
317 }
318 
319 /*!
320     \qmlproperty Menu Qt.labs.platform::SystemTrayIcon::menu
321 
322     This property holds a menu for the system tray icon.
323 */
menu() const324 QQuickPlatformMenu *QQuickPlatformSystemTrayIcon::menu() const
325 {
326     return m_menu;
327 }
328 
setMenu(QQuickPlatformMenu * menu)329 void QQuickPlatformSystemTrayIcon::setMenu(QQuickPlatformMenu *menu)
330 {
331     if (m_menu == menu)
332         return;
333 
334     if (m_menu)
335         m_menu->setSystemTrayIcon(nullptr);
336     if (menu) {
337         menu->setSystemTrayIcon(this);
338         if (m_handle && m_complete && menu->create())
339             m_handle->updateMenu(menu->handle());
340     }
341 
342     m_menu = menu;
343     emit menuChanged();
344 }
345 
346 /*!
347     \since Qt.labs.platform 1.1 (Qt 5.12)
348     \qmlproperty rect Qt.labs.platform::SystemTrayIcon::geometry
349 
350     This property holds the geometry of the system tray icon.
351 */
geometry() const352 QRect QQuickPlatformSystemTrayIcon::geometry() const
353 {
354     return m_handle ? m_handle->geometry() : QRect();
355 }
356 
357 /*!
358     \since Qt.labs.platform 1.1 (Qt 5.12)
359     \qmlproperty url Qt.labs.platform::SystemTrayIcon::icon.source
360     \qmlproperty string Qt.labs.platform::SystemTrayIcon::icon.name
361     \qmlproperty bool Qt.labs.platform::SystemTrayIcon::icon.mask
362 
363     This property holds the system tray icon.
364 
365     \code
366     SystemTrayIcon {
367         icon.mask: true
368         icon.source: "qrc:/images/tray-icon.png"
369     }
370     \endcode
371 */
icon() const372 QQuickPlatformIcon QQuickPlatformSystemTrayIcon::icon() const
373 {
374     if (!m_iconLoader)
375         return QQuickPlatformIcon();
376 
377     return m_iconLoader->icon();
378 }
379 
setIcon(const QQuickPlatformIcon & icon)380 void QQuickPlatformSystemTrayIcon::setIcon(const QQuickPlatformIcon &icon)
381 {
382     if (iconLoader()->icon() == icon)
383         return;
384 
385     iconLoader()->setIcon(icon);
386     emit iconChanged();
387 }
388 
389 /*!
390     \qmlmethod void Qt.labs.platform::SystemTrayIcon::show()
391 
392     Shows the system tray icon.
393 */
show()394 void QQuickPlatformSystemTrayIcon::show()
395 {
396     setVisible(true);
397 }
398 
399 /*!
400     \qmlmethod void Qt.labs.platform::SystemTrayIcon::hide()
401 
402     Hides the system tray icon.
403 */
hide()404 void QQuickPlatformSystemTrayIcon::hide()
405 {
406     setVisible(false);
407 }
408 
409 /*!
410     \qmlmethod void Qt.labs.platform::SystemTrayIcon::showMessage(string title, string message, MessageIcon icon, int msecs)
411 
412     Shows a system tray message with the given \a title, \a message and \a icon
413     for the time specified in \a msecs.
414 
415     \note System tray messages are dependent on the system configuration and user preferences,
416     and may not appear at all. Therefore, it should not be relied upon as the sole means for providing
417     critical information.
418 
419     \sa supportsMessages, messageClicked()
420 */
showMessage(const QString & title,const QString & msg,QPlatformSystemTrayIcon::MessageIcon icon,int msecs)421 void QQuickPlatformSystemTrayIcon::showMessage(const QString &title, const QString &msg, QPlatformSystemTrayIcon::MessageIcon icon, int msecs)
422 {
423     if (m_handle)
424         m_handle->showMessage(title, msg, QIcon(), icon, msecs);
425 }
426 
init()427 void QQuickPlatformSystemTrayIcon::init()
428 {
429     if (!m_handle)
430         return;
431 
432     m_handle->init();
433     if (m_menu && m_menu->create())
434         m_handle->updateMenu(m_menu->handle());
435     m_handle->updateToolTip(m_tooltip);
436     if (m_iconLoader)
437         m_iconLoader->setEnabled(true);
438 }
439 
cleanup()440 void QQuickPlatformSystemTrayIcon::cleanup()
441 {
442     if (m_handle)
443         m_handle->cleanup();
444     if (m_iconLoader)
445         m_iconLoader->setEnabled(false);
446 }
447 
classBegin()448 void QQuickPlatformSystemTrayIcon::classBegin()
449 {
450 }
451 
componentComplete()452 void QQuickPlatformSystemTrayIcon::componentComplete()
453 {
454     m_complete = true;
455     if (m_visible)
456         init();
457 }
458 
iconLoader() const459 QQuickPlatformIconLoader *QQuickPlatformSystemTrayIcon::iconLoader() const
460 {
461     if (!m_iconLoader) {
462         QQuickPlatformSystemTrayIcon *that = const_cast<QQuickPlatformSystemTrayIcon *>(this);
463         static int slot = staticMetaObject.indexOfSlot("updateIcon()");
464         m_iconLoader = new QQuickPlatformIconLoader(slot, that);
465         m_iconLoader->setEnabled(m_complete);
466     }
467     return m_iconLoader;
468 }
469 
updateIcon()470 void QQuickPlatformSystemTrayIcon::updateIcon()
471 {
472     if (!m_handle || !m_iconLoader)
473         return;
474 
475     const QRect oldGeometry = m_handle->geometry();
476 
477     m_handle->updateIcon(m_iconLoader->toQIcon());
478 
479     if (oldGeometry != m_handle->geometry())
480         emit geometryChanged();
481 }
482 
483 QT_END_NAMESPACE
484