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

source code of qtbase/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp