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 G_DEFINE_TYPE (GMenu, g_menu, G_TYPE_MENU_MODEL)
77 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 g_menu_is_mutable (GMenuModel *model)
87 {
88 GMenu *menu = G_MENU (model);
89
90 return menu->mutable;
91 }
92
93 static gint
94 g_menu_get_n_items (GMenuModel *model)
95 {
96 GMenu *menu = G_MENU (model);
97
98 return menu->items->len;
99 }
100
101 static void
102 g_menu_get_item_attributes (GMenuModel *model,
103 gint position,
104 GHashTable **table)
105 {
106 GMenu *menu = G_MENU (model);
107
108 *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).attributes);
109 }
110
111 static void
112 g_menu_get_item_links (GMenuModel *model,
113 gint position,
114 GHashTable **table)
115 {
116 GMenu *menu = G_MENU (model);
117
118 *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).links);
119 }
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 g_menu_insert_item (GMenu *menu,
149 gint position,
150 GMenuItem *item)
151 {
152 struct item new_item;
153
154 g_return_if_fail (G_IS_MENU (menu));
155 g_return_if_fail (G_IS_MENU_ITEM (item));
156
157 if (position < 0 || (guint) position > menu->items->len)
158 position = menu->items->len;
159
160 new_item.attributes = g_hash_table_ref (item->attributes);
161 new_item.links = g_hash_table_ref (item->links);
162 item->cow = TRUE;
163
164 g_array_insert_val (menu->items, position, new_item);
165 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 g_menu_prepend_item (GMenu *menu,
181 GMenuItem *item)
182 {
183 g_menu_insert_item (menu, 0, item);
184 }
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 g_menu_append_item (GMenu *menu,
199 GMenuItem *item)
200 {
201 g_menu_insert_item (menu, -1, item);
202 }
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 g_menu_freeze (GMenu *menu)
221 {
222 g_return_if_fail (G_IS_MENU (menu));
223
224 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 g_menu_new (void)
240 {
241 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 g_menu_insert (GMenu *menu,
259 gint position,
260 const gchar *label,
261 const gchar *detailed_action)
262 {
263 GMenuItem *menu_item;
264
265 menu_item = g_menu_item_new (label, detailed_action);
266 g_menu_insert_item (menu, position, menu_item);
267 g_object_unref (menu_item);
268 }
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 g_menu_prepend (GMenu *menu,
284 const gchar *label,
285 const gchar *detailed_action)
286 {
287 g_menu_insert (menu, 0, label, detailed_action);
288 }
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 g_menu_append (GMenu *menu,
304 const gchar *label,
305 const gchar *detailed_action)
306 {
307 g_menu_insert (menu, -1, label, detailed_action);
308 }
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 g_menu_insert_section (GMenu *menu,
325 gint position,
326 const gchar *label,
327 GMenuModel *section)
328 {
329 GMenuItem *menu_item;
330
331 menu_item = g_menu_item_new_section (label, section);
332 g_menu_insert_item (menu, position, menu_item);
333 g_object_unref (menu_item);
334 }
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 g_menu_prepend_section (GMenu *menu,
351 const gchar *label,
352 GMenuModel *section)
353 {
354 g_menu_insert_section (menu, 0, label, section);
355 }
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 g_menu_append_section (GMenu *menu,
371 const gchar *label,
372 GMenuModel *section)
373 {
374 g_menu_insert_section (menu, -1, label, section);
375 }
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 g_menu_insert_submenu (GMenu *menu,
392 gint position,
393 const gchar *label,
394 GMenuModel *submenu)
395 {
396 GMenuItem *menu_item;
397
398 menu_item = g_menu_item_new_submenu (label, submenu);
399 g_menu_insert_item (menu, position, menu_item);
400 g_object_unref (menu_item);
401 }
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 g_menu_prepend_submenu (GMenu *menu,
417 const gchar *label,
418 GMenuModel *submenu)
419 {
420 g_menu_insert_submenu (menu, 0, label, submenu);
421 }
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 g_menu_append_submenu (GMenu *menu,
437 const gchar *label,
438 GMenuModel *submenu)
439 {
440 g_menu_insert_submenu (menu, -1, label, submenu);
441 }
442
443 static void
444 g_menu_clear_item (struct item *item)
445 {
446 if (item->attributes != NULL)
447 g_hash_table_unref (item->attributes);
448 if (item->links != NULL)
449 g_hash_table_unref (item->links);
450 }
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 g_menu_remove (GMenu *menu,
472 gint position)
473 {
474 g_return_if_fail (G_IS_MENU (menu));
475 g_return_if_fail (0 <= position && (guint) position < menu->items->len);
476
477 g_menu_clear_item (&g_array_index (menu->items, struct item, position));
478 g_array_remove_index (menu->items, position);
479 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 g_menu_remove_all (GMenu *menu)
492 {
493 gint i, n;
494
495 g_return_if_fail (G_IS_MENU (menu));
496 n = menu->items->len;
497
498 for (i = 0; i < n; i++)
499 g_menu_clear_item (&g_array_index (menu->items, struct item, i));
500 g_array_set_size (menu->items, 0);
501
502 g_menu_model_items_changed (G_MENU_MODEL (menu), 0, n, 0);
503 }
504
505 static void
506 g_menu_finalize (GObject *object)
507 {
508 GMenu *menu = G_MENU (object);
509 struct item *items;
510 gint n_items;
511 gint i;
512
513 n_items = menu->items->len;
514 items = (struct item *) g_array_free (menu->items, FALSE);
515 for (i = 0; i < n_items; i++)
516 g_menu_clear_item (&items[i]);
517 g_free (items);
518
519 G_OBJECT_CLASS (g_menu_parent_class)
520 ->finalize (object);
521 }
522
523 static void
524 g_menu_init (GMenu *menu)
525 {
526 menu->items = g_array_new (FALSE, FALSE, sizeof (struct item));
527 menu->mutable = TRUE;
528 }
529
530 static void
531 g_menu_class_init (GMenuClass *class)
532 {
533 GMenuModelClass *model_class = G_MENU_MODEL_CLASS (class);
534 GObjectClass *object_class = G_OBJECT_CLASS (class);
535
536 object_class->finalize = g_menu_finalize;
537
538 model_class->is_mutable = g_menu_is_mutable;
539 model_class->get_n_items = g_menu_get_n_items;
540 model_class->get_item_attributes = g_menu_get_item_attributes;
541 model_class->get_item_links = g_menu_get_item_links;
542 }
543
544
545 static void
546 g_menu_item_clear_cow (GMenuItem *menu_item)
547 {
548 if (menu_item->cow)
549 {
550 GHashTableIter iter;
551 GHashTable *new;
552 gpointer key;
553 gpointer val;
554
555 new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
556 g_hash_table_iter_init (&iter, menu_item->attributes);
557 while (g_hash_table_iter_next (&iter, &key, &val))
558 g_hash_table_insert (new, g_strdup (key), g_variant_ref (val));
559 g_hash_table_unref (menu_item->attributes);
560 menu_item->attributes = new;
561
562 new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
563 g_hash_table_iter_init (&iter, menu_item->links);
564 while (g_hash_table_iter_next (&iter, &key, &val))
565 g_hash_table_insert (new, g_strdup (key), g_object_ref (val));
566 g_hash_table_unref (menu_item->links);
567 menu_item->links = new;
568
569 menu_item->cow = FALSE;
570 }
571 }
572
573 static void
574 g_menu_item_finalize (GObject *object)
575 {
576 GMenuItem *menu_item = G_MENU_ITEM (object);
577
578 g_hash_table_unref (menu_item->attributes);
579 g_hash_table_unref (menu_item->links);
580
581 G_OBJECT_CLASS (g_menu_item_parent_class)
582 ->finalize (object);
583 }
584
585 static void
586 g_menu_item_init (GMenuItem *menu_item)
587 {
588 menu_item->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
589 menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
590 menu_item->cow = FALSE;
591 }
592
593 static void
594 g_menu_item_class_init (GMenuItemClass *class)
595 {
596 class->finalize = g_menu_item_finalize;
597 }
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 valid_attribute_name (const gchar *name)
608 {
609 gint i;
610
611 if (!g_ascii_islower (name[0]))
612 return FALSE;
613
614 for (i = 1; name[i]; i++)
615 {
616 if (name[i] != '-' &&
617 !g_ascii_islower (name[i]) &&
618 !g_ascii_isdigit (name[i]))
619 return FALSE;
620
621 if (name[i] == '-' && name[i + 1] == '-')
622 return FALSE;
623 }
624
625 if (name[i - 1] == '-')
626 return FALSE;
627
628 if (i > 1024)
629 return FALSE;
630
631 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 g_menu_item_set_attribute_value (GMenuItem *menu_item,
664 const gchar *attribute,
665 GVariant *value)
666 {
667 g_return_if_fail (G_IS_MENU_ITEM (menu_item));
668 g_return_if_fail (attribute != NULL);
669 g_return_if_fail (valid_attribute_name (attribute));
670
671 g_menu_item_clear_cow (menu_item);
672
673 if (value != NULL)
674 g_hash_table_insert (menu_item->attributes, g_strdup (attribute), g_variant_ref_sink (value));
675 else
676 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 g_menu_item_set_attribute (GMenuItem *menu_item,
708 const gchar *attribute,
709 const gchar *format_string,
710 ...)
711 {
712 GVariant *value;
713
714 if (format_string != NULL)
715 {
716 va_list ap;
717
718 va_start (ap, format_string);
719 value = g_variant_new_va (format_string, NULL, &ap);
720 va_end (ap);
721 }
722 else
723 value = NULL;
724
725 g_menu_item_set_attribute_value (menu_item, attribute, value);
726 }
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 g_menu_item_set_link (GMenuItem *menu_item,
749 const gchar *link,
750 GMenuModel *model)
751 {
752 g_return_if_fail (G_IS_MENU_ITEM (menu_item));
753 g_return_if_fail (link != NULL);
754 g_return_if_fail (valid_attribute_name (link));
755
756 g_menu_item_clear_cow (menu_item);
757
758 if (model != NULL)
759 g_hash_table_insert (menu_item->links, g_strdup (link), g_object_ref (model));
760 else
761 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 g_menu_item_get_attribute_value (GMenuItem *menu_item,
782 const gchar *attribute,
783 const GVariantType *expected_type)
784 {
785 GVariant *value;
786
787 g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), NULL);
788 g_return_val_if_fail (attribute != NULL, NULL);
789
790 value = g_hash_table_lookup (menu_item->attributes, attribute);
791
792 if (value != NULL)
793 {
794 if (expected_type == NULL || g_variant_is_of_type (value, expected_type))
795 g_variant_ref (value);
796 else
797 value = NULL;
798 }
799
800 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 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 g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), FALSE);
835 g_return_val_if_fail (attribute != NULL, FALSE);
836 g_return_val_if_fail (format_string != NULL, FALSE);
837
838 value = g_hash_table_lookup (menu_item->attributes, attribute);
839
840 if (value == NULL)
841 return FALSE;
842
843 if (!g_variant_check_format_string (value, format_string, FALSE))
844 return FALSE;
845
846 va_start (ap, format_string);
847 g_variant_get_va (value, format_string, NULL, &ap);
848 va_end (ap);
849
850 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 g_menu_item_get_link (GMenuItem *menu_item,
866 const gchar *link)
867 {
868 GMenuModel *model;
869
870 g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), NULL);
871 g_return_val_if_fail (link != NULL, NULL);
872 g_return_val_if_fail (valid_attribute_name (link), NULL);
873
874 model = g_hash_table_lookup (menu_item->links, link);
875
876 if (model)
877 g_object_ref (model);
878
879 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 g_menu_item_set_label (GMenuItem *menu_item,
896 const gchar *label)
897 {
898 GVariant *value;
899
900 if (label != NULL)
901 value = g_variant_new_string (label);
902 else
903 value = NULL;
904
905 g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_LABEL, value);
906 }
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 g_menu_item_set_submenu (GMenuItem *menu_item,
925 GMenuModel *submenu)
926 {
927 g_menu_item_set_link (menu_item, G_MENU_LINK_SUBMENU, submenu);
928 }
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 g_menu_item_set_section (GMenuItem *menu_item,
947 GMenuModel *section)
948 {
949 g_menu_item_set_link (menu_item, G_MENU_LINK_SECTION, section);
950 }
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 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 if (action != NULL)
1005 {
1006 action_value = g_variant_new_string (action);
1007 }
1008 else
1009 {
1010 action_value = NULL;
1011 target_value = NULL;
1012 }
1013
1014 g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ACTION, action_value);
1015 g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_TARGET, target_value);
1016 }
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 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 if (format_string != NULL)
1056 {
1057 va_list ap;
1058
1059 va_start (ap, format_string);
1060 value = g_variant_new_va (format_string, NULL, &ap);
1061 va_end (ap);
1062 }
1063 else
1064 value = NULL;
1065
1066 g_menu_item_set_action_and_target_value (menu_item, action, value);
1067 }
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 g_menu_item_set_detailed_action (GMenuItem *menu_item,
1090 const gchar *detailed_action)
1091 {
1092 GError *error = NULL;
1093 GVariant *target;
1094 gchar *name;
1095
1096 if (!g_action_parse_detailed_name (detailed_action, &name, &target, &error))
1097 g_error ("g_menu_item_set_detailed_action: %s", error->message);
1098
1099 g_menu_item_set_action_and_target_value (menu_item, name, target);
1100 if (target)
1101 g_variant_unref (target);
1102 g_free (name);
1103 }
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 g_menu_item_new (const gchar *label,
1125 const gchar *detailed_action)
1126 {
1127 GMenuItem *menu_item;
1128
1129 menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1130
1131 if (label != NULL)
1132 g_menu_item_set_label (menu_item, label);
1133
1134 if (detailed_action != NULL)
1135 g_menu_item_set_detailed_action (menu_item, detailed_action);
1136
1137 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 g_menu_item_new_submenu (const gchar *label,
1156 GMenuModel *submenu)
1157 {
1158 GMenuItem *menu_item;
1159
1160 menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1161
1162 if (label != NULL)
1163 g_menu_item_set_label (menu_item, label);
1164
1165 g_menu_item_set_submenu (menu_item, submenu);
1166
1167 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 encorporated 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 g_menu_item_new_section (const gchar *label,
1242 GMenuModel *section)
1243 {
1244 GMenuItem *menu_item;
1245
1246 menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1247
1248 if (label != NULL)
1249 g_menu_item_set_label (menu_item, label);
1250
1251 g_menu_item_set_section (menu_item, section);
1252
1253 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 g_menu_item_new_from_model (GMenuModel *model,
1273 gint item_index)
1274 {
1275 GMenuModelClass *class = G_MENU_MODEL_GET_CLASS (model);
1276 GMenuItem *menu_item;
1277
1278 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 if (class->get_item_attributes)
1292 {
1293 GHashTable *attributes = NULL;
1294
1295 class->get_item_attributes (model, item_index, &attributes);
1296 if (attributes)
1297 {
1298 g_hash_table_unref (menu_item->attributes);
1299 menu_item->attributes = attributes;
1300 menu_item->cow = TRUE;
1301 }
1302 }
1303 else
1304 {
1305 GMenuAttributeIter *iter;
1306 const gchar *attribute;
1307 GVariant *value;
1308
1309 iter = g_menu_model_iterate_item_attributes (model, item_index);
1310 while (g_menu_attribute_iter_get_next (iter, &attribute, &value))
1311 g_hash_table_insert (menu_item->attributes, g_strdup (attribute), value);
1312 g_object_unref (iter);
1313 }
1314
1315 /* Same story for the links... */
1316 if (class->get_item_links)
1317 {
1318 GHashTable *links = NULL;
1319
1320 class->get_item_links (model, item_index, &links);
1321 if (links)
1322 {
1323 g_hash_table_unref (menu_item->links);
1324 menu_item->links = links;
1325 menu_item->cow = TRUE;
1326 }
1327 }
1328 else
1329 {
1330 GMenuLinkIter *iter;
1331 const gchar *link;
1332 GMenuModel *value;
1333
1334 iter = g_menu_model_iterate_item_links (model, item_index);
1335 while (g_menu_link_iter_get_next (iter, &link, &value))
1336 g_hash_table_insert (menu_item->links, g_strdup (link), value);
1337 g_object_unref (iter);
1338 }
1339
1340 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 g_menu_item_set_icon (GMenuItem *menu_item,
1365 GIcon *icon)
1366 {
1367 GVariant *value;
1368
1369 g_return_if_fail (G_IS_MENU_ITEM (menu_item));
1370 g_return_if_fail (icon == NULL || G_IS_ICON (icon));
1371
1372 if (icon != NULL)
1373 value = g_icon_serialize (icon);
1374 else
1375 value = NULL;
1376
1377 g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ICON, value);
1378 if (value)
1379 g_variant_unref (value);
1380 }