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