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 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 "qdbusplatformmenu_p.h"
41
42#include <QDateTime>
43#include <QDebug>
44#include <QWindow>
45
46QT_BEGIN_NAMESPACE
47
48Q_LOGGING_CATEGORY(qLcMenu, "qt.qpa.menu")
49
50static int nextDBusID = 1;
51QHash<int, QDBusPlatformMenuItem *> menuItemsByID;
52
53QDBusPlatformMenuItem::QDBusPlatformMenuItem()
54 : m_subMenu(nullptr)
55 , m_role(NoRole)
56 , m_isEnabled(true)
57 , m_isVisible(true)
58 , m_isSeparator(false)
59 , m_isCheckable(false)
60 , m_isChecked(false)
61 , m_hasExclusiveGroup(false)
62 , m_dbusID(nextDBusID++)
63{
64 menuItemsByID.insert(m_dbusID, this);
65}
66
67QDBusPlatformMenuItem::~QDBusPlatformMenuItem()
68{
69 menuItemsByID.remove(m_dbusID);
70 if (m_subMenu)
71 static_cast<QDBusPlatformMenu *>(m_subMenu)->setContainingMenuItem(nullptr);
72}
73
74void QDBusPlatformMenuItem::setText(const QString &text)
75{
76 qCDebug(qLcMenu) << m_dbusID << text;
77 m_text = text;
78}
79
80void QDBusPlatformMenuItem::setIcon(const QIcon &icon)
81{
82 m_icon = icon;
83}
84
85/*!
86 Set a submenu under this menu item.
87*/
88void QDBusPlatformMenuItem::setMenu(QPlatformMenu *menu)
89{
90 if (m_subMenu)
91 static_cast<QDBusPlatformMenu *>(m_subMenu)->setContainingMenuItem(nullptr);
92 m_subMenu = menu;
93 if (menu)
94 static_cast<QDBusPlatformMenu *>(menu)->setContainingMenuItem(this);
95}
96
97void QDBusPlatformMenuItem::setEnabled(bool enabled)
98{
99 m_isEnabled = enabled;
100}
101
102void QDBusPlatformMenuItem::setVisible(bool isVisible)
103{
104 m_isVisible = isVisible;
105}
106
107void QDBusPlatformMenuItem::setIsSeparator(bool isSeparator)
108{
109 m_isSeparator = isSeparator;
110}
111
112void QDBusPlatformMenuItem::setRole(QPlatformMenuItem::MenuRole role)
113{
114 m_role = role;
115}
116
117void QDBusPlatformMenuItem::setCheckable(bool checkable)
118{
119 m_isCheckable = checkable;
120}
121
122void QDBusPlatformMenuItem::setChecked(bool isChecked)
123{
124 m_isChecked = isChecked;
125}
126
127void QDBusPlatformMenuItem::setHasExclusiveGroup(bool hasExclusiveGroup)
128{
129 m_hasExclusiveGroup = hasExclusiveGroup;
130}
131
132#ifndef QT_NO_SHORTCUT
133void QDBusPlatformMenuItem::setShortcut(const QKeySequence &shortcut)
134{
135 m_shortcut = shortcut;
136}
137#endif
138
139void QDBusPlatformMenuItem::trigger()
140{
141 emit activated();
142}
143
144QDBusPlatformMenuItem *QDBusPlatformMenuItem::byId(int id)
145{
146 // We need to check contains because otherwise QHash would insert
147 // a default-constructed nullptr value into menuItemsByID
148 if (menuItemsByID.contains(id))
149 return menuItemsByID[id];
150 return nullptr;
151}
152
153QList<const QDBusPlatformMenuItem *> QDBusPlatformMenuItem::byIds(const QList<int> &ids)
154{
155 QList<const QDBusPlatformMenuItem *> ret;
156 for (int id : ids) {
157 if (menuItemsByID.contains(id))
158 ret << menuItemsByID[id];
159 }
160 return ret;
161}
162
163
164QDBusPlatformMenu::QDBusPlatformMenu()
165 : m_isEnabled(true)
166 , m_isVisible(true)
167 , m_revision(1)
168 , m_containingMenuItem(nullptr)
169{
170}
171
172QDBusPlatformMenu::~QDBusPlatformMenu()
173{
174 if (m_containingMenuItem)
175 m_containingMenuItem->setMenu(nullptr);
176}
177
178void QDBusPlatformMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
179{
180 QDBusPlatformMenuItem *item = static_cast<QDBusPlatformMenuItem *>(menuItem);
181 QDBusPlatformMenuItem *beforeItem = static_cast<QDBusPlatformMenuItem *>(before);
182 int idx = m_items.indexOf(beforeItem);
183 qCDebug(qLcMenu) << item->dbusID() << item->text();
184 if (idx < 0)
185 m_items.append(item);
186 else
187 m_items.insert(idx, item);
188 m_itemsByTag.insert(item->tag(), item);
189 if (item->menu())
190 syncSubMenu(static_cast<const QDBusPlatformMenu *>(item->menu()));
191 emitUpdated();
192}
193
194void QDBusPlatformMenu::removeMenuItem(QPlatformMenuItem *menuItem)
195{
196 QDBusPlatformMenuItem *item = static_cast<QDBusPlatformMenuItem *>(menuItem);
197 m_items.removeAll(item);
198 m_itemsByTag.remove(menuItem->tag());
199 if (item->menu()) {
200 // disconnect from the signals we connected to in syncSubMenu()
201 const QDBusPlatformMenu *menu = static_cast<const QDBusPlatformMenu *>(item->menu());
202 disconnect(menu, &QDBusPlatformMenu::propertiesUpdated,
203 this, &QDBusPlatformMenu::propertiesUpdated);
204 disconnect(menu, &QDBusPlatformMenu::updated,
205 this, &QDBusPlatformMenu::updated);
206 disconnect(menu, &QDBusPlatformMenu::popupRequested,
207 this, &QDBusPlatformMenu::popupRequested);
208 }
209 emitUpdated();
210}
211
212void QDBusPlatformMenu::syncSubMenu(const QDBusPlatformMenu *menu)
213{
214 // The adaptor is only connected to the propertiesUpdated signal of the top-level
215 // menu, so the submenus should transfer their signals to their parents.
216 connect(menu, &QDBusPlatformMenu::propertiesUpdated,
217 this, &QDBusPlatformMenu::propertiesUpdated, Qt::UniqueConnection);
218 connect(menu, &QDBusPlatformMenu::updated,
219 this, &QDBusPlatformMenu::updated, Qt::UniqueConnection);
220 connect(menu, &QDBusPlatformMenu::popupRequested,
221 this, &QDBusPlatformMenu::popupRequested, Qt::UniqueConnection);
222}
223
224void QDBusPlatformMenu::syncMenuItem(QPlatformMenuItem *menuItem)
225{
226 QDBusPlatformMenuItem *item = static_cast<QDBusPlatformMenuItem *>(menuItem);
227 // if a submenu was added to this item, we need to connect to its signals
228 if (item->menu())
229 syncSubMenu(static_cast<const QDBusPlatformMenu *>(item->menu()));
230 // TODO keep around copies of the QDBusMenuLayoutItems so they can be updated?
231 // or eliminate them by putting dbus streaming operators in this class instead?
232 // or somehow tell the dbusmenu client that something has changed, so it will ask for properties again
233 QDBusMenuItemList updated;
234 QDBusMenuItemKeysList removed;
235 updated << QDBusMenuItem(item);
236 qCDebug(qLcMenu) << updated;
237 emit propertiesUpdated(updated, removed);
238}
239
240void QDBusPlatformMenu::emitUpdated()
241{
242 if (m_containingMenuItem)
243 emit updated(++m_revision, m_containingMenuItem->dbusID());
244 else
245 emit updated(++m_revision, 0);
246}
247
248void QDBusPlatformMenu::setText(const QString &text)
249{
250 m_text = text;
251}
252
253void QDBusPlatformMenu::setIcon(const QIcon &icon)
254{
255 m_icon = icon;
256}
257
258void QDBusPlatformMenu::setEnabled(bool enabled)
259{
260 m_isEnabled = enabled;
261}
262
263void QDBusPlatformMenu::setVisible(bool isVisible)
264{
265 m_isVisible = isVisible;
266}
267
268void QDBusPlatformMenu::setContainingMenuItem(QDBusPlatformMenuItem *item)
269{
270 m_containingMenuItem = item;
271}
272
273void QDBusPlatformMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item)
274{
275 Q_UNUSED(parentWindow);
276 Q_UNUSED(targetRect);
277 Q_UNUSED(item);
278 setVisible(true);
279 emit popupRequested(m_containingMenuItem->dbusID(), QDateTime::currentMSecsSinceEpoch());
280}
281
282QPlatformMenuItem *QDBusPlatformMenu::menuItemAt(int position) const
283{
284 return m_items.value(position);
285}
286
287QPlatformMenuItem *QDBusPlatformMenu::menuItemForTag(quintptr tag) const
288{
289 return m_itemsByTag[tag];
290}
291
292const QList<QDBusPlatformMenuItem *> QDBusPlatformMenu::items() const
293{
294 return m_items;
295}
296
297QPlatformMenuItem *QDBusPlatformMenu::createMenuItem() const
298{
299 QDBusPlatformMenuItem *ret = new QDBusPlatformMenuItem();
300 return ret;
301}
302
303QPlatformMenu *QDBusPlatformMenu::createSubMenu() const
304{
305 return new QDBusPlatformMenu;
306}
307
308QT_END_NAMESPACE
309