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