940ba66e679323efa293d9d85ac07d8b9c00af69
[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 /**
490  * g_menu_remove_all:
491  * @menu: a #GMenu
492  *
493  * Removes all items in the menu.
494  *
495  * Since: 2.38
496  **/
497 void
498 g_menu_remove_all (GMenu *menu)
499 {
500   gint i, n;
501
502   g_return_if_fail (G_IS_MENU (menu));
503   n = menu->items->len;
504
505   for (i = 0; i < n; i++)
506     g_menu_clear_item (&g_array_index (menu->items, struct item, i));
507   g_array_set_size (menu->items, 0);
508
509   g_menu_model_items_changed (G_MENU_MODEL (menu), 0, n, 0);
510 }
511
512 static void
513 g_menu_finalize (GObject *object)
514 {
515   GMenu *menu = G_MENU (object);
516   struct item *items;
517   gint n_items;
518   gint i;
519
520   n_items = menu->items->len;
521   items = (struct item *) g_array_free (menu->items, FALSE);
522   for (i = 0; i < n_items; i++)
523     g_menu_clear_item (&items[i]);
524   g_free (items);
525
526   G_OBJECT_CLASS (g_menu_parent_class)
527     ->finalize (object);
528 }
529
530 static void
531 g_menu_init (GMenu *menu)
532 {
533   menu->items = g_array_new (FALSE, FALSE, sizeof (struct item));
534   menu->mutable = TRUE;
535 }
536
537 static void
538 g_menu_class_init (GMenuClass *class)
539 {
540   GMenuModelClass *model_class = G_MENU_MODEL_CLASS (class);
541   GObjectClass *object_class = G_OBJECT_CLASS (class);
542
543   object_class->finalize = g_menu_finalize;
544
545   model_class->is_mutable = g_menu_is_mutable;
546   model_class->get_n_items = g_menu_get_n_items;
547   model_class->get_item_attributes = g_menu_get_item_attributes;
548   model_class->get_item_links = g_menu_get_item_links;
549 }
550
551
552 static void
553 g_menu_item_clear_cow (GMenuItem *menu_item)
554 {
555   if (menu_item->cow)
556     {
557       GHashTableIter iter;
558       GHashTable *new;
559       gpointer key;
560       gpointer val;
561
562       new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
563       g_hash_table_iter_init (&iter, menu_item->attributes);
564       while (g_hash_table_iter_next (&iter, &key, &val))
565         g_hash_table_insert (new, g_strdup (key), g_variant_ref (val));
566       g_hash_table_unref (menu_item->attributes);
567       menu_item->attributes = new;
568
569       new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
570       g_hash_table_iter_init (&iter, menu_item->links);
571       while (g_hash_table_iter_next (&iter, &key, &val))
572         g_hash_table_insert (new, g_strdup (key), g_object_ref (val));
573       g_hash_table_unref (menu_item->links);
574       menu_item->links = new;
575
576       menu_item->cow = FALSE;
577     }
578 }
579
580 static void
581 g_menu_item_finalize (GObject *object)
582 {
583   GMenuItem *menu_item = G_MENU_ITEM (object);
584
585   g_hash_table_unref (menu_item->attributes);
586   g_hash_table_unref (menu_item->links);
587
588   G_OBJECT_CLASS (g_menu_item_parent_class)
589     ->finalize (object);
590 }
591
592 static void
593 g_menu_item_init (GMenuItem *menu_item)
594 {
595   menu_item->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
596   menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
597   menu_item->cow = FALSE;
598 }
599
600 static void
601 g_menu_item_class_init (GMenuItemClass *class)
602 {
603   class->finalize = g_menu_item_finalize;
604 }
605
606 /* We treat attribute names the same as GSettings keys:
607  * - only lowercase ascii, digits and '-'
608  * - must start with lowercase
609  * - must not end with '-'
610  * - no consecutive '-'
611  * - not longer than 1024 chars
612  */
613 static gboolean
614 valid_attribute_name (const gchar *name)
615 {
616   gint i;
617
618   if (!g_ascii_islower (name[0]))
619     return FALSE;
620
621   for (i = 1; name[i]; i++)
622     {
623       if (name[i] != '-' &&
624           !g_ascii_islower (name[i]) &&
625           !g_ascii_isdigit (name[i]))
626         return FALSE;
627
628       if (name[i] == '-' && name[i + 1] == '-')
629         return FALSE;
630     }
631
632   if (name[i - 1] == '-')
633     return FALSE;
634
635   if (i > 1024)
636     return FALSE;
637
638   return TRUE;
639 }
640
641 /**
642  * g_menu_item_set_attribute_value:
643  * @menu_item: a #GMenuItem
644  * @attribute: the attribute to set
645  * @value: (allow-none): a #GVariant to use as the value, or %NULL
646  *
647  * Sets or unsets an attribute on @menu_item.
648  *
649  * The attribute to set or unset is specified by @attribute. This
650  * can be one of the standard attribute names %G_MENU_ATTRIBUTE_LABEL,
651  * %G_MENU_ATTRIBUTE_ACTION, %G_MENU_ATTRIBUTE_TARGET, or a custom
652  * attribute name.
653  * Attribute names are restricted to lowercase characters, numbers
654  * and '-'. Furthermore, the names must begin with a lowercase character,
655  * must not end with a '-', and must not contain consecutive dashes.
656  *
657  * must consist only of lowercase
658  * ASCII characters, digits and '-'.
659  *
660  * If @value is non-%NULL then it is used as the new value for the
661  * attribute.  If @value is %NULL then the attribute is unset. If
662  * the @value #GVariant is floating, it is consumed.
663  *
664  * See also g_menu_item_set_attribute() for a more convenient way to do
665  * the same.
666  *
667  * Since: 2.32
668  */
669 void
670 g_menu_item_set_attribute_value (GMenuItem   *menu_item,
671                                  const gchar *attribute,
672                                  GVariant    *value)
673 {
674   g_return_if_fail (G_IS_MENU_ITEM (menu_item));
675   g_return_if_fail (attribute != NULL);
676   g_return_if_fail (valid_attribute_name (attribute));
677
678   g_menu_item_clear_cow (menu_item);
679
680   if (value != NULL)
681     g_hash_table_insert (menu_item->attributes, g_strdup (attribute), g_variant_ref_sink (value));
682   else
683     g_hash_table_remove (menu_item->attributes, attribute);
684 }
685
686 /**
687  * g_menu_item_set_attribute:
688  * @menu_item: a #GMenuItem
689  * @attribute: the attribute to set
690  * @format_string: (allow-none): a #GVariant format string, or %NULL
691  * @...: positional parameters, as per @format_string
692  *
693  * Sets or unsets an attribute on @menu_item.
694  *
695  * The attribute to set or unset is specified by @attribute. This
696  * can be one of the standard attribute names %G_MENU_ATTRIBUTE_LABEL,
697  * %G_MENU_ATTRIBUTE_ACTION, %G_MENU_ATTRIBUTE_TARGET, or a custom
698  * attribute name.
699  * Attribute names are restricted to lowercase characters, numbers
700  * and '-'. Furthermore, the names must begin with a lowercase character,
701  * must not end with a '-', and must not contain consecutive dashes.
702  *
703  * If @format_string is non-%NULL then the proper position parameters
704  * are collected to create a #GVariant instance to use as the attribute
705  * value.  If it is %NULL then the positional parameterrs are ignored
706  * and the named attribute is unset.
707  *
708  * See also g_menu_item_set_attribute_value() for an equivalent call
709  * that directly accepts a #GVariant.
710  *
711  * Since: 2.32
712  */
713 void
714 g_menu_item_set_attribute (GMenuItem   *menu_item,
715                            const gchar *attribute,
716                            const gchar *format_string,
717                            ...)
718 {
719   GVariant *value;
720
721   if (format_string != NULL)
722     {
723       va_list ap;
724
725       va_start (ap, format_string);
726       value = g_variant_new_va (format_string, NULL, &ap);
727       va_end (ap);
728     }
729   else
730     value = NULL;
731
732   g_menu_item_set_attribute_value (menu_item, attribute, value);
733 }
734
735 /**
736  * g_menu_item_set_link:
737  * @menu_item: a #GMenuItem
738  * @link: type of link to establish or unset
739  * @model: (allow-none): the #GMenuModel to link to (or %NULL to unset)
740  *
741  * Creates a link from @menu_item to @model if non-%NULL, or unsets it.
742  *
743  * Links are used to establish a relationship between a particular menu
744  * item and another menu.  For example, %G_MENU_LINK_SUBMENU is used to
745  * associate a submenu with a particular menu item, and %G_MENU_LINK_SECTION
746  * is used to create a section. Other types of link can be used, but there
747  * is no guarantee that clients will be able to make sense of them.
748  * Link types are restricted to lowercase characters, numbers
749  * and '-'. Furthermore, the names must begin with a lowercase character,
750  * must not end with a '-', and must not contain consecutive dashes.
751  *
752  * Since: 2.32
753  */
754 void
755 g_menu_item_set_link (GMenuItem   *menu_item,
756                       const gchar *link,
757                       GMenuModel  *model)
758 {
759   g_return_if_fail (G_IS_MENU_ITEM (menu_item));
760   g_return_if_fail (link != NULL);
761   g_return_if_fail (valid_attribute_name (link));
762
763   g_menu_item_clear_cow (menu_item);
764
765   if (model != NULL)
766     g_hash_table_insert (menu_item->links, g_strdup (link), g_object_ref (model));
767   else
768     g_hash_table_remove (menu_item->links, link);
769 }
770
771 /**
772  * g_menu_item_get_attribute_value:
773  * @menu_item: a #GMenuItem
774  * @attribute: the attribute name to query
775  * @expected_type: (allow-none): the expected type of the attribute
776  *
777  * Queries the named @attribute on @menu_item.
778  *
779  * If @expected_type is specified and the attribute does not have this
780  * type, %NULL is returned.  %NULL is also returned if the attribute
781  * simply does not exist.
782  *
783  * Returns: (transfer full): the attribute value, or %NULL
784  *
785  * Since: 2.34
786  */
787 GVariant *
788 g_menu_item_get_attribute_value (GMenuItem          *menu_item,
789                                  const gchar        *attribute,
790                                  const GVariantType *expected_type)
791 {
792   GVariant *value;
793
794   g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), NULL);
795   g_return_val_if_fail (attribute != NULL, NULL);
796
797   value = g_hash_table_lookup (menu_item->attributes, attribute);
798
799   if (value != NULL)
800     {
801       if (expected_type == NULL || g_variant_is_of_type (value, expected_type))
802         g_variant_ref (value);
803       else
804         value = NULL;
805     }
806
807   return value;
808 }
809
810 /**
811  * g_menu_item_get_attribute:
812  * @menu_item: a #GMenuItem
813  * @attribute: the attribute name to query
814  * @format_string: a #GVariant format string
815  * @...: positional parameters, as per @format_string
816  *
817  * Queries the named @attribute on @menu_item.
818  *
819  * If the attribute exists and matches the #GVariantType corresponding
820  * to @format_string then @format_string is used to deconstruct the
821  * value into the positional parameters and %TRUE is returned.
822  *
823  * If the attribute does not exist, or it does exist but has the wrong
824  * type, then the positional parameters are ignored and %FALSE is
825  * returned.
826  *
827  * Returns: %TRUE if the named attribute was found with the expected
828  *     type
829  *
830  * Since: 2.34
831  */
832 gboolean
833 g_menu_item_get_attribute (GMenuItem   *menu_item,
834                            const gchar *attribute,
835                            const gchar *format_string,
836                            ...)
837 {
838   GVariant *value;
839   va_list ap;
840
841   g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), FALSE);
842   g_return_val_if_fail (attribute != NULL, FALSE);
843   g_return_val_if_fail (format_string != NULL, FALSE);
844
845   value = g_hash_table_lookup (menu_item->attributes, attribute);
846
847   if (value == NULL)
848     return FALSE;
849
850   if (!g_variant_check_format_string (value, format_string, FALSE))
851     return FALSE;
852
853   va_start (ap, format_string);
854   g_variant_get_va (value, format_string, NULL, &ap);
855   va_end (ap);
856
857   return TRUE;
858 }
859
860 /**
861  * g_menu_item_get_link:
862  * @menu_item: a #GMenuItem
863  * @link: the link name to query
864  *
865  * Queries the named @link on @menu_item.
866  *
867  * Returns: (transfer full): the link, or %NULL
868  *
869  * Since: 2.34
870  */
871 GMenuModel *
872 g_menu_item_get_link (GMenuItem   *menu_item,
873                       const gchar *link)
874 {
875   GMenuModel *model;
876
877   g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), NULL);
878   g_return_val_if_fail (link != NULL, NULL);
879   g_return_val_if_fail (valid_attribute_name (link), NULL);
880
881   model = g_hash_table_lookup (menu_item->links, link);
882
883   if (model)
884     g_object_ref (model);
885
886   return model;
887 }
888
889 /**
890  * g_menu_item_set_label:
891  * @menu_item: a #GMenuItem
892  * @label: (allow-none): the label to set, or %NULL to unset
893  *
894  * Sets or unsets the "label" attribute of @menu_item.
895  *
896  * If @label is non-%NULL it is used as the label for the menu item.  If
897  * it is %NULL then the label attribute is unset.
898  *
899  * Since: 2.32
900  */
901 void
902 g_menu_item_set_label (GMenuItem   *menu_item,
903                        const gchar *label)
904 {
905   GVariant *value;
906
907   if (label != NULL)
908     value = g_variant_new_string (label);
909   else
910     value = NULL;
911
912   g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_LABEL, value);
913 }
914
915 /**
916  * g_menu_item_set_submenu:
917  * @menu_item: a #GMenuItem
918  * @submenu: (allow-none): a #GMenuModel, or %NULL
919  *
920  * Sets or unsets the "submenu" link of @menu_item to @submenu.
921  *
922  * If @submenu is non-%NULL, it is linked to.  If it is %NULL then the
923  * link is unset.
924  *
925  * The effect of having one menu appear as a submenu of another is
926  * exactly as it sounds.
927  *
928  * Since: 2.32
929  */
930 void
931 g_menu_item_set_submenu (GMenuItem  *menu_item,
932                          GMenuModel *submenu)
933 {
934   g_menu_item_set_link (menu_item, G_MENU_LINK_SUBMENU, submenu);
935 }
936
937 /**
938  * g_menu_item_set_section:
939  * @menu_item: a #GMenuItem
940  * @section: (allow-none): a #GMenuModel, or %NULL
941  *
942  * Sets or unsets the "section" link of @menu_item to @section.
943  *
944  * The effect of having one menu appear as a section of another is
945  * exactly as it sounds: the items from @section become a direct part of
946  * the menu that @menu_item is added to.  See g_menu_item_new_section()
947  * for more information about what it means for a menu item to be a
948  * section.
949  *
950  * Since: 2.32
951  */
952 void
953 g_menu_item_set_section (GMenuItem  *menu_item,
954                          GMenuModel *section)
955 {
956   g_menu_item_set_link (menu_item, G_MENU_LINK_SECTION, section);
957 }
958
959 /**
960  * g_menu_item_set_action_and_target_value:
961  * @menu_item: a #GMenuItem
962  * @action: (allow-none): the name of the action for this item
963  * @target_value: (allow-none): a #GVariant to use as the action target
964  *
965  * Sets or unsets the "action" and "target" attributes of @menu_item.
966  *
967  * If @action is %NULL then both the "action" and "target" attributes
968  * are unset (and @target_value is ignored).
969  *
970  * If @action is non-%NULL then the "action" attribute is set.  The
971  * "target" attribute is then set to the value of @target_value if it is
972  * non-%NULL or unset otherwise.
973  *
974  * Normal menu items (ie: not submenu, section or other custom item
975  * types) are expected to have the "action" attribute set to identify
976  * the action that they are associated with.  The state type of the
977  * action help to determine the disposition of the menu item.  See
978  * #GAction and #GActionGroup for an overview of actions.
979  *
980  * In general, clicking on the menu item will result in activation of
981  * the named action with the "target" attribute given as the parameter
982  * to the action invocation.  If the "target" attribute is not set then
983  * the action is invoked with no parameter.
984  *
985  * If the action has no state then the menu item is usually drawn as a
986  * plain menu item (ie: with no additional decoration).
987  *
988  * If the action has a boolean state then the menu item is usually drawn
989  * as a toggle menu item (ie: with a checkmark or equivalent
990  * indication).  The item should be marked as 'toggled' or 'checked'
991  * when the boolean state is %TRUE.
992  *
993  * If the action has a string state then the menu item is usually drawn
994  * as a radio menu item (ie: with a radio bullet or equivalent
995  * indication).  The item should be marked as 'selected' when the string
996  * state is equal to the value of the @target property.
997  *
998  * See g_menu_item_set_action_and_target() or
999  * g_menu_item_set_detailed_action() for two equivalent calls that are
1000  * probably more convenient for most uses.
1001  *
1002  * Since: 2.32
1003  */
1004 void
1005 g_menu_item_set_action_and_target_value (GMenuItem   *menu_item,
1006                                          const gchar *action,
1007                                          GVariant    *target_value)
1008 {
1009   GVariant *action_value;
1010
1011   if (action != NULL)
1012     {
1013       action_value = g_variant_new_string (action);
1014     }
1015   else
1016     {
1017       action_value = NULL;
1018       target_value = NULL;
1019     }
1020
1021   g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ACTION, action_value);
1022   g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_TARGET, target_value);
1023 }
1024
1025 /**
1026  * g_menu_item_set_action_and_target:
1027  * @menu_item: a #GMenuItem
1028  * @action: (allow-none): the name of the action for this item
1029  * @format_string: (allow-none): a GVariant format string
1030  * @...: positional parameters, as per @format_string
1031  *
1032  * Sets or unsets the "action" and "target" attributes of @menu_item.
1033  *
1034  * If @action is %NULL then both the "action" and "target" attributes
1035  * are unset (and @format_string is ignored along with the positional
1036  * parameters).
1037  *
1038  * If @action is non-%NULL then the "action" attribute is set.
1039  * @format_string is then inspected.  If it is non-%NULL then the proper
1040  * position parameters are collected to create a #GVariant instance to
1041  * use as the target value.  If it is %NULL then the positional
1042  * parameters are ignored and the "target" attribute is unset.
1043  *
1044  * See also g_menu_item_set_action_and_target_value() for an equivalent
1045  * call that directly accepts a #GVariant.  See
1046  * g_menu_item_set_detailed_action() for a more convenient version that
1047  * works with string-typed targets.
1048  *
1049  * See also g_menu_item_set_action_and_target_value() for a
1050  * description of the semantics of the action and target attributes.
1051  *
1052  * Since: 2.32
1053  */
1054 void
1055 g_menu_item_set_action_and_target (GMenuItem   *menu_item,
1056                                    const gchar *action,
1057                                    const gchar *format_string,
1058                                    ...)
1059 {
1060   GVariant *value;
1061
1062   if (format_string != NULL)
1063     {
1064       va_list ap;
1065
1066       va_start (ap, format_string);
1067       value = g_variant_new_va (format_string, NULL, &ap);
1068       va_end (ap);
1069     }
1070   else
1071     value = NULL;
1072
1073   g_menu_item_set_action_and_target_value (menu_item, action, value);
1074 }
1075
1076 /**
1077  * g_menu_item_set_detailed_action:
1078  * @menu_item: a #GMenuItem
1079  * @detailed_action: the "detailed" action string
1080  *
1081  * Sets the "action" and possibly the "target" attribute of @menu_item.
1082  *
1083  * The format of @detailed_action is the same format parsed by
1084  * g_action_parse_detailed_name().
1085  *
1086  * See g_menu_item_set_action_and_target() or
1087  * g_menu_item_set_action_and_target_value() for more flexible (but
1088  * slightly less convenient) alternatives.
1089  *
1090  * See also g_menu_item_set_action_and_target_value() for a description of
1091  * the semantics of the action and target attributes.
1092  *
1093  * Since: 2.32
1094  */
1095 void
1096 g_menu_item_set_detailed_action (GMenuItem   *menu_item,
1097                                  const gchar *detailed_action)
1098 {
1099   GError *error = NULL;
1100   GVariant *target;
1101   gchar *name;
1102
1103   if (!g_action_parse_detailed_name (detailed_action, &name, &target, &error))
1104     g_error ("g_menu_item_set_detailed_action: %s", error->message);
1105
1106   g_menu_item_set_action_and_target_value (menu_item, name, target);
1107   if (target)
1108     g_variant_unref (target);
1109   g_free (name);
1110 }
1111
1112 /**
1113  * g_menu_item_new:
1114  * @label: (allow-none): the section label, or %NULL
1115  * @detailed_action: (allow-none): the detailed action string, or %NULL
1116  *
1117  * Creates a new #GMenuItem.
1118  *
1119  * If @label is non-%NULL it is used to set the "label" attribute of the
1120  * new item.
1121  *
1122  * If @detailed_action is non-%NULL it is used to set the "action" and
1123  * possibly the "target" attribute of the new item.  See
1124  * g_menu_item_set_detailed_action() for more information.
1125  *
1126  * Returns: a new #GMenuItem
1127  *
1128  * Since: 2.32
1129  */
1130 GMenuItem *
1131 g_menu_item_new (const gchar *label,
1132                  const gchar *detailed_action)
1133 {
1134   GMenuItem *menu_item;
1135
1136   menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1137
1138   if (label != NULL)
1139     g_menu_item_set_label (menu_item, label);
1140
1141   if (detailed_action != NULL)
1142     g_menu_item_set_detailed_action (menu_item, detailed_action);
1143
1144   return menu_item;
1145 }
1146
1147 /**
1148  * g_menu_item_new_submenu:
1149  * @label: (allow-none): the section label, or %NULL
1150  * @submenu: a #GMenuModel with the items of the submenu
1151  *
1152  * Creates a new #GMenuItem representing a submenu.
1153  *
1154  * This is a convenience API around g_menu_item_new() and
1155  * g_menu_item_set_submenu().
1156  *
1157  * Returns: a new #GMenuItem
1158  *
1159  * Since: 2.32
1160  */
1161 GMenuItem *
1162 g_menu_item_new_submenu (const gchar *label,
1163                          GMenuModel  *submenu)
1164 {
1165   GMenuItem *menu_item;
1166
1167   menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1168
1169   if (label != NULL)
1170     g_menu_item_set_label (menu_item, label);
1171
1172   g_menu_item_set_submenu (menu_item, submenu);
1173
1174   return menu_item;
1175 }
1176
1177 /**
1178  * g_menu_item_new_section:
1179  * @label: (allow-none): the section label, or %NULL
1180  * @section: a #GMenuModel with the items of the section
1181  *
1182  * Creates a new #GMenuItem representing a section.
1183  *
1184  * This is a convenience API around g_menu_item_new() and
1185  * g_menu_item_set_section().
1186  *
1187  * The effect of having one menu appear as a section of another is
1188  * exactly as it sounds: the items from @section become a direct part of
1189  * the menu that @menu_item is added to.
1190  *
1191  * Visual separation is typically displayed between two non-empty
1192  * sections.  If @label is non-%NULL then it will be encorporated into
1193  * this visual indication.  This allows for labeled subsections of a
1194  * menu.
1195  *
1196  * As a simple example, consider a typical "Edit" menu from a simple
1197  * program.  It probably contains an "Undo" and "Redo" item, followed by
1198  * a separator, followed by "Cut", "Copy" and "Paste".
1199  *
1200  * This would be accomplished by creating three #GMenu instances.  The
1201  * first would be populated with the "Undo" and "Redo" items, and the
1202  * second with the "Cut", "Copy" and "Paste" items.  The first and
1203  * second menus would then be added as submenus of the third.  In XML
1204  * format, this would look something like the following:
1205  *
1206  * <informalexample><programlisting><![CDATA[
1207  * <menu id='edit-menu'>
1208  *   <section>
1209  *     <item label='Undo'/>
1210  *     <item label='Redo'/>
1211  *   </section>
1212  *   <section>
1213  *     <item label='Cut'/>
1214  *     <item label='Copy'/>
1215  *     <item label='Paste'/>
1216  *   </section>
1217  * </menu>
1218  * ]]></programlisting></informalexample>
1219  *
1220  * The following example is exactly equivalent.  It is more illustrative
1221  * of the exact relationship between the menus and items (keeping in
1222  * mind that the 'link' element defines a new menu that is linked to the
1223  * containing one).  The style of the second example is more verbose and
1224  * difficult to read (and therefore not recommended except for the
1225  * purpose of understanding what is really going on).
1226  *
1227  * <informalexample><programlisting><![CDATA[
1228  * <menu id='edit-menu'>
1229  *   <item>
1230  *     <link name='section'>
1231  *       <item label='Undo'/>
1232  *       <item label='Redo'/>
1233  *     </link>
1234  *   </item>
1235  *   <item>
1236  *     <link name='section'>
1237  *       <item label='Cut'/>
1238  *       <item label='Copy'/>
1239  *       <item label='Paste'/>
1240  *     </link>
1241  *   </item>
1242  * </menu>
1243  * ]]></programlisting></informalexample>
1244  *
1245  * Returns: a new #GMenuItem
1246  *
1247  * Since: 2.32
1248  */
1249 GMenuItem *
1250 g_menu_item_new_section (const gchar *label,
1251                          GMenuModel  *section)
1252 {
1253   GMenuItem *menu_item;
1254
1255   menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1256
1257   if (label != NULL)
1258     g_menu_item_set_label (menu_item, label);
1259
1260   g_menu_item_set_section (menu_item, section);
1261
1262   return menu_item;
1263 }
1264
1265 /**
1266  * g_menu_item_new_from_model:
1267  * @model: a #GMenuModel
1268  * @item_index: the index of an item in @model
1269  *
1270  * Creates a #GMenuItem as an exact copy of an existing menu item in a
1271  * #GMenuModel.
1272  *
1273  * @item_index must be valid (ie: be sure to call
1274  * g_menu_model_get_n_items() first).
1275  *
1276  * Returns: a new #GMenuItem.
1277  *
1278  * Since: 2.34
1279  */
1280 GMenuItem *
1281 g_menu_item_new_from_model (GMenuModel *model,
1282                             gint        item_index)
1283 {
1284   GMenuModelClass *class = G_MENU_MODEL_GET_CLASS (model);
1285   GMenuItem *menu_item;
1286
1287   menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
1288
1289   /* With some trickery we can be pretty efficient.
1290    *
1291    * A GMenuModel must either implement iterate_item_attributes() or
1292    * get_item_attributes().  If it implements get_item_attributes() then
1293    * we are in luck -- we can just take a reference on the returned
1294    * hashtable and mark ourselves as copy-on-write.
1295    *
1296    * In the case that the model is based on get_item_attributes (which
1297    * is the case for both GMenu and GDBusMenuModel) then this is
1298    * basically just g_hash_table_ref().
1299    */
1300   if (class->get_item_attributes)
1301     {
1302       GHashTable *attributes = NULL;
1303
1304       class->get_item_attributes (model, item_index, &attributes);
1305       if (attributes)
1306         {
1307           g_hash_table_unref (menu_item->attributes);
1308           menu_item->attributes = attributes;
1309           menu_item->cow = TRUE;
1310         }
1311     }
1312   else
1313     {
1314       GMenuAttributeIter *iter;
1315       const gchar *attribute;
1316       GVariant *value;
1317
1318       iter = g_menu_model_iterate_item_attributes (model, item_index);
1319       while (g_menu_attribute_iter_get_next (iter, &attribute, &value))
1320         g_hash_table_insert (menu_item->attributes, g_strdup (attribute), value);
1321       g_object_unref (iter);
1322     }
1323
1324   /* Same story for the links... */
1325   if (class->get_item_links)
1326     {
1327       GHashTable *links = NULL;
1328
1329       class->get_item_links (model, item_index, &links);
1330       if (links)
1331         {
1332           g_hash_table_unref (menu_item->links);
1333           menu_item->links = links;
1334           menu_item->cow = TRUE;
1335         }
1336     }
1337   else
1338     {
1339       GMenuLinkIter *iter;
1340       const gchar *link;
1341       GMenuModel *value;
1342
1343       iter = g_menu_model_iterate_item_links (model, item_index);
1344       while (g_menu_link_iter_get_next (iter, &link, &value))
1345         g_hash_table_insert (menu_item->links, g_strdup (link), value);
1346       g_object_unref (iter);
1347     }
1348
1349   return menu_item;
1350 }