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 "qdbusmenutypes_p.h"
5
6#include <QDBusConnection>
7#include <QDBusMetaType>
8#include <QImage>
9#include <QIcon>
10#include <QImage>
11#include <QPixmap>
12#include <QDebug>
13#include <QtEndian>
14#include <QBuffer>
15#if QT_CONFIG(shortcut)
16# include <private/qkeysequence_p.h>
17#endif
18#include <qpa/qplatformmenu.h>
19#include "qdbusplatformmenu_p.h"
20
21QT_BEGIN_NAMESPACE
22
23using namespace Qt::StringLiterals;
24
25QT_IMPL_METATYPE_EXTERN(QDBusMenuItem)
26QT_IMPL_METATYPE_EXTERN(QDBusMenuItemList)
27QT_IMPL_METATYPE_EXTERN(QDBusMenuItemKeys)
28QT_IMPL_METATYPE_EXTERN(QDBusMenuItemKeysList)
29QT_IMPL_METATYPE_EXTERN(QDBusMenuLayoutItem)
30QT_IMPL_METATYPE_EXTERN(QDBusMenuLayoutItemList)
31QT_IMPL_METATYPE_EXTERN(QDBusMenuEvent)
32QT_IMPL_METATYPE_EXTERN(QDBusMenuEventList)
33QT_IMPL_METATYPE_EXTERN(QDBusMenuShortcut)
34
35const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuItem &item)
36{
37 arg.beginStructure();
38 arg << item.m_id << item.m_properties;
39 arg.endStructure();
40 return arg;
41}
42
43const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuItem &item)
44{
45 arg.beginStructure();
46 arg >> item.m_id >> item.m_properties;
47 arg.endStructure();
48 return arg;
49}
50
51const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuItemKeys &keys)
52{
53 arg.beginStructure();
54 arg << keys.id << keys.properties;
55 arg.endStructure();
56 return arg;
57}
58
59const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuItemKeys &keys)
60{
61 arg.beginStructure();
62 arg >> keys.id >> keys.properties;
63 arg.endStructure();
64 return arg;
65}
66
67uint QDBusMenuLayoutItem::populate(int id, int depth, const QStringList &propertyNames, const QDBusPlatformMenu *topLevelMenu)
68{
69 qCDebug(qLcMenu) << id << "depth" << depth << propertyNames;
70 m_id = id;
71 if (id == 0) {
72 m_properties.insert(key: "children-display"_L1, value: "submenu"_L1);
73 if (topLevelMenu)
74 populate(menu: topLevelMenu, depth, propertyNames);
75 return 1; // revision
76 }
77
78 QDBusPlatformMenuItem *item = QDBusPlatformMenuItem::byId(id);
79 if (item) {
80 const QDBusPlatformMenu *menu = static_cast<const QDBusPlatformMenu *>(item->menu());
81
82 if (menu) {
83 if (depth != 0)
84 populate(menu, depth, propertyNames);
85 return menu->revision();
86 }
87 }
88
89 return 1; // revision
90}
91
92void QDBusMenuLayoutItem::populate(const QDBusPlatformMenu *menu, int depth, const QStringList &propertyNames)
93{
94 const auto items = menu->items();
95 for (QDBusPlatformMenuItem *item : items) {
96 QDBusMenuLayoutItem child;
97 child.populate(item, depth: depth - 1, propertyNames);
98 m_children << child;
99 }
100}
101
102void QDBusMenuLayoutItem::populate(const QDBusPlatformMenuItem *item, int depth, const QStringList &propertyNames)
103{
104 m_id = item->dbusID();
105 QDBusMenuItem proxy(item);
106 m_properties = proxy.m_properties;
107
108 const QDBusPlatformMenu *menu = static_cast<const QDBusPlatformMenu *>(item->menu());
109 if (depth != 0 && menu)
110 populate(menu, depth, propertyNames);
111}
112
113const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuLayoutItem &item)
114{
115 arg.beginStructure();
116 arg << item.m_id << item.m_properties;
117 arg.beginArray(elementMetaTypeId: qMetaTypeId<QDBusVariant>());
118 for (const QDBusMenuLayoutItem &child : item.m_children)
119 arg << QDBusVariant(QVariant::fromValue<QDBusMenuLayoutItem>(value: child));
120 arg.endArray();
121 arg.endStructure();
122 return arg;
123}
124
125const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuLayoutItem &item)
126{
127 arg.beginStructure();
128 arg >> item.m_id >> item.m_properties;
129 arg.beginArray();
130 while (!arg.atEnd()) {
131 QDBusVariant dbusVariant;
132 arg >> dbusVariant;
133 QDBusArgument childArgument = qvariant_cast<QDBusArgument>(v: dbusVariant.variant());
134
135 QDBusMenuLayoutItem child;
136 childArgument >> child;
137 item.m_children.append(t: child);
138 }
139 arg.endArray();
140 arg.endStructure();
141 return arg;
142}
143
144void QDBusMenuItem::registerDBusTypes()
145{
146 qDBusRegisterMetaType<QDBusMenuItem>();
147 qDBusRegisterMetaType<QDBusMenuItemList>();
148 qDBusRegisterMetaType<QDBusMenuItemKeys>();
149 qDBusRegisterMetaType<QDBusMenuItemKeysList>();
150 qDBusRegisterMetaType<QDBusMenuLayoutItem>();
151 qDBusRegisterMetaType<QDBusMenuLayoutItemList>();
152 qDBusRegisterMetaType<QDBusMenuEvent>();
153 qDBusRegisterMetaType<QDBusMenuEventList>();
154 qDBusRegisterMetaType<QDBusMenuShortcut>();
155}
156
157QDBusMenuItem::QDBusMenuItem(const QDBusPlatformMenuItem *item)
158 : m_id(item->dbusID())
159{
160 if (item->isSeparator()) {
161 m_properties.insert(key: "type"_L1, value: "separator"_L1);
162 } else {
163 m_properties.insert(key: "label"_L1, value: convertMnemonic(label: item->text()));
164 if (item->menu())
165 m_properties.insert(key: "children-display"_L1, value: "submenu"_L1);
166 m_properties.insert(key: "enabled"_L1, value: item->isEnabled());
167 if (item->isCheckable()) {
168 QString toggleType = item->hasExclusiveGroup() ? "radio"_L1 : "checkmark"_L1;
169 m_properties.insert(key: "toggle-type"_L1, value: toggleType);
170 m_properties.insert(key: "toggle-state"_L1, value: item->isChecked() ? 1 : 0);
171 }
172#ifndef QT_NO_SHORTCUT
173 const QKeySequence &scut = item->shortcut();
174 if (!scut.isEmpty()) {
175 QDBusMenuShortcut shortcut = convertKeySequence(sequence: scut);
176 m_properties.insert(key: "shortcut"_L1, value: QVariant::fromValue(value: shortcut));
177 }
178#endif
179 const QIcon &icon = item->icon();
180 if (!icon.name().isEmpty()) {
181 m_properties.insert(key: "icon-name"_L1, value: icon.name());
182 } else if (!icon.isNull()) {
183 QBuffer buf;
184 icon.pixmap(extent: 16).save(device: &buf, format: "PNG");
185 m_properties.insert(key: "icon-data"_L1, value: buf.data());
186 }
187 }
188 m_properties.insert(key: "visible"_L1, value: item->isVisible());
189}
190
191QDBusMenuItemList QDBusMenuItem::items(const QList<int> &ids, const QStringList &propertyNames)
192{
193 Q_UNUSED(propertyNames);
194 QDBusMenuItemList ret;
195 const QList<const QDBusPlatformMenuItem *> items = QDBusPlatformMenuItem::byIds(ids);
196 ret.reserve(asize: items.size());
197 for (const QDBusPlatformMenuItem *item : items)
198 ret << QDBusMenuItem(item);
199 return ret;
200}
201
202QString QDBusMenuItem::convertMnemonic(const QString &label)
203{
204 // convert only the first occurrence of ampersand which is not at the end
205 // dbusmenu uses underscore instead of ampersand
206 int idx = label.indexOf(c: u'&');
207 if (idx < 0 || idx == label.size() - 1)
208 return label;
209 QString ret(label);
210 ret[idx] = u'_';
211 return ret;
212}
213
214#ifndef QT_NO_SHORTCUT
215QDBusMenuShortcut QDBusMenuItem::convertKeySequence(const QKeySequence &sequence)
216{
217 QDBusMenuShortcut shortcut;
218 for (int i = 0; i < sequence.count(); ++i) {
219 QStringList tokens;
220 int key = sequence[i].toCombined();
221 if (key & Qt::MetaModifier)
222 tokens << QStringLiteral("Super");
223 if (key & Qt::ControlModifier)
224 tokens << QStringLiteral("Control");
225 if (key & Qt::AltModifier)
226 tokens << QStringLiteral("Alt");
227 if (key & Qt::ShiftModifier)
228 tokens << QStringLiteral("Shift");
229 if (key & Qt::KeypadModifier)
230 tokens << QStringLiteral("Num");
231
232 QString keyName = QKeySequencePrivate::keyName(key, format: QKeySequence::PortableText);
233 if (keyName == "+"_L1)
234 tokens << QStringLiteral("plus");
235 else if (keyName == "-"_L1)
236 tokens << QStringLiteral("minus");
237 else
238 tokens << keyName;
239 shortcut << tokens;
240 }
241 return shortcut;
242}
243#endif
244
245const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuEvent &ev)
246{
247 arg.beginStructure();
248 arg << ev.m_id << ev.m_eventId << ev.m_data << ev.m_timestamp;
249 arg.endStructure();
250 return arg;
251}
252
253const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuEvent &ev)
254{
255 arg.beginStructure();
256 arg >> ev.m_id >> ev.m_eventId >> ev.m_data >> ev.m_timestamp;
257 arg.endStructure();
258 return arg;
259}
260
261#ifndef QT_NO_DEBUG_STREAM
262QDebug operator<<(QDebug d, const QDBusMenuItem &item)
263{
264 QDebugStateSaver saver(d);
265 d.nospace();
266 d << "QDBusMenuItem(id=" << item.m_id << ", properties=" << item.m_properties << ')';
267 return d;
268}
269
270QDebug operator<<(QDebug d, const QDBusMenuLayoutItem &item)
271{
272 QDebugStateSaver saver(d);
273 d.nospace();
274 d << "QDBusMenuLayoutItem(id=" << item.m_id << ", properties=" << item.m_properties << ", " << item.m_children.size() << " children)";
275 return d;
276}
277#endif
278
279QT_END_NAMESPACE
280

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