1 /*
2 * vala-panel-appmenu
3 * Copyright (C) 2018 Konstantin Pugin <ria.freelander@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdbool.h>
20
21 #include "dbusmenu-interface.h"
22 #include "definitions.h"
23 #include "item.h"
24 #include "utils.h"
25
26 #define ITEM_MAGIC 0xDEADBEEF
27 #define item_set_magic(item) (item)->magic = GUINT_TO_POINTER(ITEM_MAGIC)
28 #define item_check_magic(item) (GPOINTER_TO_UINT((item)->magic) == ITEM_MAGIC)
29
30 #define submenu_str(en) ((en) ? G_MENU_LINK_SUBMENU : DBUS_MENU_DISABLED_SUBMENU)
31
32 G_GNUC_INTERNAL void dbus_menu_item_free(gpointer data);
33 G_GNUC_INTERNAL DBusMenuItem *dbus_menu_item_copy(DBusMenuItem *src);
34 G_DEFINE_BOXED_TYPE(DBusMenuItem, dbus_menu_item, dbus_menu_item_copy, dbus_menu_item_free)
35 #if 0
36 #include "item-pixbuf.c"
37 #endif
38
39 static void act_props_try_update(DBusMenuItem *item);
40
dbus_menu_item_new_first_section(u_int32_t id,GActionGroup * action_group)41 G_GNUC_INTERNAL DBusMenuItem *dbus_menu_item_new_first_section(u_int32_t id,
42 GActionGroup *action_group)
43 {
44 DBusMenuItem *item = g_slice_new0(DBusMenuItem);
45 item->id = id;
46 item->action_type = DBUS_MENU_ACTION_SECTION;
47 item->enabled = false;
48 item->toggled = false;
49 item->attrs =
50 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
51 item->links = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref);
52 item->ref_action_group = action_group;
53 item_set_magic(item);
54 return item;
55 }
56
dbus_menu_item_new(u_int32_t id,DBusMenuModel * parent_model,GVariant * props)57 G_GNUC_INTERNAL DBusMenuItem *dbus_menu_item_new(u_int32_t id, DBusMenuModel *parent_model,
58 GVariant *props)
59 {
60 DBusMenuItem *item = g_slice_new0(DBusMenuItem);
61 DBusMenuXml *xml;
62 GVariantIter iter;
63 const char *prop;
64 GVariant *value;
65 item_set_magic(item);
66 item->enabled = true;
67 item->toggled = false;
68 item->id = id;
69 item->attrs =
70 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
71 item->links = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref);
72 g_object_get(parent_model, "action-group", &item->ref_action_group, "xml", &xml, NULL);
73 g_variant_iter_init(&iter, props);
74 // Iterate by immutable properties, it is construct_only
75 bool action_creator_found = false;
76 while (g_variant_iter_loop(&iter, "{&sv}", &prop, &value))
77 {
78 if (g_strcmp0(prop, DBUS_MENU_PROP_CHILDREN_DISPLAY) == 0)
79 {
80 if (value == NULL)
81 {
82 g_hash_table_remove(item->attrs, G_MENU_ATTRIBUTE_SUBMENU_ACTION);
83 continue;
84 }
85 else if (g_strcmp0(g_variant_get_string(value, NULL),
86 DBUS_MENU_CHILDREN_DISPLAY_SUBMENU) == 0)
87 {
88 item->action_type = DBUS_MENU_ACTION_SUBMENU;
89 g_autofree char *name =
90 dbus_menu_action_get_name(id, item->action_type, true);
91 g_hash_table_insert(item->attrs,
92 g_strdup(G_MENU_ATTRIBUTE_SUBMENU_ACTION),
93 g_variant_new_string(name));
94 action_creator_found = true;
95 }
96 }
97 else if (g_strcmp0(prop, DBUS_MENU_PROP_TOGGLE_TYPE) == 0)
98 {
99 g_autofree char *name =
100 dbus_menu_action_get_name(id, item->action_type, true);
101 if (g_strcmp0(g_variant_get_string(value, NULL),
102 DBUS_MENU_TOGGLE_TYPE_CHECK) == 0)
103 {
104 item->action_type = DBUS_MENU_ACTION_CHECKMARK;
105 g_hash_table_insert(item->attrs,
106 g_strdup(G_MENU_ATTRIBUTE_ACTION),
107 g_variant_new_string(name));
108 action_creator_found = true;
109 }
110 else if (g_strcmp0(g_variant_get_string(value, NULL),
111 DBUS_MENU_TOGGLE_TYPE_RADIO) == 0)
112 {
113 item->action_type = DBUS_MENU_ACTION_RADIO;
114 g_hash_table_insert(item->attrs,
115 g_strdup(G_MENU_ATTRIBUTE_ACTION),
116 g_variant_new_string(name));
117 GVariant *vstr =
118 g_variant_new_string(DBUS_MENU_ACTION_RADIO_SELECTED);
119 g_hash_table_insert(item->attrs,
120 g_strdup(G_MENU_ATTRIBUTE_TARGET),
121 g_variant_ref_sink(vstr));
122 action_creator_found = true;
123 }
124 }
125 else if (g_strcmp0(prop, DBUS_MENU_PROP_TYPE) == 0)
126 {
127 const char *type = g_variant_get_string(value, NULL);
128 if (!g_strcmp0(type, DBUS_MENU_TYPE_SEPARATOR))
129 {
130 item->action_type = DBUS_MENU_ACTION_SECTION;
131 action_creator_found = true;
132 }
133 else if (!g_strcmp0(type, DBUS_MENU_TYPE_NORMAL))
134 {
135 item->action_type = DBUS_MENU_ACTION_NORMAL;
136 g_autofree char *name =
137 dbus_menu_action_get_name(id, item->action_type, true);
138 g_hash_table_insert(item->attrs,
139 g_strdup(G_MENU_ATTRIBUTE_ACTION),
140 g_variant_new_string(name));
141 action_creator_found = true;
142 }
143 }
144 else if (g_strcmp0(prop, "x-kde-title") == 0)
145 {
146 item->action_type = DBUS_MENU_ACTION_SECTION;
147 g_hash_table_insert(item->attrs, g_strdup(G_MENU_ATTRIBUTE_LABEL), value);
148 action_creator_found = true;
149 }
150 else if (!action_creator_found)
151 {
152 item->action_type = DBUS_MENU_ACTION_NORMAL;
153 g_autofree char *name =
154 dbus_menu_action_get_name(id, item->action_type, true);
155 g_hash_table_insert(item->attrs,
156 g_strdup(G_MENU_ATTRIBUTE_ACTION),
157 g_variant_new_string(name));
158 action_creator_found = true;
159 }
160 }
161 if (item->action_type != DBUS_MENU_ACTION_SECTION)
162 g_hash_table_insert(item->attrs,
163 g_strdup(G_MENU_ATTRIBUTE_LABEL),
164 g_variant_new_string(""));
165 dbus_menu_item_update_props(item, props);
166 return item;
167 }
168
dbus_menu_item_free(gpointer data)169 G_GNUC_INTERNAL void dbus_menu_item_free(gpointer data)
170 {
171 DBusMenuItem *item = (DBusMenuItem *)data;
172 if (item == NULL)
173 return;
174 item->magic = NULL;
175 g_clear_pointer(&item->attrs, g_hash_table_destroy);
176 g_clear_pointer(&item->links, g_hash_table_destroy);
177 g_clear_object(&item->ref_action);
178 g_slice_free(DBusMenuItem, data);
179 }
180
dbus_menu_item_copy(DBusMenuItem * src)181 G_GNUC_INTERNAL DBusMenuItem *dbus_menu_item_copy(DBusMenuItem *src)
182 {
183 DBusMenuItem *dst = g_slice_new0(DBusMenuItem);
184 dst->id = src->id;
185 dst->action_type = src->action_type;
186 dst->enabled = src->enabled;
187 dst->toggled = src->toggled;
188 dst->ref_action = G_ACTION(g_object_ref(src->ref_action));
189 dst->ref_action_group = src->ref_action_group;
190 dst->attrs = g_hash_table_ref(src->attrs);
191 dst->links = g_hash_table_ref(src->links);
192 return dst;
193 }
194
attr_update_checked(DBusMenuItem * item,const char * key,GVariant * value)195 static bool attr_update_checked(DBusMenuItem *item, const char *key, GVariant *value)
196 {
197 GVariant *old = (GVariant *)g_hash_table_lookup(item->attrs, key);
198 bool are_equal = false;
199 if (old != NULL)
200 are_equal = g_variant_equal(old, value);
201 if (!are_equal)
202 {
203 g_hash_table_insert(item->attrs, g_strdup(key), g_variant_ref_sink(value));
204 return true;
205 }
206 return false;
207 }
208
dbus_menu_item_is_firefox_stub(DBusMenuItem * item)209 G_GNUC_INTERNAL bool dbus_menu_item_is_firefox_stub(DBusMenuItem *item)
210 {
211 const char *hidden_when =
212 (const char *)g_hash_table_lookup(item->attrs, G_MENU_ATTRIBUTE_HIDDEN_WHEN);
213 const char *action =
214 (const char *)g_hash_table_lookup(item->attrs, G_MENU_ATTRIBUTE_ACTION);
215 const char *label = (const char *)g_hash_table_lookup(item->attrs, G_MENU_ATTRIBUTE_LABEL);
216 if (!g_strcmp0(hidden_when, G_MENU_HIDDEN_WHEN_ACTION_MISSING) &&
217 !g_strcmp0(action, DBUS_MENU_DISABLED_ACTION) && !g_strcmp0(label, "Label Empty"))
218 return true;
219 return false;
220 }
221
dbus_menu_item_preload(DBusMenuItem * item)222 G_GNUC_INTERNAL void dbus_menu_item_preload(DBusMenuItem *item)
223 {
224 if (!item_check_magic(item))
225 return;
226 if (item->action_type != DBUS_MENU_ACTION_SUBMENU)
227 return;
228 int id;
229 DBusMenuXml *xml = NULL;
230 bool need_update;
231 DBusMenuModel *submenu =
232 DBUS_MENU_MODEL(g_hash_table_lookup(item->links, submenu_str(item->enabled)));
233 if (!submenu || !DBUS_MENU_IS_MODEL(submenu))
234 return;
235 g_object_get(submenu, "parent-id", &id, "xml", &xml, NULL);
236 if (!xml || !DBUS_MENU_IS_XML(xml))
237 return;
238 dbus_menu_xml_call_event_sync(xml,
239 id,
240 "opened",
241 g_variant_new("v", g_variant_new_int32(0)),
242 CURRENT_TIME,
243 NULL,
244 NULL);
245 dbus_menu_xml_call_about_to_show_sync(xml, id, (gboolean *)&need_update, NULL, NULL);
246 need_update = need_update || dbus_menu_model_is_layout_update_required(submenu);
247 if (need_update)
248 dbus_menu_model_update_layout(submenu);
249 }
250
dbus_menu_item_copy_attrs(DBusMenuItem * src,DBusMenuItem * dst)251 G_GNUC_INTERNAL bool dbus_menu_item_copy_attrs(DBusMenuItem *src, DBusMenuItem *dst)
252 {
253 GHashTableIter iter;
254 g_hash_table_iter_init(&iter, src->attrs);
255 bool is_updated = false;
256 char *key;
257 GVariant *value;
258 while (g_hash_table_iter_next(&iter, (void **)&key, (void **)&value))
259 {
260 is_updated = attr_update_checked(dst, key, value) || is_updated;
261 }
262 return is_updated;
263 }
264
dbus_menu_item_update_enabled(DBusMenuItem * item,bool enabled)265 G_GNUC_INTERNAL bool dbus_menu_item_update_enabled(DBusMenuItem *item, bool enabled)
266 {
267 bool updated = false;
268 if (item->action_type == DBUS_MENU_ACTION_SUBMENU && !item->toggled)
269 {
270 DBusMenuModel *submenu =
271 DBUS_MENU_MODEL(g_hash_table_lookup(item->links, submenu_str(item->enabled)));
272 if (item->enabled != enabled)
273 {
274 if (submenu != NULL)
275 {
276 g_object_ref(submenu);
277 g_hash_table_remove(item->links, submenu_str(item->enabled));
278 g_hash_table_insert(item->links, submenu_str(enabled), submenu);
279 }
280 if (enabled)
281 {
282 g_hash_table_remove(item->attrs, G_MENU_ATTRIBUTE_ACTION);
283 }
284 else
285 {
286 g_hash_table_insert(item->attrs,
287 g_strdup(G_MENU_ATTRIBUTE_ACTION),
288 g_variant_new_string(
289 DBUS_MENU_DISABLED_ACTION));
290 }
291 updated = true;
292 }
293 }
294 item->enabled = enabled;
295 act_props_try_update(item);
296 return updated;
297 }
298
act_props_try_update(DBusMenuItem * item)299 static void act_props_try_update(DBusMenuItem *item)
300 {
301 if (!G_IS_ACTION(item->ref_action))
302 return;
303 g_simple_action_set_enabled(G_SIMPLE_ACTION(item->ref_action), item->enabled);
304 if (item->action_type == DBUS_MENU_ACTION_RADIO)
305 {
306 dbus_menu_action_lock(item->ref_action);
307 g_action_change_state((item->ref_action),
308 g_variant_new_string(
309 item->toggled ? DBUS_MENU_ACTION_RADIO_SELECTED
310 : DBUS_MENU_ACTION_RADIO_UNSELECTED));
311 dbus_menu_action_unlock(item->ref_action);
312 }
313 else if (item->action_type == DBUS_MENU_ACTION_CHECKMARK)
314 {
315 dbus_menu_action_lock(item->ref_action);
316 g_action_change_state((item->ref_action), g_variant_new_boolean(item->toggled));
317 dbus_menu_action_unlock(item->ref_action);
318 }
319 }
320
dbus_menu_item_update_shortcut(DBusMenuItem * item,GVariant * value)321 static bool dbus_menu_item_update_shortcut(DBusMenuItem *item, GVariant *value)
322 {
323 GString *new_accel_string = g_string_new(NULL);
324 if (g_variant_n_children(value) != 1)
325 g_debug("Unable to parse shortcut correctly, too many keys. Taking first.");
326
327 GVariantIter iter;
328 GVariant *child = g_variant_get_child_value(value, 0);
329 g_variant_iter_init(&iter, child);
330 char *string;
331
332 while (g_variant_iter_loop(&iter, "s", &string))
333 {
334 if (g_strcmp0(string, DBUS_MENU_SHORTCUT_CONTROL) == 0)
335 g_string_append(new_accel_string, DBUS_MENUMODEL_SHORTCUT_CONTROL);
336 else if (g_strcmp0(string, DBUS_MENU_SHORTCUT_ALT) == 0)
337 g_string_append(new_accel_string, DBUS_MENUMODEL_SHORTCUT_ALT);
338 else if (g_strcmp0(string, DBUS_MENU_SHORTCUT_SHIFT) == 0)
339 g_string_append(new_accel_string, DBUS_MENUMODEL_SHORTCUT_SHIFT);
340 else if (g_strcmp0(string, DBUS_MENU_SHORTCUT_SUPER) == 0)
341 g_string_append(new_accel_string, DBUS_MENUMODEL_SHORTCUT_SUPER);
342 else
343 g_string_append(new_accel_string, string);
344 }
345 g_variant_unref(child);
346 g_autofree char *str = g_string_free(new_accel_string, false);
347 GVariant *new_accel = g_variant_new_string(str);
348 bool updated = attr_update_checked(item, G_MENU_ATTRIBUTE_ACCEL, new_accel);
349 if (!updated)
350 g_variant_unref(new_accel);
351 return updated;
352 }
353
dbus_menu_item_update_props(DBusMenuItem * item,GVariant * props)354 G_GNUC_INTERNAL bool dbus_menu_item_update_props(DBusMenuItem *item, GVariant *props)
355 {
356 GVariantIter iter;
357 const char *prop;
358 GVariant *value;
359 bool properties_is_updated = false;
360
361 g_variant_iter_init(&iter, props);
362 while (g_variant_iter_loop(&iter, "{&sv}", &prop, &value))
363 {
364 if (g_strcmp0(prop, "accessible-desc") == 0)
365 {
366 // TODO: Can we supported this property?
367 // properties_is_updated = true;
368 }
369 else if (g_strcmp0(prop, "enabled") == 0)
370 {
371 bool enabled = g_variant_get_boolean(value);
372 properties_is_updated =
373 dbus_menu_item_update_enabled(item, enabled) || properties_is_updated;
374 }
375 #if 0
376 else if (g_strcmp0(prop, "icon-data") == 0)
377 {
378 // icon-name has more priority
379 if (!g_hash_table_lookup(item->attrs, G_MENU_ATTRIBUTE_ICON))
380 {
381 g_autoptr(GIcon) icon = g_icon_new_pixbuf_from_variant(value);
382 GVariant *value = g_icon_serialize(icon);
383 properties_is_updated =
384 properties_is_updated ||
385 attr_update_checked(item,
386 G_MENU_ATTRIBUTE_ICON,
387 value);
388 properties_is_updated =
389 properties_is_updated ||
390 attr_update_checked(item,
391 G_MENU_ATTRIBUTE_VERB_ICON,
392 value);
393 }
394 }
395 else if (g_strcmp0(prop, "icon-name") == 0)
396 {
397 g_autoptr(GIcon) icon =
398 g_themed_icon_new(g_variant_get_string(value, NULL));
399 GVariant *value = g_icon_serialize(icon);
400 g_autoptr(GVariant) boolvar = g_variant_new_boolean(true);
401 properties_is_updated =
402 properties_is_updated ||
403 attr_update_checked(item, G_MENU_ATTRIBUTE_ICON, value);
404 properties_is_updated =
405 properties_is_updated ||
406 attr_update_checked(item,
407 G_MENU_ATTRIBUTE_VERB_ICON,
408 value);
409 properties_is_updated =
410 properties_is_updated ||
411 attr_update_checked(item, HAS_ICON_NAME, boolvar);
412 }
413 #endif
414 else if (g_strcmp0(prop, "label") == 0)
415 {
416 properties_is_updated =
417 attr_update_checked(item, G_MENU_ATTRIBUTE_LABEL, value) ||
418 properties_is_updated;
419 }
420 else if (g_strcmp0(prop, "shortcut") == 0)
421 {
422 properties_is_updated =
423 dbus_menu_item_update_shortcut(item, value) || properties_is_updated;
424 }
425 else if (g_strcmp0(prop, "toggle-state") == 0)
426 {
427 item->toggled = g_variant_get_int32(value) > 0;
428 act_props_try_update(item);
429 }
430 else if (g_strcmp0(prop, "visible") == 0)
431 {
432 bool vis = g_variant_get_boolean(value);
433 if (item->action_type == DBUS_MENU_ACTION_SECTION)
434 {
435 item->toggled = !vis;
436 }
437 else if (vis)
438 {
439 g_autofree char *name =
440 dbus_menu_action_get_name(item->id, item->action_type, true);
441 bool found =
442 g_hash_table_remove(item->attrs, G_MENU_ATTRIBUTE_HIDDEN_WHEN);
443 if (found)
444 {
445 g_hash_table_insert(item->attrs,
446 g_strdup(G_MENU_ATTRIBUTE_ACTION),
447 g_variant_new_string(name));
448 properties_is_updated = true;
449 }
450 }
451 else
452 {
453 bool found = g_hash_table_contains(item->attrs,
454 G_MENU_ATTRIBUTE_HIDDEN_WHEN);
455 if (!found)
456 {
457 g_hash_table_insert(item->attrs,
458 g_strdup(G_MENU_ATTRIBUTE_HIDDEN_WHEN),
459 g_variant_new_string(
460 G_MENU_HIDDEN_WHEN_ACTION_MISSING));
461 g_hash_table_insert(item->attrs,
462 g_strdup(G_MENU_ATTRIBUTE_ACTION),
463 g_variant_new_string(
464 DBUS_MENU_DISABLED_ACTION));
465 properties_is_updated = true;
466 }
467 }
468 }
469 else
470 {
471 g_debug("updating unsupported property - '%s'", prop);
472 }
473 }
474 return properties_is_updated;
475 }
476
dbus_menu_item_remove_props(DBusMenuItem * item,GVariant * props)477 G_GNUC_INTERNAL bool dbus_menu_item_remove_props(DBusMenuItem *item, GVariant *props)
478 {
479 GVariantIter iter;
480 const char *prop;
481 bool properties_is_updated = false;
482
483 g_variant_iter_init(&iter, props);
484 while (g_variant_iter_next(&iter, "&s", &prop))
485 {
486 if (g_strcmp0(prop, "accessible-desc") == 0)
487 {
488 // TODO: Can we support this property?
489 // properties_is_updated = true;
490 }
491 else if (g_strcmp0(prop, "enabled") == 0)
492 {
493 bool enabled = true;
494 dbus_menu_item_update_enabled(item, enabled);
495 }
496 else if (g_strcmp0(prop, "icon-name") == 0)
497 {
498 if (g_hash_table_lookup(item->attrs, HAS_ICON_NAME))
499 {
500 g_hash_table_remove(item->attrs, G_MENU_ATTRIBUTE_ICON);
501 g_hash_table_remove(item->attrs, G_MENU_ATTRIBUTE_VERB_ICON);
502 g_hash_table_remove(item->attrs, HAS_ICON_NAME);
503 properties_is_updated = true;
504 }
505 }
506 else if (g_strcmp0(prop, "icon-data") == 0)
507 {
508 if (!g_hash_table_lookup(item->attrs, HAS_ICON_NAME))
509 {
510 g_hash_table_remove(item->attrs, G_MENU_ATTRIBUTE_ICON);
511 g_hash_table_remove(item->attrs, G_MENU_ATTRIBUTE_VERB_ICON);
512 properties_is_updated = true;
513 }
514 }
515 else if (g_strcmp0(prop, "label") == 0)
516 {
517 g_hash_table_remove(item->attrs, G_MENU_ATTRIBUTE_LABEL);
518 properties_is_updated = true;
519 }
520 else if (g_strcmp0(prop, "shortcut") == 0)
521 {
522 g_hash_table_remove(item->attrs, G_MENU_ATTRIBUTE_ACCEL);
523 properties_is_updated = true;
524 }
525 else if (g_strcmp0(prop, "visible") == 0)
526 {
527 g_autofree char *name =
528 dbus_menu_action_get_name(item->id, item->action_type, false);
529 g_hash_table_remove(item->attrs, G_MENU_ATTRIBUTE_HIDDEN_WHEN);
530 g_hash_table_insert(item->attrs,
531 g_strdup(G_MENU_ATTRIBUTE_ACTION),
532 g_variant_new_string(name));
533 properties_is_updated = true;
534 }
535 else
536 {
537 g_debug("removing unsupported property - '%s'", prop);
538 }
539 }
540 return properties_is_updated;
541 }
542
dbus_menu_item_compare_func(const DBusMenuItem * a,const DBusMenuItem * b,gpointer user_data)543 G_GNUC_INTERNAL int dbus_menu_item_compare_func(const DBusMenuItem *a, const DBusMenuItem *b,
544 gpointer user_data)
545 {
546 return b->id - a->id;
547 }
548
dbus_menu_item_id_compare_func(const DBusMenuItem * a,gconstpointer b,gpointer user_data)549 G_GNUC_INTERNAL int dbus_menu_item_id_compare_func(const DBusMenuItem *a, gconstpointer b,
550 gpointer user_data)
551 {
552 return GPOINTER_TO_UINT(b) - a->id;
553 }
554
dbus_menu_item_compare_immutable(DBusMenuItem * a,DBusMenuItem * b)555 G_GNUC_INTERNAL bool dbus_menu_item_compare_immutable(DBusMenuItem *a, DBusMenuItem *b)
556 {
557 if (a->id != b->id)
558 return false;
559 if (a->ref_action_group != b->ref_action_group)
560 return false;
561 if (a->action_type != b->action_type)
562 return false;
563 return true;
564 }
565
dbus_menu_item_is_submenu(DBusMenuItem * item)566 static bool dbus_menu_item_is_submenu(DBusMenuItem *item)
567 {
568 if (!item)
569 return false;
570 if (item->action_type != DBUS_MENU_ACTION_SUBMENU)
571 return false;
572 return true;
573 }
574
dbus_menu_item_copy_submenu(DBusMenuItem * src,DBusMenuItem * dst,DBusMenuModel * parent)575 G_GNUC_INTERNAL void dbus_menu_item_copy_submenu(DBusMenuItem *src, DBusMenuItem *dst,
576 DBusMenuModel *parent)
577 {
578 DBusMenuXml *xml;
579 DBusMenuModel *submenu = NULL;
580 g_object_get(parent, "xml", &xml, NULL);
581 if (!dbus_menu_item_is_submenu(src))
582 {
583 if (dst->action_type == DBUS_MENU_ACTION_SUBMENU)
584 {
585 if (dst->toggled)
586 dst->enabled = true;
587 submenu = dbus_menu_model_new(dst->id, parent, xml, dst->ref_action_group);
588 g_hash_table_insert(dst->links, submenu_str(dst->enabled), submenu);
589 }
590 return;
591 }
592 if (dst->action_type == DBUS_MENU_ACTION_SUBMENU &&
593 src->action_type == DBUS_MENU_ACTION_SUBMENU)
594 {
595 if (src->toggled || dst->toggled)
596 dst->enabled = dst->toggled = true;
597 submenu =
598 DBUS_MENU_MODEL(g_hash_table_lookup(src->links, submenu_str(src->enabled)));
599 g_hash_table_insert(dst->links, submenu_str(dst->enabled), g_object_ref(submenu));
600 g_object_set(submenu, "parent-id", dst->id, NULL);
601 }
602 }
603
dbus_menu_item_generate_action(DBusMenuItem * item,DBusMenuModel * parent)604 G_GNUC_INTERNAL void dbus_menu_item_generate_action(DBusMenuItem *item, DBusMenuModel *parent)
605 {
606 if (item->action_type == DBUS_MENU_ACTION_SECTION)
607 return;
608 DBusMenuXml *xml;
609 DBusMenuModel *submenu = g_hash_table_lookup(item->links, submenu_str(item->enabled));
610 g_object_get(parent, "xml", &xml, NULL);
611 item->ref_action = dbus_menu_action_reference(item->id,
612 xml,
613 submenu,
614 G_ACTION_MAP(item->ref_action_group),
615 item->action_type);
616 act_props_try_update(item);
617 }
618