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