1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com>
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui 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 "qdbusmenubar_p.h"
41 #include "qdbusmenuregistrarproxy_p.h"
42 
43 QT_BEGIN_NAMESPACE
44 
45 /* note: do not change these to QStringLiteral;
46    we are unloaded before QtDBus is done using the strings.
47  */
48 #define REGISTRAR_SERVICE QLatin1String("com.canonical.AppMenu.Registrar")
49 #define REGISTRAR_PATH QLatin1String("/com/canonical/AppMenu/Registrar")
50 
QDBusMenuBar()51 QDBusMenuBar::QDBusMenuBar()
52     : QPlatformMenuBar()
53     , m_menu(new QDBusPlatformMenu())
54     , m_menuAdaptor(new QDBusMenuAdaptor(m_menu))
55     , m_windowId(0)
56 {
57     QDBusMenuItem::registerDBusTypes();
58     connect(m_menu, &QDBusPlatformMenu::propertiesUpdated,
59             m_menuAdaptor, &QDBusMenuAdaptor::ItemsPropertiesUpdated);
60     connect(m_menu, &QDBusPlatformMenu::updated,
61             m_menuAdaptor, &QDBusMenuAdaptor::LayoutUpdated);
62     connect(m_menu, &QDBusPlatformMenu::popupRequested,
63             m_menuAdaptor, &QDBusMenuAdaptor::ItemActivationRequested);
64 }
65 
~QDBusMenuBar()66 QDBusMenuBar::~QDBusMenuBar()
67 {
68     unregisterMenuBar();
69     delete m_menuAdaptor;
70     delete m_menu;
71     qDeleteAll(m_menuItems);
72 }
73 
menuItemForMenu(QPlatformMenu * menu)74 QDBusPlatformMenuItem *QDBusMenuBar::menuItemForMenu(QPlatformMenu *menu)
75 {
76     if (!menu)
77         return nullptr;
78     quintptr tag = menu->tag();
79     const auto it = m_menuItems.constFind(tag);
80     if (it != m_menuItems.cend()) {
81         return *it;
82     } else {
83         QDBusPlatformMenuItem *item = new QDBusPlatformMenuItem;
84         updateMenuItem(item, menu);
85         m_menuItems.insert(tag, item);
86         return item;
87     }
88 }
89 
updateMenuItem(QDBusPlatformMenuItem * item,QPlatformMenu * menu)90 void QDBusMenuBar::updateMenuItem(QDBusPlatformMenuItem *item, QPlatformMenu *menu)
91 {
92     const QDBusPlatformMenu *ourMenu = qobject_cast<const QDBusPlatformMenu *>(menu);
93     item->setText(ourMenu->text());
94     item->setIcon(ourMenu->icon());
95     item->setEnabled(ourMenu->isEnabled());
96     item->setVisible(ourMenu->isVisible());
97     item->setMenu(menu);
98 }
99 
insertMenu(QPlatformMenu * menu,QPlatformMenu * before)100 void QDBusMenuBar::insertMenu(QPlatformMenu *menu, QPlatformMenu *before)
101 {
102     QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
103     QDBusPlatformMenuItem *beforeItem = menuItemForMenu(before);
104     m_menu->insertMenuItem(menuItem, beforeItem);
105     m_menu->emitUpdated();
106 }
107 
removeMenu(QPlatformMenu * menu)108 void QDBusMenuBar::removeMenu(QPlatformMenu *menu)
109 {
110     QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
111     m_menu->removeMenuItem(menuItem);
112     m_menu->emitUpdated();
113 }
114 
syncMenu(QPlatformMenu * menu)115 void QDBusMenuBar::syncMenu(QPlatformMenu *menu)
116 {
117     QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
118     updateMenuItem(menuItem, menu);
119 }
120 
handleReparent(QWindow * newParentWindow)121 void QDBusMenuBar::handleReparent(QWindow *newParentWindow)
122 {
123     if (newParentWindow) {
124         unregisterMenuBar();
125         m_windowId = newParentWindow->winId();
126         registerMenuBar();
127     }
128 }
129 
menuForTag(quintptr tag) const130 QPlatformMenu *QDBusMenuBar::menuForTag(quintptr tag) const
131 {
132     QDBusPlatformMenuItem *menuItem = m_menuItems.value(tag);
133     if (menuItem)
134         return const_cast<QPlatformMenu *>(menuItem->menu());
135     return nullptr;
136 }
137 
createMenu() const138 QPlatformMenu *QDBusMenuBar::createMenu() const
139 {
140     return new QDBusPlatformMenu;
141 }
142 
registerMenuBar()143 void QDBusMenuBar::registerMenuBar()
144 {
145     static uint menuBarId = 0;
146 
147     QDBusConnection connection = QDBusConnection::sessionBus();
148     m_objectPath = QStringLiteral("/MenuBar/%1").arg(++menuBarId);
149     if (!connection.registerObject(m_objectPath, m_menu))
150         return;
151 
152     QDBusMenuRegistrarInterface registrar(REGISTRAR_SERVICE, REGISTRAR_PATH, connection, this);
153     QDBusPendingReply<> r = registrar.RegisterWindow(m_windowId, QDBusObjectPath(m_objectPath));
154     r.waitForFinished();
155     if (r.isError()) {
156         qWarning("Failed to register window menu, reason: %s (\"%s\")",
157                  qUtf8Printable(r.error().name()), qUtf8Printable(r.error().message()));
158         connection.unregisterObject(m_objectPath);
159     }
160 }
161 
unregisterMenuBar()162 void QDBusMenuBar::unregisterMenuBar()
163 {
164     QDBusConnection connection = QDBusConnection::sessionBus();
165 
166     if (m_windowId) {
167         QDBusMenuRegistrarInterface registrar(REGISTRAR_SERVICE, REGISTRAR_PATH, connection, this);
168         QDBusPendingReply<> r = registrar.UnregisterWindow(m_windowId);
169         r.waitForFinished();
170         if (r.isError())
171             qWarning("Failed to unregister window menu, reason: %s (\"%s\")",
172                      qUtf8Printable(r.error().name()), qUtf8Printable(r.error().message()));
173     }
174 
175     if (!m_objectPath.isEmpty())
176         connection.unregisterObject(m_objectPath);
177 }
178 
179 QT_END_NAMESPACE
180