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

source code of gtk/subprojects/glib/gio/gmenu.c