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