Branch data Line data Source code
1 : : /*
2 : : * Copyright © 2011 Canonical Ltd.
3 : : *
4 : : * SPDX-License-Identifier: LGPL-2.1-or-later
5 : : *
6 : : * This library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Lesser General Public
8 : : * License as published by the Free Software Foundation; either
9 : : * version 2.1 of the License, or (at your option) any later version.
10 : : *
11 : : * This library is distributed in the hope that it will be useful, but
12 : : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public
17 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 : : *
19 : : * Author: Ryan Lortie <desrt@desrt.ca>
20 : : */
21 : :
22 : : #include "config.h"
23 : :
24 : : #include "gmenu.h"
25 : :
26 : : #include "gaction.h"
27 : : #include <string.h>
28 : :
29 : : #include "gicon.h"
30 : :
31 : : /**
32 : : * GMenu:
33 : : *
34 : : * `GMenu` is a simple implementation of [class@Gio.MenuModel].
35 : : * You populate a `GMenu` by adding [class@Gio.MenuItem] instances to it.
36 : : *
37 : : * There are some convenience functions to allow you to directly
38 : : * add items (avoiding [class@Gio.MenuItem]) for the common cases. To add
39 : : * a regular item, use [method@Gio.Menu.insert]. To add a section, use
40 : : * [method@Gio.Menu.insert_section]. To add a submenu, use
41 : : * [method@Gio.Menu.insert_submenu].
42 : : *
43 : : * Since: 2.32
44 : : */
45 : :
46 : : /**
47 : : * GMenuItem:
48 : : *
49 : : * #GMenuItem is an opaque structure type. You must access it using the
50 : : * functions below.
51 : : *
52 : : * Since: 2.32
53 : : */
54 : :
55 : : struct _GMenuItem
56 : : {
57 : : GObject parent_instance;
58 : :
59 : : GHashTable *attributes;
60 : : GHashTable *links;
61 : : gboolean cow;
62 : : };
63 : :
64 : : typedef GObjectClass GMenuItemClass;
65 : :
66 : : struct _GMenu
67 : : {
68 : : GMenuModel parent_instance;
69 : :
70 : : GArray *items;
71 : : gboolean mutable;
72 : : };
73 : :
74 : : typedef GMenuModelClass GMenuClass;
75 : :
76 : 222 : G_DEFINE_TYPE (GMenu, g_menu, G_TYPE_MENU_MODEL)
77 : 240 : G_DEFINE_TYPE (GMenuItem, g_menu_item, G_TYPE_OBJECT)
78 : :
79 : : struct item
80 : : {
81 : : GHashTable *attributes;
82 : : GHashTable *links;
83 : : };
84 : :
85 : : static gboolean
86 : 4 : g_menu_is_mutable (GMenuModel *model)
87 : : {
88 : 4 : GMenu *menu = G_MENU (model);
89 : :
90 : 4 : return menu->mutable;
91 : : }
92 : :
93 : : static gint
94 : 31 : g_menu_get_n_items (GMenuModel *model)
95 : : {
96 : 31 : GMenu *menu = G_MENU (model);
97 : :
98 : 31 : return menu->items->len;
99 : : }
100 : :
101 : : static void
102 : 59 : g_menu_get_item_attributes (GMenuModel *model,
103 : : gint position,
104 : : GHashTable **table)
105 : : {
106 : 59 : GMenu *menu = G_MENU (model);
107 : :
108 : 59 : *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).attributes);
109 : 59 : }
110 : :
111 : : static void
112 : 46 : g_menu_get_item_links (GMenuModel *model,
113 : : gint position,
114 : : GHashTable **table)
115 : : {
116 : 46 : GMenu *menu = G_MENU (model);
117 : :
118 : 46 : *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).links);
119 : 46 : }
120 : :
121 : : /**
122 : : * g_menu_insert_item:
123 : : * @menu: a #GMenu
124 : : * @position: the position at which to insert the item
125 : : * @item: the #GMenuItem to insert
126 : : *
127 : : * Inserts @item into @menu.
128 : : *
129 : : * The "insertion" is actually done by copying all of the attribute and
130 : : * link values of @item and using them to form a new item within @menu.
131 : : * As such, @item itself is not really inserted, but rather, a menu item
132 : : * that is exactly the same as the one presently described by @item.
133 : : *
134 : : * This means that @item is essentially useless after the insertion
135 : : * occurs. Any changes you make to it are ignored unless it is inserted
136 : : * again (at which point its updated values will be copied).
137 : : *
138 : : * You should probably just free @item once you're done.
139 : : *
140 : : * There are many convenience functions to take care of common cases.
141 : : * See g_menu_insert(), g_menu_insert_section() and
142 : : * g_menu_insert_submenu() as well as "prepend" and "append" variants of
143 : : * each of these functions.
144 : : *
145 : : * Since: 2.32
146 : : */
147 : : void
148 : 37 : g_menu_insert_item (GMenu *menu,
149 : : gint position,
150 : : GMenuItem *item)
151 : : {
152 : : struct item new_item;
153 : :
154 : 37 : g_return_if_fail (G_IS_MENU (menu));
155 : 37 : g_return_if_fail (G_IS_MENU_ITEM (item));
156 : :
157 : 37 : if (position < 0 || (guint) position > menu->items->len)
158 : 27 : position = menu->items->len;
159 : :
160 : 37 : new_item.attributes = g_hash_table_ref (item->attributes);
161 : 37 : new_item.links = g_hash_table_ref (item->links);
162 : 37 : item->cow = TRUE;
163 : :
164 : 37 : g_array_insert_val (menu->items, position, new_item);
165 : 37 : g_menu_model_items_changed (G_MENU_MODEL (menu), position, 0, 1);
166 : : }
167 : :
168 : : /**
169 : : * g_menu_prepend_item:
170 : : * @menu: a #GMenu
171 : : * @item: a #GMenuItem to prepend
172 : : *
173 : : * Prepends @item to the start of @menu.
174 : : *
175 : : * See g_menu_insert_item() for more information.
176 : : *
177 : : * Since: 2.32
178 : : */
179 : : void
180 : 1 : g_menu_prepend_item (GMenu *menu,
181 : : GMenuItem *item)
182 : : {
183 : 1 : g_menu_insert_item (menu, 0, item);
184 : 1 : }
185 : :
186 : : /**
187 : : * g_menu_append_item:
188 : : * @menu: a #GMenu
189 : : * @item: a #GMenuItem to append
190 : : *
191 : : * Appends @item to the end of @menu.
192 : : *
193 : : * See g_menu_insert_item() for more information.
194 : : *
195 : : * Since: 2.32
196 : : */
197 : : void
198 : 4 : g_menu_append_item (GMenu *menu,
199 : : GMenuItem *item)
200 : : {
201 : 4 : g_menu_insert_item (menu, -1, item);
202 : 4 : }
203 : :
204 : : /**
205 : : * g_menu_freeze:
206 : : * @menu: a #GMenu
207 : : *
208 : : * Marks @menu as frozen.
209 : : *
210 : : * After the menu is frozen, it is an error to attempt to make any
211 : : * changes to it. In effect this means that the #GMenu API must no
212 : : * longer be used.
213 : : *
214 : : * This function causes g_menu_model_is_mutable() to begin returning
215 : : * %FALSE, which has some positive performance implications.
216 : : *
217 : : * Since: 2.32
218 : : */
219 : : void
220 : 1 : g_menu_freeze (GMenu *menu)
221 : : {
222 : 1 : g_return_if_fail (G_IS_MENU (menu));
223 : :
224 : 1 : menu->mutable = FALSE;
225 : : }
226 : :
227 : : /**
228 : : * g_menu_new:
229 : : *
230 : : * Creates a new #GMenu.
231 : : *
232 : : * The new menu has no items.
233 : : *
234 : : * Returns: a new #GMenu
235 : : *
236 : : * Since: 2.32
237 : : */
238 : : GMenu *
239 : 12 : g_menu_new (void)
240 : : {
241 : 12 : return g_object_new (G_TYPE_MENU, NULL);
242 : : }
243 : :
244 : : /**
245 : : * g_menu_insert:
246 : : * @menu: a #GMenu
247 : : * @position: the position at which to insert the item
248 : : * @label: (nullable): the section label, or %NULL
249 : : * @detailed_action: (nullable): the detailed action string, or %NULL
250 : : *
251 : : * Convenience function for inserting a normal menu item into @menu.
252 : : * Combine g_menu_item_new() and g_menu_insert_item() for a more flexible
253 : : * alternative.
254 : : *
255 : : * Since: 2.32
256 : : */
257 : : void
258 : 18 : g_menu_insert (GMenu *menu,
259 : : gint position,
260 : : const gchar *label,
261 : : const gchar *detailed_action)
262 : : {
263 : : GMenuItem *menu_item;
264 : :
265 : 18 : menu_item = g_menu_item_new (label, detailed_action);
266 : 18 : g_menu_insert_item (menu, position, menu_item);
267 : 18 : g_object_unref (menu_item);
268 : 18 : }
269 : :
270 : : /**
271 : : * g_menu_prepend:
272 : : * @menu: a #GMenu
273 : : * @label: (nullable): the section label, or %NULL
274 : : * @detailed_action: (nullable): the detailed action string, or %NULL
275 : : *
276 : : * Convenience function for prepending a normal menu item to the start
277 : : * of @menu. Combine g_menu_item_new() and g_menu_insert_item() for a more
278 : : * flexible alternative.
279 : : *
280 : : * Since: 2.32
281 : : */
282 : : void
283 : 1 : g_menu_prepend (GMenu *menu,
284 : : const gchar *label,
285 : : const gchar *detailed_action)
286 : : {
287 : 1 : g_menu_insert (menu, 0, label, detailed_action);
288 : 1 : }
289 : :
290 : : /**
291 : : * g_menu_append:
292 : : * @menu: a #GMenu
293 : : * @label: (nullable): the section label, or %NULL
294 : : * @detailed_action: (nullable): the detailed action string, or %NULL
295 : : *
296 : : * Convenience function for appending a normal menu item to the end of
297 : : * @menu. Combine g_menu_item_new() and g_menu_insert_item() for a more
298 : : * flexible alternative.
299 : : *
300 : : * Since: 2.32
301 : : */
302 : : void
303 : 15 : g_menu_append (GMenu *menu,
304 : : const gchar *label,
305 : : const gchar *detailed_action)
306 : : {
307 : 15 : g_menu_insert (menu, -1, label, detailed_action);
308 : 15 : }
309 : :
310 : : /**
311 : : * g_menu_insert_section:
312 : : * @menu: a #GMenu
313 : : * @position: the position at which to insert the item
314 : : * @label: (nullable): the section label, or %NULL
315 : : * @section: a #GMenuModel with the items of the section
316 : : *
317 : : * Convenience function for inserting a section menu item into @menu.
318 : : * Combine g_menu_item_new_section() and g_menu_insert_item() for a more
319 : : * flexible alternative.
320 : : *
321 : : * Since: 2.32
322 : : */
323 : : void
324 : 5 : g_menu_insert_section (GMenu *menu,
325 : : gint position,
326 : : const gchar *label,
327 : : GMenuModel *section)
328 : : {
329 : : GMenuItem *menu_item;
330 : :
331 : 5 : menu_item = g_menu_item_new_section (label, section);
332 : 5 : g_menu_insert_item (menu, position, menu_item);
333 : 5 : g_object_unref (menu_item);
334 : 5 : }
335 : :
336 : :
337 : : /**
338 : : * g_menu_prepend_section:
339 : : * @menu: a #GMenu
340 : : * @label: (nullable): the section label, or %NULL
341 : : * @section: a #GMenuModel with the items of the section
342 : : *
343 : : * Convenience function for prepending a section menu item to the start
344 : : * of @menu. Combine g_menu_item_new_section() and g_menu_insert_item() for
345 : : * a more flexible alternative.
346 : : *
347 : : * Since: 2.32
348 : : */
349 : : void
350 : 1 : g_menu_prepend_section (GMenu *menu,
351 : : const gchar *label,
352 : : GMenuModel *section)
353 : : {
354 : 1 : g_menu_insert_section (menu, 0, label, section);
355 : 1 : }
356 : :
357 : : /**
358 : : * g_menu_append_section:
359 : : * @menu: a #GMenu
360 : : * @label: (nullable): the section label, or %NULL
361 : : * @section: a #GMenuModel with the items of the section
362 : : *
363 : : * Convenience function for appending a section menu item to the end of
364 : : * @menu. Combine g_menu_item_new_section() and g_menu_insert_item() for a
365 : : * more flexible alternative.
366 : : *
367 : : * Since: 2.32
368 : : */
369 : : void
370 : 1 : g_menu_append_section (GMenu *menu,
371 : : const gchar *label,
372 : : GMenuModel *section)
373 : : {
374 : 1 : g_menu_insert_section (menu, -1, label, section);
375 : 1 : }
376 : :
377 : : /**
378 : : * g_menu_insert_submenu:
379 : : * @menu: a #GMenu
380 : : * @position: the position at which to insert the item
381 : : * @label: (nullable): the section label, or %NULL
382 : : * @submenu: a #GMenuModel with the items of the submenu
383 : : *
384 : : * Convenience function for inserting a submenu menu item into @menu.
385 : : * Combine g_menu_item_new_submenu() and g_menu_insert_item() for a more
386 : : * flexible alternative.
387 : : *
388 : : * Since: 2.32
389 : : */
390 : : void
391 : 5 : g_menu_insert_submenu (GMenu *menu,
392 : : gint position,
393 : : const gchar *label,
394 : : GMenuModel *submenu)
395 : : {
396 : : GMenuItem *menu_item;
397 : :
398 : 5 : menu_item = g_menu_item_new_submenu (label, submenu);
399 : 5 : g_menu_insert_item (menu, position, menu_item);
400 : 5 : g_object_unref (menu_item);
401 : 5 : }
402 : :
403 : : /**
404 : : * g_menu_prepend_submenu:
405 : : * @menu: a #GMenu
406 : : * @label: (nullable): the section label, or %NULL
407 : : * @submenu: a #GMenuModel with the items of the submenu
408 : : *
409 : : * Convenience function for prepending a submenu menu item to the start
410 : : * of @menu. Combine g_menu_item_new_submenu() and g_menu_insert_item() for
411 : : * a more flexible alternative.
412 : : *
413 : : * Since: 2.32
414 : : */
415 : : void
416 : 1 : g_menu_prepend_submenu (GMenu *menu,
417 : : const gchar *label,
418 : : GMenuModel *submenu)
419 : : {
420 : 1 : g_menu_insert_submenu (menu, 0, label, submenu);
421 : 1 : }
422 : :
423 : : /**
424 : : * g_menu_append_submenu:
425 : : * @menu: a #GMenu
426 : : * @label: (nullable): the section label, or %NULL
427 : : * @submenu: a #GMenuModel with the items of the submenu
428 : : *
429 : : * Convenience function for appending a submenu menu item to the end of
430 : : * @menu. Combine g_menu_item_new_submenu() and g_menu_insert_item() for a
431 : : * more flexible alternative.
432 : : *
433 : : * Since: 2.32
434 : : */
435 : : void
436 : 1 : g_menu_append_submenu (GMenu *menu,
437 : : const gchar *label,
438 : : GMenuModel *submenu)
439 : : {
440 : 1 : g_menu_insert_submenu (menu, -1, label, submenu);
441 : 1 : }
442 : :
443 : : static void
444 : 37 : g_menu_clear_item (struct item *item)
445 : : {
446 : 37 : if (item->attributes != NULL)
447 : 37 : g_hash_table_unref (item->attributes);
448 : 37 : if (item->links != NULL)
449 : 37 : g_hash_table_unref (item->links);
450 : 37 : }
451 : :
452 : : /**
453 : : * g_menu_remove:
454 : : * @menu: a #GMenu
455 : : * @position: the position of the item to remove
456 : : *
457 : : * Removes an item from the menu.
458 : : *
459 : : * @position gives the index of the item to remove.
460 : : *
461 : : * It is an error if position is not in range the range from 0 to one
462 : : * less than the number of items in the menu.
463 : : *
464 : : * It is not possible to remove items by identity since items are added
465 : : * to the menu simply by copying their links and attributes (ie:
466 : : * identity of the item itself is not preserved).
467 : : *
468 : : * Since: 2.32
469 : : */
470 : : void
471 : 8 : g_menu_remove (GMenu *menu,
472 : : gint position)
473 : : {
474 : 8 : g_return_if_fail (G_IS_MENU (menu));
475 : 8 : g_return_if_fail (0 <= position && (guint) position < menu->items->len);
476 : :
477 : 8 : g_menu_clear_item (&g_array_index (menu->items, struct item, position));
478 : 8 : g_array_remove_index (menu->items, position);
479 : 8 : g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 0);
480 : : }
481 : :
482 : : /**
483 : : * g_menu_remove_all:
484 : : * @menu: a #GMenu
485 : : *
486 : : * Removes all items in the menu.
487 : : *
488 : : * Since: 2.38
489 : : **/
490 : : void
491 : 2 : g_menu_remove_all (GMenu *menu)
492 : : {
493 : : gint i, n;
494 : :
495 : 2 : g_return_if_fail (G_IS_MENU (menu));
496 : 2 : n = menu->items->len;
497 : :
498 : 4 : for (i = 0; i < n; i++)
499 : 2 : g_menu_clear_item (&g_array_index (menu->items, struct item, i));
500 : 2 : g_array_set_size (menu->items, 0);
501 : :
502 : 2 : g_menu_model_items_changed (G_MENU_MODEL (menu), 0, n, 0);
503 : : }
504 : :
505 : : static void
506 : 13 : g_menu_finalize (GObject *object)
507 : : {
508 : 13 : GMenu *menu = G_MENU (object);
509 : : struct item *items;
510 : : gint n_items;
511 : : gint i;
512 : :
513 : 13 : n_items = menu->items->len;
514 : 13 : items = (struct item *) g_array_free (menu->items, FALSE);
515 : 40 : for (i = 0; i < n_items; i++)
516 : 27 : g_menu_clear_item (&items[i]);
517 : 13 : g_free (items);
518 : :
519 : 13 : G_OBJECT_CLASS (g_menu_parent_class)
520 : 13 : ->finalize (object);
521 : 13 : }
522 : :
523 : : static void
524 : 13 : g_menu_init (GMenu *menu)
525 : : {
526 : 13 : menu->items = g_array_new (FALSE, FALSE, sizeof (struct item));
527 : 13 : menu->mutable = TRUE;
528 : 13 : }
529 : :
530 : : static void
531 : 3 : g_menu_class_init (GMenuClass *class)
532 : : {
533 : 3 : GMenuModelClass *model_class = G_MENU_MODEL_CLASS (class);
534 : 3 : GObjectClass *object_class = G_OBJECT_CLASS (class);
535 : :
536 : 3 : object_class->finalize = g_menu_finalize;
537 : :
538 : 3 : model_class->is_mutable = g_menu_is_mutable;
539 : 3 : model_class->get_n_items = g_menu_get_n_items;
540 : 3 : model_class->get_item_attributes = g_menu_get_item_attributes;
541 : 3 : model_class->get_item_links = g_menu_get_item_links;
542 : 3 : }
543 : :
544 : :
545 : : static void
546 : 83 : g_menu_item_clear_cow (GMenuItem *menu_item)
547 : : {
548 : 83 : if (menu_item->cow)
549 : : {
550 : : GHashTableIter iter;
551 : : GHashTable *new;
552 : : gpointer key;
553 : : gpointer val;
554 : :
555 : 3 : new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
556 : 3 : g_hash_table_iter_init (&iter, menu_item->attributes);
557 : 18 : while (g_hash_table_iter_next (&iter, &key, &val))
558 : 30 : g_hash_table_insert (new, g_strdup (key), g_variant_ref (val));
559 : 3 : g_hash_table_unref (menu_item->attributes);
560 : 3 : menu_item->attributes = new;
561 : :
562 : 3 : new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
563 : 3 : g_hash_table_iter_init (&iter, menu_item->links);
564 : 4 : while (g_hash_table_iter_next (&iter, &key, &val))
565 : 2 : g_hash_table_insert (new, g_strdup (key), g_object_ref (val));
566 : 3 : g_hash_table_unref (menu_item->links);
567 : 3 : menu_item->links = new;
568 : :
569 : 3 : menu_item->cow = FALSE;
570 : : }
571 : 83 : }
572 : :
573 : : static void
574 : 50 : g_menu_item_finalize (GObject *object)
575 : : {
576 : 50 : GMenuItem *menu_item = G_MENU_ITEM (object);
577 : :
578 : 50 : g_hash_table_unref (menu_item->attributes);
579 : 50 : g_hash_table_unref (menu_item->links);
580 : :
581 : 50 : G_OBJECT_CLASS (g_menu_item_parent_class)
582 : 50 : ->finalize (object);
583 : 50 : }
584 : :
585 : : static void
586 : 50 : g_menu_item_init (GMenuItem *menu_item)
587 : : {
588 : 50 : menu_item->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
589 : 50 : menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
590 : 50 : menu_item->cow = FALSE;
591 : 50 : }
592 : :
593 : : static void
594 : 4 : g_menu_item_class_init (GMenuItemClass *class)
595 : : {
596 : 4 : class->finalize = g_menu_item_finalize;
597 : 4 : }
598 : :
599 : : /* We treat attribute names the same as GSettings keys:
600 : : * - only lowercase ascii, digits and '-'
601 : : * - must start with lowercase
602 : : * - must not end with '-'
603 : : * - no consecutive '-'
604 : : * - not longer than 1024 chars
605 : : */
606 : : static gboolean
607 : 84 : valid_attribute_name (const gchar *name)
608 : : {
609 : : gint i;
610 : :
611 : 84 : if (!g_ascii_islower (name[0]))
612 : 0 : return FALSE;
613 : :
614 : 490 : for (i = 1; name[i]; i++)
615 : : {
616 : 406 : if (name[i] != '-' &&
617 : 404 : !g_ascii_islower (name[i]) &&
618 : 6 : !g_ascii_isdigit (name[i]))
619 : 0 : return FALSE;
620 : :
621 : 406 : if (name[i] == '-' && name[i + 1] == '-')
622 : 0 : return FALSE;
623 : : }
624 : :
625 : 84 : if (name[i - 1] == '-')
626 : 0 : return FALSE;
627 : :
628 : 84 : if (i > 1024)
629 : 0 : return FALSE;
630 : :
631 : 84 : return TRUE;
632 : : }
633 : :
634 : : /**
635 : : * g_menu_item_set_attribute_value:
636 : : * @menu_item: a #GMenuItem
637 : : * @attribute: the attribute to set
638 : : * @value: (nullable): a #GVariant to use as the value, or %NULL
639 : : *
640 : : * Sets or unsets an attribute on @menu_item.
641 : : *
642 : : * The attribute to set or unset is specified by @attribute. This
643 : : * can be one of the standard attribute names %G_MENU_ATTRIBUTE_LABEL,
644 : : * %G_MENU_ATTRIBUTE_ACTION, %G_MENU_ATTRIBUTE_TARGET, or a custom
645 : : * attribute name.
646 : : * Attribute names are restricted to lowercase characters, numbers
647 : : * and '-'. Furthermore, the names must begin with a lowercase character,
648 : : * must not end with a '-', and must not contain consecutive dashes.
649 : : *
650 : : * must consist only of lowercase
651 : : * ASCII characters, digits and '-'.
652 : : *
653 : : * If @value is non-%NULL then it is used as the new value for the
654 : : * attribute. If @value is %NULL then the attribute is unset. If
655 : : * the @value #GVariant is floating, it is consumed.
656 : : *
657 : : * See also g_menu_item_set_attribute() for a more convenient way to do
658 : : * the same.
659 : : *
660 : : * Since: 2.32
661 : : */
662 : : void
663 : 65 : g_menu_item_set_attribute_value (GMenuItem *menu_item,
664 : : const gchar *attribute,
665 : : GVariant *value)
666 : : {
667 : 65 : g_return_if_fail (G_IS_MENU_ITEM (menu_item));
668 : 65 : g_return_if_fail (attribute != NULL);
669 : 65 : g_return_if_fail (valid_attribute_name (attribute));
670 : :
671 : 65 : g_menu_item_clear_cow (menu_item);
672 : :
673 : 65 : if (value != NULL)
674 : 126 : g_hash_table_insert (menu_item->attributes, g_strdup (attribute), g_variant_ref_sink (value));
675 : : else
676 : 2 : g_hash_table_remove (menu_item->attributes, attribute);
677 : : }
678 : :
679 : : /**
680 : : * g_menu_item_set_attribute:
681 : : * @menu_item: a #GMenuItem
682 : : * @attribute: the attribute to set
683 : : * @format_string: (nullable): a #GVariant format string, or %NULL
684 : : * @...: positional parameters, as per @format_string
685 : : *
686 : : * Sets or unsets an attribute on @menu_item.
687 : : *
688 : : * The attribute to set or unset is specified by @attribute. This
689 : : * can be one of the standard attribute names %G_MENU_ATTRIBUTE_LABEL,
690 : : * %G_MENU_ATTRIBUTE_ACTION, %G_MENU_ATTRIBUTE_TARGET, or a custom
691 : : * attribute name.
692 : : * Attribute names are restricted to lowercase characters, numbers
693 : : * and '-'. Furthermore, the names must begin with a lowercase character,
694 : : * must not end with a '-', and must not contain consecutive dashes.
695 : : *
696 : : * If @format_string is non-%NULL then the proper position parameters
697 : : * are collected to create a #GVariant instance to use as the attribute
698 : : * value. If it is %NULL then the positional parameterrs are ignored
699 : : * and the named attribute is unset.
700 : : *
701 : : * See also g_menu_item_set_attribute_value() for an equivalent call
702 : : * that directly accepts a #GVariant.
703 : : *
704 : : * Since: 2.32
705 : : */
706 : : void
707 : 5 : g_menu_item_set_attribute (GMenuItem *menu_item,
708 : : const gchar *attribute,
709 : : const gchar *format_string,
710 : : ...)
711 : : {
712 : : GVariant *value;
713 : :
714 : 5 : if (format_string != NULL)
715 : : {
716 : : va_list ap;
717 : :
718 : 5 : va_start (ap, format_string);
719 : 5 : value = g_variant_new_va (format_string, NULL, &ap);
720 : 5 : va_end (ap);
721 : : }
722 : : else
723 : 0 : value = NULL;
724 : :
725 : 5 : g_menu_item_set_attribute_value (menu_item, attribute, value);
726 : 5 : }
727 : :
728 : : /**
729 : : * g_menu_item_set_link:
730 : : * @menu_item: a #GMenuItem
731 : : * @link: type of link to establish or unset
732 : : * @model: (nullable): the #GMenuModel to link to (or %NULL to unset)
733 : : *
734 : : * Creates a link from @menu_item to @model if non-%NULL, or unsets it.
735 : : *
736 : : * Links are used to establish a relationship between a particular menu
737 : : * item and another menu. For example, %G_MENU_LINK_SUBMENU is used to
738 : : * associate a submenu with a particular menu item, and %G_MENU_LINK_SECTION
739 : : * is used to create a section. Other types of link can be used, but there
740 : : * is no guarantee that clients will be able to make sense of them.
741 : : * Link types are restricted to lowercase characters, numbers
742 : : * and '-'. Furthermore, the names must begin with a lowercase character,
743 : : * must not end with a '-', and must not contain consecutive dashes.
744 : : *
745 : : * Since: 2.32
746 : : */
747 : : void
748 : 18 : g_menu_item_set_link (GMenuItem *menu_item,
749 : : const gchar *link,
750 : : GMenuModel *model)
751 : : {
752 : 18 : g_return_if_fail (G_IS_MENU_ITEM (menu_item));
753 : 18 : g_return_if_fail (link != NULL);
754 : 18 : g_return_if_fail (valid_attribute_name (link));
755 : :
756 : 18 : g_menu_item_clear_cow (menu_item);
757 : :
758 : 18 : if (model != NULL)
759 : 34 : g_hash_table_insert (menu_item->links, g_strdup (link), g_object_ref (model));
760 : : else
761 : 1 : g_hash_table_remove (menu_item->links, link);
762 : : }
763 : :
764 : : /**
765 : : * g_menu_item_get_attribute_value:
766 : : * @menu_item: a #GMenuItem
767 : : * @attribute: the attribute name to query
768 : : * @expected_type: (nullable): the expected type of the attribute
769 : : *
770 : : * Queries the named @attribute on @menu_item.
771 : : *
772 : : * If @expected_type is specified and the attribute does not have this
773 : : * type, %NULL is returned. %NULL is also returned if the attribute
774 : : * simply does not exist.
775 : : *
776 : : * Returns: (nullable) (transfer full): the attribute value, or %NULL
777 : : *
778 : : * Since: 2.34
779 : : */
780 : : GVariant *
781 : 3 : g_menu_item_get_attribute_value (GMenuItem *menu_item,
782 : : const gchar *attribute,
783 : : const GVariantType *expected_type)
784 : : {
785 : : GVariant *value;
786 : :
787 : 3 : g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), NULL);
788 : 3 : g_return_val_if_fail (attribute != NULL, NULL);
789 : :
790 : 3 : value = g_hash_table_lookup (menu_item->attributes, attribute);
791 : :
792 : 3 : if (value != NULL)
793 : : {
794 : 3 : if (expected_type == NULL || g_variant_is_of_type (value, expected_type))
795 : 3 : g_variant_ref (value);
796 : : else
797 : 0 : value = NULL;
798 : : }
799 : :
800 : 3 : return value;
801 : : }
802 : :
803 : : /**
804 : : * g_menu_item_get_attribute:
805 : : * @menu_item: a #GMenuItem
806 : : * @attribute: the attribute name to query
807 : : * @format_string: a #GVariant format string
808 : : * @...: positional parameters, as per @format_string
809 : : *
810 : : * Queries the named @attribute on @menu_item.
811 : : *
812 : : * If the attribute exists and matches the #GVariantType corresponding
813 : : * to @format_string then @format_string is used to deconstruct the
814 : : * value into the positional parameters and %TRUE is returned.
815 : : *
816 : : * If the attribute does not exist, or it does exist but has the wrong
817 : : * type, then the positional parameters are ignored and %FALSE is
818 : : * returned.
819 : : *
820 : : * Returns: %TRUE if the named attribute was found with the expected
821 : : * type
822 : : *
823 : : * Since: 2.34
824 : : */
825 : : gboolean
826 : 2 : g_menu_item_get_attribute (GMenuItem *menu_item,
827 : : const gchar *attribute,
828 : : const gchar *format_string,
829 : : ...)
830 : : {
831 : : GVariant *value;
832 : : va_list ap;
833 : :
834 : 2 : g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), FALSE);
835 : 2 : g_return_val_if_fail (attribute != NULL, FALSE);
836 : 2 : g_return_val_if_fail (format_string != NULL, FALSE);
837 : :
838 : 2 : value = g_hash_table_lookup (menu_item->attributes, attribute);
839 : :
840 : 2 : if (value == NULL)
841 : 0 : return FALSE;
842 : :
843 : 2 : if (!g_variant_check_format_string (value, format_string, FALSE))
844 : 0 : return FALSE;
845 : :
846 : 2 : va_start (ap, format_string);
847 : 2 : g_variant_get_va (value, format_string, NULL, &ap);
848 : 2 : va_end (ap);
849 : :
850 : 2 : return TRUE;
851 : : }
852 : :
853 : : /**
854 : : * g_menu_item_get_link:
855 : : * @menu_item: a #GMenuItem
856 : : * @link: the link name to query
857 : : *
858 : : * Queries the named @link on @menu_item.
859 : : *
860 : : * Returns: (nullable) (transfer full): the link, or %NULL
861 : : *
862 : : * Since: 2.34
863 : : */
864 : : GMenuModel *
865 : 1 : g_menu_item_get_link (GMenuItem *menu_item,
866 : : const gchar *link)
867 : : {
868 : : GMenuModel *model;
869 : :
870 : 1 : g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), NULL);
871 : 1 : g_return_val_if_fail (link != NULL, NULL);
872 : 1 : g_return_val_if_fail (valid_attribute_name (link), NULL);
873 : :
874 : 1 : model = g_hash_table_lookup (menu_item->links, link);
875 : :
876 : 1 : if (model)
877 : 1 : g_object_ref (model);
878 : :
879 : 1 : return model;
880 : : }
881 : :
882 : : /**
883 : : * g_menu_item_set_label:
884 : : * @menu_item: a #GMenuItem
885 : : * @label: (nullable): the label to set, or %NULL to unset
886 : : *
887 : : * Sets or unsets the "label" attribute of @menu_item.
888 : : *
889 : : * If @label is non-%NULL it is used as the label for the menu item. If
890 : : * it is %NULL then the label attribute is unset.
891 : : *
892 : : * Since: 2.32
893 : : */
894 : : void
895 : 37 : g_menu_item_set_label (GMenuItem *menu_item,
896 : : const gchar *label)
897 : : {
898 : : GVariant *value;
899 : :
900 : 37 : if (label != NULL)
901 : 37 : value = g_variant_new_string (label);
902 : : else
903 : 0 : value = NULL;
904 : :
905 : 37 : g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_LABEL, value);
906 : 37 : }
907 : :
908 : : /**
909 : : * g_menu_item_set_submenu:
910 : : * @menu_item: a #GMenuItem
911 : : * @submenu: (nullable): a #GMenuModel, or %NULL
912 : : *
913 : : * Sets or unsets the "submenu" link of @menu_item to @submenu.
914 : : *
915 : : * If @submenu is non-%NULL, it is linked to. If it is %NULL then the
916 : : * link is unset.
917 : : *
918 : : * The effect of having one menu appear as a submenu of another is
919 : : * exactly as it sounds.
920 : : *
921 : : * Since: 2.32
922 : : */
923 : : void
924 : 6 : g_menu_item_set_submenu (GMenuItem *menu_item,
925 : : GMenuModel *submenu)
926 : : {
927 : 6 : g_menu_item_set_link (menu_item, G_MENU_LINK_SUBMENU, submenu);
928 : 6 : }
929 : :
930 : : /**
931 : : * g_menu_item_set_section:
932 : : * @menu_item: a #GMenuItem
933 : : * @section: (nullable): a #GMenuModel, or %NULL
934 : : *
935 : : * Sets or unsets the "section" link of @menu_item to @section.
936 : : *
937 : : * The effect of having one menu appear as a section of another is
938 : : * exactly as it sounds: the items from @section become a direct part of
939 : : * the menu that @menu_item is added to. See g_menu_item_new_section()
940 : : * for more information about what it means for a menu item to be a
941 : : * section.
942 : : *
943 : : * Since: 2.32
944 : : */
945 : : void
946 : 6 : g_menu_item_set_section (GMenuItem *menu_item,
947 : : GMenuModel *section)
948 : : {
949 : 6 : g_menu_item_set_link (menu_item, G_MENU_LINK_SECTION, section);
950 : 6 : }
951 : :
952 : : /**
953 : : * g_menu_item_set_action_and_target_value:
954 : : * @menu_item: a #GMenuItem
955 : : * @action: (nullable): the name of the action for this item
956 : : * @target_value: (nullable): a #GVariant to use as the action target
957 : : *
958 : : * Sets or unsets the "action" and "target" attributes of @menu_item.
959 : : *
960 : : * If @action is %NULL then both the "action" and "target" attributes
961 : : * are unset (and @target_value is ignored).
962 : : *
963 : : * If @action is non-%NULL then the "action" attribute is set. The
964 : : * "target" attribute is then set to the value of @target_value if it is
965 : : * non-%NULL or unset otherwise.
966 : : *
967 : : * Normal menu items (ie: not submenu, section or other custom item
968 : : * types) are expected to have the "action" attribute set to identify
969 : : * the action that they are associated with. The state type of the
970 : : * action help to determine the disposition of the menu item. See
971 : : * #GAction and #GActionGroup for an overview of actions.
972 : : *
973 : : * In general, clicking on the menu item will result in activation of
974 : : * the named action with the "target" attribute given as the parameter
975 : : * to the action invocation. If the "target" attribute is not set then
976 : : * the action is invoked with no parameter.
977 : : *
978 : : * If the action has no state then the menu item is usually drawn as a
979 : : * plain menu item (ie: with no additional decoration).
980 : : *
981 : : * If the action has a boolean state then the menu item is usually drawn
982 : : * as a toggle menu item (ie: with a checkmark or equivalent
983 : : * indication). The item should be marked as 'toggled' or 'checked'
984 : : * when the boolean state is %TRUE.
985 : : *
986 : : * If the action has a string state then the menu item is usually drawn
987 : : * as a radio menu item (ie: with a radio bullet or equivalent
988 : : * indication). The item should be marked as 'selected' when the string
989 : : * state is equal to the value of the @target property.
990 : : *
991 : : * See g_menu_item_set_action_and_target() or
992 : : * g_menu_item_set_detailed_action() for two equivalent calls that are
993 : : * probably more convenient for most uses.
994 : : *
995 : : * Since: 2.32
996 : : */
997 : : void
998 : 7 : g_menu_item_set_action_and_target_value (GMenuItem *menu_item,
999 : : const gchar *action,
1000 : : GVariant *target_value)
1001 : : {
1002 : : GVariant *action_value;
1003 : :
1004 : 7 : if (action != NULL)
1005 : : {
1006 : 7 : action_value = g_variant_new_string (action);
1007 : : }
1008 : : else
1009 : : {
1010 : 0 : action_value = NULL;
1011 : 0 : target_value = NULL;
1012 : : }
1013 : :
1014 : 7 : g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ACTION, action_value);
1015 : 7 : g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_TARGET, target_value);
1016 : 7 : }
1017 : :
1018 : : /**
1019 : : * g_menu_item_set_action_and_target:
1020 : : * @menu_item: a #GMenuItem
1021 : : * @action: (nullable): the name of the action for this item
1022 : : * @format_string: (nullable): a GVariant format string
1023 : : * @...: positional parameters, as per @format_string
1024 : : *
1025 : : * Sets or unsets the "action" and "target" attributes of @menu_item.
1026 : : *
1027 : : * If @action is %NULL then both the "action" and "target" attributes
1028 : : * are unset (and @format_string is ignored along with the positional
1029 : : * parameters).
1030 : : *
1031 : : * If @action is non-%NULL then the "action" attribute is set.
1032 : : * @format_string is then inspected. If it is non-%NULL then the proper
1033 : : * position parameters are collected to create a #GVariant instance to
1034 : : * use as the target value. If it is %NULL then the positional
1035 : : * parameters are ignored and the "target" attribute is unset.
1036 : : *
1037 : : * See also g_menu_item_set_action_and_target_value() for an equivalent
1038 : : * call that directly accepts a #GVariant. See
1039 : : * g_menu_item_set_detailed_action() for a more convenient version that
1040 : : * works with string-typed targets.
1041 : : *
1042 : : * See also g_menu_item_set_action_and_target_value() for a
1043 : : * description of the semantics of the action and target attributes.
1044 : : *
1045 : : * Since: 2.32
1046 : : */
1047 : : void
1048 : 1 : g_menu_item_set_action_and_target (GMenuItem *menu_item,
1049 : : const gchar *action,
1050 : : const gchar *format_string,
1051 : : ...)
1052 : : {
1053 : : GVariant *value;
1054 : :
1055 : 1 : if (format_string != NULL)
1056 : : {
1057 : : va_list ap;
1058 : :
1059 : 1 : va_start (ap, format_string);
1060 : 1 : value = g_variant_new_va (format_string, NULL, &ap);
1061 : 1 : va_end (ap);
1062 : : }
1063 : : else
1064 : 0 : value = NULL;
1065 : :
1066 : 1 : g_menu_item_set_action_and_target_value (menu_item, action, value);
1067 : 1 : }
1068 : :
1069 : : /**
1070 : : * g_menu_item_set_detailed_action:
1071 : : * @menu_item: a #GMenuItem
1072 : : * @detailed_action: the "detailed" action string
1073 : : *
1074 : : * Sets the "action" and possibly the "target" attribute of @menu_item.
1075 : : *
1076 : : * The format of @detailed_action is the same format parsed by
1077 : : * g_action_parse_detailed_name().
1078 : : *
1079 : : * See g_menu_item_set_action_and_target() or
1080 : : * g_menu_item_set_action_and_target_value() for more flexible (but
1081 : : * slightly less convenient) alternatives.
1082 : : *
1083 : : * See also g_menu_item_set_action_and_target_value() for a description of
1084 : : * the semantics of the action and target attributes.
1085 : : *
1086 : : * Since: 2.32
1087 : : */
1088 : : void
1089 : 6 : g_menu_item_set_detailed_action (GMenuItem *menu_item,
1090 : : const gchar *detailed_action)
1091 : : {
1092 : 6 : GError *error = NULL;
1093 : : GVariant *target;
1094 : : gchar *name;
1095 : :
1096 : 6 : if (!g_action_parse_detailed_name (detailed_action, &name, &target, &error))
1097 : 0 : g_error ("g_menu_item_set_detailed_action: %s", error->message);
1098 : :
1099 : 6 : g_menu_item_set_action_and_target_value (menu_item, name, target);
1100 : 6 : if (target)
1101 : 4 : g_variant_unref (target);
1102 : 6 : g_free (name);
1103 : 6 : }
1104 : :
1105 : : /**
1106 : : * g_menu_item_new:
1107 : : * @label: (nullable): the section label, or %NULL
1108 : : * @detailed_action: (nullable): the detailed action string, or %NULL
1109 : : *
1110 : : * Creates a new #GMenuItem.
1111 : : *
1112 : : * If @label is non-%NULL it is used to set the "label" attribute of the
1113 : : * new item.
1114 : : *
1115 : : * If @detailed_action is non-%NULL it is used to set the "action" and
1116 : : * possibly the "target" attribute of the new item. See
1117 : : * g_menu_item_set_detailed_action() for more information.
1118 : : *
1119 : : * Returns: a new #GMenuItem
1120 : : *
1121 : : * Since: 2.32
1122 : : */
1123 : : GMenuItem *
1124 : 36 : g_menu_item_new (const gchar *label,
1125 : : const gchar *detailed_action)
1126 : : {
1127 : : GMenuItem *menu_item;
1128 : :
1129 : 36 : menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1130 : :
1131 : 36 : if (label != NULL)
1132 : 25 : g_menu_item_set_label (menu_item, label);
1133 : :
1134 : 36 : if (detailed_action != NULL)
1135 : 6 : g_menu_item_set_detailed_action (menu_item, detailed_action);
1136 : :
1137 : 36 : return menu_item;
1138 : : }
1139 : :
1140 : : /**
1141 : : * g_menu_item_new_submenu:
1142 : : * @label: (nullable): the section label, or %NULL
1143 : : * @submenu: a #GMenuModel with the items of the submenu
1144 : : *
1145 : : * Creates a new #GMenuItem representing a submenu.
1146 : : *
1147 : : * This is a convenience API around g_menu_item_new() and
1148 : : * g_menu_item_set_submenu().
1149 : : *
1150 : : * Returns: a new #GMenuItem
1151 : : *
1152 : : * Since: 2.32
1153 : : */
1154 : : GMenuItem *
1155 : 6 : g_menu_item_new_submenu (const gchar *label,
1156 : : GMenuModel *submenu)
1157 : : {
1158 : : GMenuItem *menu_item;
1159 : :
1160 : 6 : menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1161 : :
1162 : 6 : if (label != NULL)
1163 : 6 : g_menu_item_set_label (menu_item, label);
1164 : :
1165 : 6 : g_menu_item_set_submenu (menu_item, submenu);
1166 : :
1167 : 6 : return menu_item;
1168 : : }
1169 : :
1170 : : /**
1171 : : * g_menu_item_new_section:
1172 : : * @label: (nullable): the section label, or %NULL
1173 : : * @section: a #GMenuModel with the items of the section
1174 : : *
1175 : : * Creates a new #GMenuItem representing a section.
1176 : : *
1177 : : * This is a convenience API around g_menu_item_new() and
1178 : : * g_menu_item_set_section().
1179 : : *
1180 : : * The effect of having one menu appear as a section of another is
1181 : : * exactly as it sounds: the items from @section become a direct part of
1182 : : * the menu that @menu_item is added to.
1183 : : *
1184 : : * Visual separation is typically displayed between two non-empty
1185 : : * sections. If @label is non-%NULL then it will be incorporated into
1186 : : * this visual indication. This allows for labeled subsections of a
1187 : : * menu.
1188 : : *
1189 : : * As a simple example, consider a typical "Edit" menu from a simple
1190 : : * program. It probably contains an "Undo" and "Redo" item, followed by
1191 : : * a separator, followed by "Cut", "Copy" and "Paste".
1192 : : *
1193 : : * This would be accomplished by creating three #GMenu instances. The
1194 : : * first would be populated with the "Undo" and "Redo" items, and the
1195 : : * second with the "Cut", "Copy" and "Paste" items. The first and
1196 : : * second menus would then be added as submenus of the third. In XML
1197 : : * format, this would look something like the following:
1198 : : * |[
1199 : : * <menu id='edit-menu'>
1200 : : * <section>
1201 : : * <item label='Undo'/>
1202 : : * <item label='Redo'/>
1203 : : * </section>
1204 : : * <section>
1205 : : * <item label='Cut'/>
1206 : : * <item label='Copy'/>
1207 : : * <item label='Paste'/>
1208 : : * </section>
1209 : : * </menu>
1210 : : * ]|
1211 : : *
1212 : : * The following example is exactly equivalent. It is more illustrative
1213 : : * of the exact relationship between the menus and items (keeping in
1214 : : * mind that the 'link' element defines a new menu that is linked to the
1215 : : * containing one). The style of the second example is more verbose and
1216 : : * difficult to read (and therefore not recommended except for the
1217 : : * purpose of understanding what is really going on).
1218 : : * |[
1219 : : * <menu id='edit-menu'>
1220 : : * <item>
1221 : : * <link name='section'>
1222 : : * <item label='Undo'/>
1223 : : * <item label='Redo'/>
1224 : : * </link>
1225 : : * </item>
1226 : : * <item>
1227 : : * <link name='section'>
1228 : : * <item label='Cut'/>
1229 : : * <item label='Copy'/>
1230 : : * <item label='Paste'/>
1231 : : * </link>
1232 : : * </item>
1233 : : * </menu>
1234 : : * ]|
1235 : : *
1236 : : * Returns: a new #GMenuItem
1237 : : *
1238 : : * Since: 2.32
1239 : : */
1240 : : GMenuItem *
1241 : 6 : g_menu_item_new_section (const gchar *label,
1242 : : GMenuModel *section)
1243 : : {
1244 : : GMenuItem *menu_item;
1245 : :
1246 : 6 : menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1247 : :
1248 : 6 : if (label != NULL)
1249 : 6 : g_menu_item_set_label (menu_item, label);
1250 : :
1251 : 6 : g_menu_item_set_section (menu_item, section);
1252 : :
1253 : 6 : return menu_item;
1254 : : }
1255 : :
1256 : : /**
1257 : : * g_menu_item_new_from_model:
1258 : : * @model: a #GMenuModel
1259 : : * @item_index: the index of an item in @model
1260 : : *
1261 : : * Creates a #GMenuItem as an exact copy of an existing menu item in a
1262 : : * #GMenuModel.
1263 : : *
1264 : : * @item_index must be valid (ie: be sure to call
1265 : : * g_menu_model_get_n_items() first).
1266 : : *
1267 : : * Returns: a new #GMenuItem.
1268 : : *
1269 : : * Since: 2.34
1270 : : */
1271 : : GMenuItem *
1272 : 1 : g_menu_item_new_from_model (GMenuModel *model,
1273 : : gint item_index)
1274 : : {
1275 : 1 : GMenuModelClass *class = G_MENU_MODEL_GET_CLASS (model);
1276 : : GMenuItem *menu_item;
1277 : :
1278 : 1 : menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1279 : :
1280 : : /* With some trickery we can be pretty efficient.
1281 : : *
1282 : : * A GMenuModel must either implement iterate_item_attributes() or
1283 : : * get_item_attributes(). If it implements get_item_attributes() then
1284 : : * we are in luck -- we can just take a reference on the returned
1285 : : * hashtable and mark ourselves as copy-on-write.
1286 : : *
1287 : : * In the case that the model is based on get_item_attributes (which
1288 : : * is the case for both GMenu and GDBusMenuModel) then this is
1289 : : * basically just g_hash_table_ref().
1290 : : */
1291 : 1 : if (class->get_item_attributes)
1292 : : {
1293 : 1 : GHashTable *attributes = NULL;
1294 : :
1295 : 1 : class->get_item_attributes (model, item_index, &attributes);
1296 : 1 : if (attributes)
1297 : : {
1298 : 1 : g_hash_table_unref (menu_item->attributes);
1299 : 1 : menu_item->attributes = attributes;
1300 : 1 : menu_item->cow = TRUE;
1301 : : }
1302 : : }
1303 : : else
1304 : : {
1305 : : GMenuAttributeIter *iter;
1306 : : const gchar *attribute;
1307 : : GVariant *value;
1308 : :
1309 : 0 : iter = g_menu_model_iterate_item_attributes (model, item_index);
1310 : 0 : while (g_menu_attribute_iter_get_next (iter, &attribute, &value))
1311 : 0 : g_hash_table_insert (menu_item->attributes, g_strdup (attribute), value);
1312 : 0 : g_object_unref (iter);
1313 : : }
1314 : :
1315 : : /* Same story for the links... */
1316 : 1 : if (class->get_item_links)
1317 : : {
1318 : 1 : GHashTable *links = NULL;
1319 : :
1320 : 1 : class->get_item_links (model, item_index, &links);
1321 : 1 : if (links)
1322 : : {
1323 : 1 : g_hash_table_unref (menu_item->links);
1324 : 1 : menu_item->links = links;
1325 : 1 : menu_item->cow = TRUE;
1326 : : }
1327 : : }
1328 : : else
1329 : : {
1330 : : GMenuLinkIter *iter;
1331 : : const gchar *link;
1332 : : GMenuModel *value;
1333 : :
1334 : 0 : iter = g_menu_model_iterate_item_links (model, item_index);
1335 : 0 : while (g_menu_link_iter_get_next (iter, &link, &value))
1336 : 0 : g_hash_table_insert (menu_item->links, g_strdup (link), value);
1337 : 0 : g_object_unref (iter);
1338 : : }
1339 : :
1340 : 1 : return menu_item;
1341 : : }
1342 : :
1343 : : /**
1344 : : * g_menu_item_set_icon:
1345 : : * @menu_item: a #GMenuItem
1346 : : * @icon: a #GIcon, or %NULL
1347 : : *
1348 : : * Sets (or unsets) the icon on @menu_item.
1349 : : *
1350 : : * This call is the same as calling g_icon_serialize() and using the
1351 : : * result as the value to g_menu_item_set_attribute_value() for
1352 : : * %G_MENU_ATTRIBUTE_ICON.
1353 : : *
1354 : : * This API is only intended for use with "noun" menu items; things like
1355 : : * bookmarks or applications in an "Open With" menu. Don't use it on
1356 : : * menu items corresponding to verbs (eg: stock icons for 'Save' or
1357 : : * 'Quit').
1358 : : *
1359 : : * If @icon is %NULL then the icon is unset.
1360 : : *
1361 : : * Since: 2.38
1362 : : **/
1363 : : void
1364 : 1 : g_menu_item_set_icon (GMenuItem *menu_item,
1365 : : GIcon *icon)
1366 : : {
1367 : : GVariant *value;
1368 : :
1369 : 1 : g_return_if_fail (G_IS_MENU_ITEM (menu_item));
1370 : 1 : g_return_if_fail (icon == NULL || G_IS_ICON (icon));
1371 : :
1372 : 1 : if (icon != NULL)
1373 : 1 : value = g_icon_serialize (icon);
1374 : : else
1375 : 0 : value = NULL;
1376 : :
1377 : 1 : g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ICON, value);
1378 : 1 : if (value)
1379 : 1 : g_variant_unref (value);
1380 : : }
|