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