// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default

#include "qquicklabsplatformsystemtrayicon_p.h"
#include "qquicklabsplatformmenu_p.h"
#include "qquicklabsplatformiconloader_p.h"

#include <QtCore/qloggingcategory.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtGui/private/qguiapplication_p.h>

#include "widgets/qwidgetplatform_p.h"

QT_BEGIN_NAMESPACE

/*!
    \qmltype SystemTrayIcon
    \inherits QtObject
//!     \nativetype QQuickLabsPlatformSystemTrayIcon
    \inqmlmodule Qt.labs.platform
    \since 5.8
    \brief A system tray icon.

    The SystemTrayIcon type provides an icon for an application in the system tray.

    Many desktop platforms provide a special system tray or notification area,
    where applications can display icons and notification messages.

    \image {qtlabsplatform-systemtrayicon.png} {System tray icon}

    The following example shows how to create a system tray icon, and how to make
    use of the \l activated() signal:

    \code
    SystemTrayIcon {
        visible: true
        icon.source: "qrc:/images/tray-icon.png"

        onActivated: {
            window.show()
            window.raise()
            window.requestActivate()
        }
    }
    \endcode

    \section2 Tray menu

    SystemTrayIcon can have a menu that opens when the icon is activated.

    \image {qtlabsplatform-systemtrayicon-menu.png} {System tray icon with menu}

    The following example illustrates how to assign a \l Menu to a system tray icon:

    \code
    SystemTrayIcon {
        visible: true
        icon.source: "qrc:/images/tray-icon.png"

        menu: Menu {
            MenuItem {
                text: qsTr("Quit")
                onTriggered: Qt.quit()
            }
        }
    }
    \endcode

    \section2 Notification messages

    SystemTrayIcon can display notification messages.

    \image {qtlabsplatform-systemtrayicon-message.png} {System tray icon message popup}

    The following example presents how to show a notification message using
    \l showMessage(), and how to make use of the \l messageClicked() signal:

    \code
    SystemTrayIcon {
        visible: true
        icon.source: "qrc:/images/tray-icon.png"

        onMessageClicked: console.log("Message clicked")
        Component.onCompleted: showMessage("Message title", "Something important came up. Click this to know more.")
    }
    \endcode

    \section2 Availability

    A native system tray icon is currently \l available on the following platforms:

    \list
    \li All window managers and independent tray implementations for X11 that implement the
        \l{http://standards.freedesktop.org/systemtray-spec/systemtray-spec-0.2.html}
        {freedesktop.org XEmbed system tray specification}.
    \li All desktop environments that implement the
        \l{http://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem}
        {freedesktop.org D-Bus StatusNotifierItem specification}, including recent versions of KDE and Unity.
    \li All supported versions of macOS. Note that the Growl notification system must be installed
        for showMessage() to display messages on OS X prior to 10.8 (Mountain Lion).
    \endlist

    \input includes/widgets.qdocinc 1

    \labs

    \sa Menu
*/

/*!
    \qmlsignal Qt.labs.platform::SystemTrayIcon::activated(ActivationReason reason)

    This signal is emitted when the system tray icon is activated by the user. The
    \a reason argument specifies how the system tray icon was activated.

    Available reasons:

    \value SystemTrayIcon.Unknown     Unknown reason
    \value SystemTrayIcon.Context     The context menu for the system tray icon was requested
    \value SystemTrayIcon.DoubleClick The system tray icon was double clicked
    \value SystemTrayIcon.Trigger     The system tray icon was clicked
    \value SystemTrayIcon.MiddleClick The system tray icon was clicked with the middle mouse button
*/

/*!
    \qmlsignal Qt.labs.platform::SystemTrayIcon::messageClicked()

    This signal is emitted when a notification message is clicked by the user.

    \sa showMessage()
*/

Q_STATIC_LOGGING_CATEGORY(qtLabsPlatformTray, "qt.labs.platform.tray")

QQuickLabsPlatformSystemTrayIcon::QQuickLabsPlatformSystemTrayIcon(QObject *parent)
    : QObject(parent),
      m_complete(false),
      m_visible(false),
      m_menu(nullptr),
      m_iconLoader(nullptr),
      m_handle(nullptr)
{
    m_handle = QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon();
    if (!m_handle)
        m_handle = QWidgetPlatform::createSystemTrayIcon(this);
    qCDebug(qtLabsPlatformTray) << "SystemTrayIcon ->" << m_handle;

    if (m_handle) {
        connect(m_handle, &QPlatformSystemTrayIcon::activated, this, &QQuickLabsPlatformSystemTrayIcon::activated);
        connect(m_handle, &QPlatformSystemTrayIcon::messageClicked, this, &QQuickLabsPlatformSystemTrayIcon::messageClicked);
    }
}

QQuickLabsPlatformSystemTrayIcon::~QQuickLabsPlatformSystemTrayIcon()
{
    if (m_menu)
        m_menu->setSystemTrayIcon(nullptr);
    cleanup();
    delete m_iconLoader;
    m_iconLoader = nullptr;
    delete m_handle;
    m_handle = nullptr;
}

QPlatformSystemTrayIcon *QQuickLabsPlatformSystemTrayIcon::handle() const
{
    return m_handle;
}

/*!
    \readonly
    \qmlproperty bool Qt.labs.platform::SystemTrayIcon::available

    This property holds whether the system tray is available.
*/
bool QQuickLabsPlatformSystemTrayIcon::isAvailable() const
{
    return m_handle && m_handle->isSystemTrayAvailable();
}

/*!
    \readonly
    \qmlproperty bool Qt.labs.platform::SystemTrayIcon::supportsMessages

    This property holds whether the system tray icon supports notification messages.

    \sa showMessage()
*/
bool QQuickLabsPlatformSystemTrayIcon::supportsMessages() const
{
    return m_handle && m_handle->supportsMessages();
}

/*!
    \qmlproperty bool Qt.labs.platform::SystemTrayIcon::visible

    This property holds whether the system tray icon is visible.

    The default value is \c false.
*/
bool QQuickLabsPlatformSystemTrayIcon::isVisible() const
{
    return m_visible;
}

void QQuickLabsPlatformSystemTrayIcon::setVisible(bool visible)
{
    if (m_visible == visible)
        return;

    if (m_handle && m_complete) {
        if (visible)
            init();
        else
            cleanup();
    }

    m_visible = visible;
    emit visibleChanged();
}

/*!
    \qmlproperty string Qt.labs.platform::SystemTrayIcon::tooltip

    This property holds the tooltip of the system tray icon.
*/
QString QQuickLabsPlatformSystemTrayIcon::tooltip() const
{
    return m_tooltip;
}

void QQuickLabsPlatformSystemTrayIcon::setTooltip(const QString &tooltip)
{
    if (m_tooltip == tooltip)
        return;

    if (m_handle && m_complete)
        m_handle->updateToolTip(tooltip);

    m_tooltip = tooltip;
    emit tooltipChanged();
}

/*!
    \qmlproperty Menu Qt.labs.platform::SystemTrayIcon::menu

    This property holds a menu for the system tray icon.
*/
QQuickLabsPlatformMenu *QQuickLabsPlatformSystemTrayIcon::menu() const
{
    return m_menu;
}

void QQuickLabsPlatformSystemTrayIcon::setMenu(QQuickLabsPlatformMenu *menu)
{
    if (m_menu == menu)
        return;

    if (m_menu)
        m_menu->setSystemTrayIcon(nullptr);

    if (menu)
        menu->setSystemTrayIcon(this);

    if (m_handle && m_complete) {
        if (menu && menu->create())
            m_handle->updateMenu(menu->handle());
        else
            m_handle->updateMenu(nullptr);
    }

    m_menu = menu;
    emit menuChanged();
}

/*!
    \since Qt.labs.platform 1.1 (Qt 5.12)
    \qmlproperty rect Qt.labs.platform::SystemTrayIcon::geometry

    This property holds the geometry of the system tray icon.
*/
QRect QQuickLabsPlatformSystemTrayIcon::geometry() const
{
    return m_handle ? m_handle->geometry() : QRect();
}

/*!
    \since Qt.labs.platform 1.1 (Qt 5.12)
    \qmlproperty url Qt.labs.platform::SystemTrayIcon::icon.source
    \qmlproperty string Qt.labs.platform::SystemTrayIcon::icon.name
    \qmlproperty bool Qt.labs.platform::SystemTrayIcon::icon.mask

    This property holds the system tray icon.

    \code
    SystemTrayIcon {
        icon.mask: true
        icon.source: "qrc:/images/tray-icon.png"
    }
    \endcode
*/
QQuickLabsPlatformIcon QQuickLabsPlatformSystemTrayIcon::icon() const
{
    if (!m_iconLoader)
        return QQuickLabsPlatformIcon();

    return m_iconLoader->icon();
}

void QQuickLabsPlatformSystemTrayIcon::setIcon(const QQuickLabsPlatformIcon &icon)
{
    if (iconLoader()->icon() == icon)
        return;

    iconLoader()->setIcon(icon);
    emit iconChanged();
}

/*!
    \qmlmethod void Qt.labs.platform::SystemTrayIcon::show()

    Shows the system tray icon.
*/
void QQuickLabsPlatformSystemTrayIcon::show()
{
    setVisible(true);
}

/*!
    \qmlmethod void Qt.labs.platform::SystemTrayIcon::hide()

    Hides the system tray icon.
*/
void QQuickLabsPlatformSystemTrayIcon::hide()
{
    setVisible(false);
}

/*!
    \qmlmethod void Qt.labs.platform::SystemTrayIcon::showMessage(string title, string message, MessageIcon icon, int msecs)

    Shows a system tray message with the given \a title, \a message and \a icon
    for the time specified in \a msecs.

    \note System tray messages are dependent on the system configuration and user preferences,
    and may not appear at all. Therefore, it should not be relied upon as the sole means for providing
    critical information.

    \sa supportsMessages, messageClicked()
*/
void QQuickLabsPlatformSystemTrayIcon::showMessage(const QString &title, const QString &msg, QPlatformSystemTrayIcon::MessageIcon icon, int msecs)
{
    if (m_handle)
        m_handle->showMessage(title, msg, QIcon(), icon, msecs);
}

void QQuickLabsPlatformSystemTrayIcon::init()
{
    if (!m_handle)
        return;

    m_handle->init();
    if (m_menu && m_menu->create())
        m_handle->updateMenu(m_menu->handle());
    m_handle->updateToolTip(m_tooltip);
    if (m_iconLoader)
        m_iconLoader->setEnabled(true);
}

void QQuickLabsPlatformSystemTrayIcon::cleanup()
{
    if (m_handle)
        m_handle->cleanup();
    if (m_iconLoader)
        m_iconLoader->setEnabled(false);
}

void QQuickLabsPlatformSystemTrayIcon::classBegin()
{
}

void QQuickLabsPlatformSystemTrayIcon::componentComplete()
{
    m_complete = true;
    if (m_visible)
        init();
}

QQuickLabsPlatformIconLoader *QQuickLabsPlatformSystemTrayIcon::iconLoader() const
{
    if (!m_iconLoader) {
        QQuickLabsPlatformSystemTrayIcon *that = const_cast<QQuickLabsPlatformSystemTrayIcon *>(this);
        static int slot = staticMetaObject.indexOfSlot("updateIcon()");
        m_iconLoader = new QQuickLabsPlatformIconLoader(slot, that);
        m_iconLoader->setEnabled(m_complete);
    }
    return m_iconLoader;
}

void QQuickLabsPlatformSystemTrayIcon::updateIcon()
{
    if (!m_handle || !m_iconLoader)
        return;

    const QRect oldGeometry = m_handle->geometry();

    m_handle->updateIcon(m_iconLoader->toQIcon());

    if (oldGeometry != m_handle->geometry())
        emit geometryChanged();
}

QT_END_NAMESPACE

#include "moc_qquicklabsplatformsystemtrayicon_p.cpp"
