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 | |
21 | QT_BEGIN_NAMESPACE |
22 | |
23 | using namespace Qt::StringLiterals; |
24 | |
25 | QT_IMPL_METATYPE_EXTERN(QDBusMenuItem) |
26 | QT_IMPL_METATYPE_EXTERN(QDBusMenuItemList) |
27 | QT_IMPL_METATYPE_EXTERN(QDBusMenuItemKeys) |
28 | QT_IMPL_METATYPE_EXTERN(QDBusMenuItemKeysList) |
29 | QT_IMPL_METATYPE_EXTERN(QDBusMenuLayoutItem) |
30 | QT_IMPL_METATYPE_EXTERN(QDBusMenuLayoutItemList) |
31 | QT_IMPL_METATYPE_EXTERN(QDBusMenuEvent) |
32 | QT_IMPL_METATYPE_EXTERN(QDBusMenuEventList) |
33 | QT_IMPL_METATYPE_EXTERN(QDBusMenuShortcut) |
34 | |
35 | const QDBusArgument &(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 | |
43 | const QDBusArgument &(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 | |
51 | const QDBusArgument &(QDBusArgument &arg, const QDBusMenuItemKeys &keys) |
52 | { |
53 | arg.beginStructure(); |
54 | arg << keys.id << keys.properties; |
55 | arg.endStructure(); |
56 | return arg; |
57 | } |
58 | |
59 | const QDBusArgument &(const QDBusArgument &arg, QDBusMenuItemKeys &keys) |
60 | { |
61 | arg.beginStructure(); |
62 | arg >> keys.id >> keys.properties; |
63 | arg.endStructure(); |
64 | return arg; |
65 | } |
66 | |
67 | uint QDBusMenuLayoutItem::(int id, int depth, const QStringList &propertyNames, const QDBusPlatformMenu *) |
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 * = 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 | |
92 | void QDBusMenuLayoutItem::(const QDBusPlatformMenu *, 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 | |
102 | void QDBusMenuLayoutItem::(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 * = static_cast<const QDBusPlatformMenu *>(item->menu()); |
109 | if (depth != 0 && menu) |
110 | populate(menu, depth, propertyNames); |
111 | } |
112 | |
113 | const QDBusArgument &(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 | |
125 | const QDBusArgument &(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 | |
144 | void QDBusMenuItem::() |
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 | |
157 | 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 | |
191 | QDBusMenuItemList QDBusMenuItem::(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 | |
202 | QString QDBusMenuItem::(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 |
215 | QDBusMenuShortcut QDBusMenuItem::(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 | |
245 | const QDBusArgument &(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 | |
253 | const QDBusArgument &(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 |
262 | QDebug (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 | |
270 | QDebug (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 | |
279 | QT_END_NAMESPACE |
280 | |