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