d11f7a3d8fa0a072842a7da2a767dae982189be9
[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 void
151 g_menu_insert_item (GMenu     *menu,
152                     gint       position,
153                     GMenuItem *item)
154 {
155   struct item new_item;
156
157   g_return_if_fail (G_IS_MENU (menu));
158   g_return_if_fail (G_IS_MENU_ITEM (item));
159
160   if (position < 0 || position > menu->items->len)
161     position = menu->items->len;
162
163   new_item.attributes = g_hash_table_ref (item->attributes);
164   new_item.links = g_hash_table_ref (item->links);
165   item->cow = TRUE;
166
167   g_array_insert_val (menu->items, position, new_item);
168   g_menu_model_items_changed (G_MENU_MODEL (menu), position, 0, 1);
169 }
170
171 /**
172  * g_menu_prepend_item:
173  * @menu: a #GMenu
174  * @item: a #GMenuItem to prepend
175  *
176  * Prepends @item to the start of @menu.
177  *
178  * See g_menu_insert_item() for more information.
179  **/
180 void
181 g_menu_prepend_item (GMenu     *menu,
182                      GMenuItem *item)
183 {
184   g_menu_insert_item (menu, 0, item);
185 }
186
187 /**
188  * g_menu_append_item:
189  * @menu: a #GMenu
190  * @item: a #GMenuItem to append
191  *
192  * Appends @item to the end of @menu.
193  *
194  * See g_menu_insert_item() for more information.
195  **/
196 void
197 g_menu_append_item (GMenu     *menu,
198                     GMenuItem *item)
199 {
200   g_menu_insert_item (menu, -1, item);
201 }
202
203 /**
204  * g_menu_freeze:
205  * @menu: a #GMenu
206  *
207  * Marks @menu as frozen.
208  *
209  * After the menu is frozen, it is an error to attempt to make any
210  * changes to it.  In effect this means that the #GMenu API must no
211  * longer be used.
212  *
213  * This function causes g_menu_model_is_mutable() to begin returning
214  * %FALSE, which has some positive performance implications.
215  **/
216 void
217 g_menu_freeze (GMenu *menu)
218 {
219   g_return_if_fail (G_IS_MENU (menu));
220
221   menu->mutable = FALSE;
222 }
223
224 /**
225  * g_menu_new:
226  *
227  * Creates a new #GMenu.
228  *
229  * The new menu has no items.
230  *
231  * Returns: a new #GMenu
232  **/
233 GMenu *
234 g_menu_new (void)
235 {
236   return g_object_new (G_TYPE_MENU, NULL);
237 }
238
239 /**
240  * g_menu_insert:
241  * @menu: a #GMenu
242  * @position: the position at which to insert the item
243  * @label: (allow-none): the section label, or %NULL
244  * @detailed_action: (allow-none): the detailed action string, or %NULL
245  *
246  * Convenience function for inserting a normal menu item into @menu.
247  * Combine g_menu_new() and g_menu_insert_item() for a more flexible
248  * alternative.
249  **/
250 void
251 g_menu_insert (GMenu       *menu,
252                gint         position,
253                const gchar *label,
254                const gchar *detailed_action)
255 {
256   GMenuItem *menu_item;
257
258   menu_item = g_menu_item_new (label, detailed_action);
259   g_menu_insert_item (menu, position, menu_item);
260   g_object_unref (menu_item);
261 }
262
263 /**
264  * g_menu_prepend:
265  * @menu: a #GMenu
266  * @label: (allow-none): the section label, or %NULL
267  * @detailed_action: (allow-none): the detailed action string, or %NULL
268  *
269  * Convenience function for prepending a normal menu item to the start
270  * of @menu.  Combine g_menu_new() and g_menu_insert_item() for a more
271  * flexible alternative.
272  **/
273 void
274 g_menu_prepend (GMenu       *menu,
275                 const gchar *label,
276                 const gchar *detailed_action)
277 {
278   g_menu_insert (menu, 0, label, detailed_action);
279 }
280
281 /**
282  * g_menu_append:
283  * @menu: a #GMenu
284  * @label: (allow-none): the section label, or %NULL
285  * @detailed_action: (allow-none): the detailed action string, or %NULL
286  *
287  * Convenience function for appending a normal menu item to the end of
288  * @menu.  Combine g_menu_new() and g_menu_insert_item() for a more
289  * flexible alternative.
290  **/
291 void
292 g_menu_append (GMenu       *menu,
293                const gchar *label,
294                const gchar *detailed_action)
295 {
296   g_menu_insert (menu, -1, label, detailed_action);
297 }
298
299 /**
300  * g_menu_insert_section:
301  * @menu: a #GMenu
302  * @position: the position at which to insert the item
303  * @label: (allow-none): the section label, or %NULL
304  * @section: a #GMenuModel with the items of the section
305  *
306  * Convenience function for inserting a section menu item into @menu.
307  * Combine g_menu_new_section() and g_menu_insert_item() for a more
308  * flexible alternative.
309  **/
310 void
311 g_menu_insert_section (GMenu       *menu,
312                        gint         position,
313                        const gchar *label,
314                        GMenuModel  *section)
315 {
316   GMenuItem *menu_item;
317
318   menu_item = g_menu_item_new_section (label, section);
319   g_menu_insert_item (menu, position, menu_item);
320   g_object_unref (menu_item);
321 }
322
323
324 /**
325  * g_menu_prepend_section:
326  * @menu: a #GMenu
327  * @label: (allow-none): the section label, or %NULL
328  * @section: a #GMenuModel with the items of the section
329  *
330  * Convenience function for prepending a section menu item to the start
331  * of @menu.  Combine g_menu_new_section() and g_menu_insert_item() for
332  * a more flexible alternative.
333  **/
334 void
335 g_menu_prepend_section (GMenu       *menu,
336                         const gchar *label,
337                         GMenuModel  *section)
338 {
339   g_menu_insert_section (menu, 0, label, section);
340 }
341
342 /**
343  * g_menu_append_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 appending a section menu item to the end of
349  * @menu.  Combine g_menu_new_section() and g_menu_insert_item() for a
350  * more flexible alternative.
351  **/
352 void
353 g_menu_append_section (GMenu       *menu,
354                        const gchar *label,
355                        GMenuModel  *section)
356 {
357   g_menu_insert_section (menu, -1, label, section);
358 }
359
360 /**
361  * g_menu_insert_submenu:
362  * @menu: a #GMenu
363  * @position: the position at which to insert the item
364  * @label: (allow-none): the section label, or %NULL
365  * @submenu: a #GMenuModel with the items of the submenu
366  *
367  * Convenience function for inserting a submenu menu item into @menu.
368  * Combine g_menu_new_submenu() and g_menu_insert_item() for a more
369  * flexible alternative.
370  **/
371 void
372 g_menu_insert_submenu (GMenu       *menu,
373                        gint         position,
374                        const gchar *label,
375                        GMenuModel  *submenu)
376 {
377   GMenuItem *menu_item;
378
379   menu_item = g_menu_item_new_submenu (label, submenu);
380   g_menu_insert_item (menu, position, menu_item);
381   g_object_unref (menu_item);
382 }
383
384 /**
385  * g_menu_prepend_submenu:
386  * @menu: a #GMenu
387  * @label: (allow-none): the section label, or %NULL
388  * @submenu: a #GMenuModel with the items of the submenu
389  *
390  * Convenience function for prepending a submenu menu item to the start
391  * of @menu.  Combine g_menu_new_submenu() and g_menu_insert_item() for
392  * a more flexible alternative.
393  **/
394 void
395 g_menu_prepend_submenu (GMenu       *menu,
396                         const gchar *label,
397                         GMenuModel  *submenu)
398 {
399   g_menu_insert_submenu (menu, 0, label, submenu);
400 }
401
402 /**
403  * g_menu_append_submenu:
404  * @menu: a #GMenu
405  * @label: (allow-none): the section label, or %NULL
406  * @submenu: a #GMenuModel with the items of the submenu
407  *
408  * Convenience function for appending a submenu menu item to the end of
409  * @menu.  Combine g_menu_new_submenu() and g_menu_insert_item() for a
410  * more flexible alternative.
411  **/
412 void
413 g_menu_append_submenu (GMenu       *menu,
414                        const gchar *label,
415                        GMenuModel  *submenu)
416 {
417   g_menu_insert_submenu (menu, -1, label, submenu);
418 }
419
420 static void
421 g_menu_clear_item (struct item *item)
422 {
423   if (item->attributes != NULL)
424     g_hash_table_unref (item->attributes);
425   if (item->links != NULL);
426     g_hash_table_unref (item->links);
427 }
428
429 /**
430  * g_menu_remove:
431  * @menu: a #GMenu
432  * @position: the position of the item to remove
433  *
434  * Removes an item from the menu.
435  *
436  * @position gives the index of the item to remove.
437  *
438  * It is an error if position is not in range the range from 0 to one
439  * less than the number of items in the menu.
440  *
441  * It is not possible to remove items by identity since items are added
442  * to the menu simply by copying their links and attributes (ie:
443  * identity of the item itself is not preserved).
444  **/
445 void
446 g_menu_remove (GMenu *menu,
447                gint   index_)
448 {
449   g_return_if_fail (G_IS_MENU (menu));
450   g_return_if_fail (0 <= index_ && index_ < menu->items->len);
451
452   g_menu_clear_item (&g_array_index (menu->items, struct item, index_));
453   g_array_remove_index (menu->items, index_);
454   g_menu_model_items_changed (G_MENU_MODEL (menu), index_, 1, 0);
455 }
456
457 static void
458 g_menu_finalize (GObject *object)
459 {
460   GMenu *menu = G_MENU (object);
461   struct item *items;
462   gint n_items;
463   gint i;
464
465   n_items = menu->items->len;
466   items = (struct item *) g_array_free (menu->items, FALSE);
467   for (i = 0; i < n_items; i++)
468     g_menu_clear_item (&items[i]);
469   g_free (items);
470
471   G_OBJECT_CLASS (g_menu_parent_class)
472     ->finalize (object);
473 }
474
475 static void
476 g_menu_init (GMenu *menu)
477 {
478   menu->items = g_array_new (FALSE, FALSE, sizeof (struct item));
479   menu->mutable = TRUE;
480 }
481
482 static void
483 g_menu_class_init (GMenuClass *class)
484 {
485   GMenuModelClass *model_class = G_MENU_MODEL_CLASS (class);
486   GObjectClass *object_class = G_OBJECT_CLASS (class);
487
488   object_class->finalize = g_menu_finalize;
489
490   model_class->is_mutable = g_menu_is_mutable;
491   model_class->get_n_items = g_menu_get_n_items;
492   model_class->get_item_attributes = g_menu_get_item_attributes;
493   model_class->get_item_links = g_menu_get_item_links;
494 }
495
496
497 static void
498 g_menu_item_clear_cow (GMenuItem *menu_item)
499 {
500   if (menu_item->cow)
501     {
502       GHashTableIter iter;
503       GHashTable *new;
504       gpointer key;
505       gpointer val;
506
507       new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
508       g_hash_table_iter_init (&iter, menu_item->attributes);
509       while (g_hash_table_iter_next (&iter, &key, &val))
510         g_hash_table_insert (new, g_strdup (key), g_variant_ref (val));
511       g_hash_table_unref (menu_item->attributes);
512       menu_item->attributes = new;
513
514       new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
515       g_hash_table_iter_init (&iter, menu_item->links);
516       while (g_hash_table_iter_next (&iter, &key, &val))
517         g_hash_table_insert (new, g_strdup (key), g_object_ref (val));
518       g_hash_table_unref (menu_item->links);
519       menu_item->links = new;
520
521       menu_item->cow = FALSE;
522     }
523 }
524
525 static void
526 g_menu_item_finalize (GObject *object)
527 {
528   GMenuItem *menu_item = G_MENU_ITEM (object);
529
530   g_hash_table_unref (menu_item->attributes);
531   g_hash_table_unref (menu_item->links);
532
533   G_OBJECT_CLASS (g_menu_item_parent_class)
534     ->finalize (object);
535 }
536
537 static void
538 g_menu_item_init (GMenuItem *menu_item)
539 {
540   menu_item->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
541   menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
542   menu_item->cow = FALSE;
543 }
544
545 static void
546 g_menu_item_class_init (GMenuItemClass *class)
547 {
548   class->finalize = g_menu_item_finalize;
549 }
550
551 /**
552  * g_menu_item_set_attribute_value:
553  * @menu_item: a #GMenuItem
554  * @attribute: the attribute to set
555  * @value: (allow-none): a #GVariant to use as the value, or %NULL
556  *
557  * Sets or unsets an attribute on @menu_item.
558  *
559  * The attribute to set or unset is specified by @attribute.
560  *
561  * If @value is non-%NULL then it is used as the new value for the
562  * attribute.  If @value is %NULL then the attribute is unset.
563  *
564  * See also g_menu_item_set_attribute() for a more convenient way to do
565  * the same.
566  **/
567 void
568 g_menu_item_set_attribute_value (GMenuItem   *menu_item,
569                                  const gchar *attribute,
570                                  GVariant    *value)
571 {
572   g_return_if_fail (G_IS_MENU_ITEM (menu_item));
573   g_return_if_fail (attribute != NULL);
574
575   g_menu_item_clear_cow (menu_item);
576
577   if (value != NULL)
578     g_hash_table_insert (menu_item->attributes, g_strdup (attribute), g_variant_ref_sink (value));
579   else
580     g_hash_table_remove (menu_item->attributes, attribute);
581 }
582
583 /**
584  * g_menu_item_set_attribute:
585  * @menu_item: a #GMenuItem
586  * @attribute: the attribute to set
587  * @format_string: (allow-none): a #GVariant format string, or %NULL
588  * @...: positional parameters, as per @format_string
589  *
590  * Sets or unsets an attribute on @menu_item.
591  *
592  * The attribute to set or unset is specified by @attribute.
593  *
594  * If @format_string is non-%NULL then the proper position parameters
595  * are collected to create a #GVariant instance to use as the attribute
596  * value.  If it is %NULL then the positional parameterrs are ignored
597  * and the named attribute is unset.
598  *
599  * See also g_menu_item_set_attribute_value() for an equivalent call
600  * that directly accepts a #GVariant.
601  **/
602 void
603 g_menu_item_set_attribute (GMenuItem   *menu_item,
604                            const gchar *attribute,
605                            const gchar *format_string,
606                            ...)
607 {
608   GVariant *value;
609
610   if (format_string != NULL)
611     {
612       va_list ap;
613
614       va_start (ap, format_string);
615       value = g_variant_new_va (format_string, NULL, &ap);
616       va_end (ap);
617     }
618   else
619     value = NULL;
620
621   g_menu_item_set_attribute_value (menu_item, attribute, value);
622 }
623
624 /**
625  * g_menu_item_set_link:
626  * @menu_item: a #GMenuItem
627  * @link: type of link to establish or unset
628  * @model: (allow-none): the #GMenuModel to link to (or %NULL to unset)
629  *
630  * Creates a link from @menu_item to @link if non-%NULL, or unsets it.
631  *
632  * Links are used to establish a relationship between a particular menu
633  * item and another menu.  For example, %G_MENU_LINK_SUBMENU is used to
634  * associate a submenu with a particular menu item.
635  */
636 void
637 g_menu_item_set_link (GMenuItem   *menu_item,
638                       const gchar *link,
639                       GMenuModel  *model)
640 {
641   g_return_if_fail (G_IS_MENU_ITEM (menu_item));
642   g_return_if_fail (link != NULL);
643
644   g_menu_item_clear_cow (menu_item);
645
646   if (model != NULL)
647     g_hash_table_insert (menu_item->links, g_strdup (link), g_object_ref (model));
648   else
649     g_hash_table_remove (menu_item->links, link);
650 }
651
652 /**
653  * g_menu_item_set_label:
654  * @menu_item: a #GMenuItem
655  * @label: (allow-none): the label to set, or %NULL to unset
656  *
657  * Sets or unsets the "label" attribute of @menu_item.
658  *
659  * If @label is non-%NULL it is used as the label for the menu item.  If
660  * it is %NULL then the label attribute is unset.
661  **/
662 void
663 g_menu_item_set_label (GMenuItem   *menu_item,
664                        const gchar *label)
665 {
666   GVariant *value;
667
668   if (label != NULL)
669     value = g_variant_new_string (label);
670   else
671     value = NULL;
672
673   g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_LABEL, value);
674 }
675
676 /**
677  * g_menu_item_set_submenu:
678  * @menu_item: a #GMenuItem
679  * @submenu: (allow-none): a #GMenuModel, or %NULL
680  *
681  * Sets or unsets the "submenu" link of @menu_item to @submenu.
682  *
683  * If @submenu is non-%NULL, it is linked to.  If it is %NULL then the
684  * link is unset.
685  *
686  * The effect of having one menu appear as a submenu of another is
687  * exactly as it sounds.
688  **/
689 void
690 g_menu_item_set_submenu (GMenuItem  *menu_item,
691                          GMenuModel *submenu)
692 {
693   g_menu_item_set_link (menu_item, G_MENU_LINK_SUBMENU, submenu);
694 }
695
696 /**
697  * g_menu_item_set_section:
698  * @menu_item: a #GMenuItem
699  * @section: (allow-none): a #GMenuModel, or %NULL
700  *
701  * Sets or unsets the "section" link of @menu_item to @section.
702  *
703  * The effect of having one menu appear as a section of another is
704  * exactly as it sounds: the items from @section become a direct part of
705  * the menu that @menu_item is added to.  See g_menu_item_new_section()
706  * for more information about what it means for a menu item to be a
707  * section.
708  **/
709 void
710 g_menu_item_set_section (GMenuItem  *menu_item,
711                          GMenuModel *section)
712 {
713   g_menu_item_set_link (menu_item, G_MENU_LINK_SECTION, section);
714 }
715
716 /**
717  * g_menu_item_set_action_and_target_value:
718  * @menu_item: a #GMenuItem
719  * @action: (allow-none): the name of the action for this item
720  * @target_value: (allow-none): a #GVariant to use as the action target
721  *
722  * Sets or unsets the "action" and "target" attributes of @menu_item.
723  *
724  * If @action is %NULL then both the "action" and "target" attributes
725  * are unset (and @target_value is ignored).
726  *
727  * If @action is non-%NULL then the "action" attribute is set.  The
728  * "target" attribute is then set to the value of @target_value if it is
729  * non-%NULL or unset otherwise.
730  *
731  * Normal menu items (ie: not submenu, section or other custom item
732  * types) are expected to have the "action" attribute set to identify
733  * the action that they are associated with.  The state type of the
734  * action help to determine the disposition of the menu item.  See
735  * #GAction and #GActionGroup for an overview of actions.
736  *
737  * In general, clicking on the menu item will result in activation of
738  * the named action with the "target" attribute given as the parameter
739  * to the action invocation.  If the "target" attribute is not set then
740  * the action is invoked with no parameter.
741  *
742  * If the action has no state then the menu item is usually drawn as a
743  * plain menu item (ie: with no additional decoration).
744  *
745  * If the action has a boolean state then the menu item is usually drawn
746  * as a toggle menu item (ie: with a checkmark or equivalent
747  * indication).  The item should be marked as 'toggled' or 'checked'
748  * when the boolean state is %TRUE.
749  *
750  * If the action has a string state then the menu item is usually drawn
751  * as a radio menu item (ie: with a radio bullet or equivalent
752  * indication).  The item should be marked as 'selected' when the string
753  * state is equal to the value of the @target property.
754  *
755  * See g_menu_item_set_action_and_target() or
756  * g_menu_item_set_detailed_action() for two equivalent calls that are
757  * probably more convenient for most uses.
758  */
759 void
760 g_menu_item_set_action_and_target_value (GMenuItem   *menu_item,
761                                          const gchar *action,
762                                          GVariant    *target_value)
763 {
764   GVariant *action_value;
765
766   if (action != NULL)
767     {
768       action_value = g_variant_new_string (action);
769     }
770   else
771     {
772       action_value = NULL;
773       target_value = NULL;
774     }
775
776   g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ACTION, action_value);
777   g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_TARGET, target_value);
778 }
779
780 /**
781  * g_menu_item_set_action_and_target:
782  * @menu_item: a #GMenuItem
783  * @action: (allow-none): the name of the action for this item
784  * @format_string: (allow-none): a GVariant format string
785  * @...: positional parameters, as per @format_string
786  *
787  * Sets or unsets the "action" and "target" attributes of @menu_item.
788  *
789  * If @action is %NULL then both the "action" and "target" attributes
790  * are unset (and @format_string is ignored along with the positional
791  * parameters).
792  *
793  * If @action is non-%NULL then the "action" attribute is set.
794  * @format_string is then inspected.  If it is non-%NULL then the proper
795  * position parameters are collected to create a #GVariant instance to
796  * use as the target value.  If it is %NULL then the positional
797  * parameters are ignored and the "target" attribute is unset.
798  *
799  * See also g_menu_item_set_action_and_target_value() for an equivalent
800  * call that directly accepts a #GVariant.  See
801  * g_menu_item_set_detailed_action() for a more convenient version that
802  * works with string-typed targets.
803  *
804  * See also g_menu_item_set_action_and_target_value() for a
805  * description of the semantics of the action and target attributes.
806  */
807 void
808 g_menu_item_set_action_and_target (GMenuItem   *menu_item,
809                                    const gchar *action,
810                                    const gchar *format_string,
811                                    ...)
812 {
813   GVariant *value;
814
815   if (format_string != NULL)
816     {
817       va_list ap;
818
819       va_start (ap, format_string);
820       value = g_variant_new_va (format_string, NULL, &ap);
821       va_end (ap);
822     }
823   else
824     value = NULL;
825
826   g_menu_item_set_action_and_target_value (menu_item, action, value);
827 }
828
829 /**
830  * g_menu_item_set_detailed_action:
831  * @menu_item: a #GMenuItem
832  * @detailed_action: the "detailed" action string
833  *
834  * Sets the "action" and possibly the "target" attribute of @menu_item.
835  *
836  * If @detailed_action contains a double colon ("::") then it is used as
837  * a separator between an action name and a target string.  In this
838  * case, this call is equivalent to calling
839  * g_menu_item_set_action_and_target() with the part before the "::" and
840  * g_menu_item_set_target_value() with a string-type #GVariant
841  * containing the part following the "::".
842  *
843  * If @detailed_action doesn't contain "::" then the action is set to
844  * the given string (verbatim) and the target value is unset.
845  *
846  * See g_menu_item_set_action_and_target() or
847  * g_menu_item_set_action_and_target_value() for more flexible (but
848  * slightly less convenient) alternatives.
849  *
850  * See also g_menu_set_action_and_target_value() for a description of
851  * the semantics of the action and target attributes.
852  **/
853 void
854 g_menu_item_set_detailed_action (GMenuItem   *menu_item,
855                                  const gchar *detailed_action)
856 {
857   const gchar *sep;
858
859   sep = strstr (detailed_action, "::");
860
861   if (sep != NULL)
862     {
863       gchar *action;
864
865       action = g_strndup (detailed_action, sep - detailed_action);
866       g_menu_item_set_action_and_target (menu_item, action, "s", sep + 2);
867       g_free (action);
868     }
869
870   else
871     g_menu_item_set_action_and_target_value (menu_item, detailed_action, NULL);
872 }
873
874 /**
875  * g_menu_item_new:
876  * @label: (allow-none): the section label, or %NULL
877  * @detailed_action: (allow-none): the detailed action string, or %NULL
878  *
879  * Creates a new #GMenuItem.
880  *
881  * If @label is non-%NULL it is used to set the "label" attribute of the
882  * new item.
883  *
884  * If @detailed_action is non-%NULL it is used to set the "action" and
885  * possibly the "target" attribute of the new item.  See
886  * g_menu_item_set_detailed_action() for more information.
887  *
888  * Returns: a new #GMenuItem
889  **/
890 GMenuItem *
891 g_menu_item_new (const gchar *label,
892                  const gchar *detailed_action)
893 {
894   GMenuItem *menu_item;
895
896   menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
897
898   if (label != NULL)
899     g_menu_item_set_label (menu_item, label);
900
901   if (detailed_action != NULL)
902     g_menu_item_set_detailed_action (menu_item, detailed_action);
903
904   return menu_item;
905 }
906
907 /**
908  * g_menu_item_new_submenu:
909  * @label: (allow-none): the section label, or %NULL
910  * @submenu: a #GMenuModel with the items of the submenu
911  *
912  * Creates a new #GMenuItem representing a submenu.
913  *
914  * This is a convenience API around g_menu_item_new() and
915  * g_menu_item_set_submenu().
916  *
917  * Returns: a new #GMenuItem
918  **/
919 GMenuItem *
920 g_menu_item_new_submenu (const gchar *label,
921                          GMenuModel  *submenu)
922 {
923   GMenuItem *menu_item;
924
925   menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
926
927   if (label != NULL)
928     g_menu_item_set_label (menu_item, label);
929
930   g_menu_item_set_submenu (menu_item, submenu);
931
932   return menu_item;
933 }
934
935 /**
936  * g_menu_item_new_section:
937  * @label: (allow-none): the section label, or %NULL
938  * @section: a #GMenuModel with the items of the section
939  *
940  * Creates a new #GMenuItem representing a section.
941  *
942  * This is a convenience API around g_menu_item_new() and
943  * g_menu_item_set_section().
944  *
945  * The effect of having one menu appear as a section of another is
946  * exactly as it sounds: the items from @section become a direct part of
947  * the menu that @menu_item is added to.
948  *
949  * Visual separation is typically displayed between two non-empty
950  * sections.  If @label is non-%NULL then it will be encorporated into
951  * this visual indication.  This allows for labeled subsections of a
952  * menu.
953  *
954  * As a simple example, consider a typical "Edit" menu from a simple
955  * program.  It probably contains an "Undo" and "Redo" item, followed by
956  * a separator, followed by "Cut", "Copy" and "Paste".
957  *
958  * This would be accomplished by creating three #GMenu instances.  The
959  * first would be populated with the "Undo" and "Redo" items, and the
960  * second with the "Cut", "Copy" and "Paste" items.  The first and
961  * second menus would then be added as submenus of the third.  In XML
962  * format, this would look something like the following:
963  *
964  * <informalexample><programlisting><![CDATA[
965  * <menu id='edit-menu'>
966  *   <section>
967  *     <item label='Undo'/>
968  *     <item label='Redo'/>
969  *   </section>
970  *   <section>
971  *     <item label='Cut'/>
972  *     <item label='Copy'/>
973  *     <item label='Paste'/>
974  *   </section>
975  * </menu>
976  * ]]></programlisting></informalexample>
977  *
978  * The following example is exactly equivalent.  It is more illustrative
979  * of the exact relationship between the menus and items (keeping in
980  * mind that the 'link' element defines a new menu that is linked to the
981  * containing one).  The style of the second example is more verbose and
982  * difficult to read (and therefore not recommended except for the
983  * purpose of understanding what is really going on).
984  *
985  * <informalexample><programlisting><![CDATA[
986  * <menu id='edit-menu'>
987  *   <item>
988  *     <link name='section'>
989  *       <item label='Undo'/>
990  *       <item label='Redo'/>
991  *     </link>
992  *   </item>
993  *   <item>
994  *     <link name='section'>
995  *       <item label='Cut'/>
996  *       <item label='Copy'/>
997  *       <item label='Paste'/>
998  *     </link>
999  *   </item>
1000  * </menu>
1001  * ]]></programlisting></informalexample>
1002  *
1003  * Returns: a new #GMenuItem
1004  **/
1005 GMenuItem *
1006 g_menu_item_new_section (const gchar *label,
1007                          GMenuModel  *section)
1008 {
1009   GMenuItem *menu_item;
1010
1011   menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1012
1013   if (label != NULL)
1014     g_menu_item_set_label (menu_item, label);
1015
1016   g_menu_item_set_section (menu_item, section);
1017
1018   return menu_item;
1019 }