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 | |
63 | struct |
64 | { |
65 | GObject ; |
66 | |
67 | GHashTable *; |
68 | GHashTable *; |
69 | gboolean ; |
70 | }; |
71 | |
72 | typedef GObjectClass ; |
73 | |
74 | struct |
75 | { |
76 | GMenuModel ; |
77 | |
78 | GArray *; |
79 | gboolean ; |
80 | }; |
81 | |
82 | typedef GMenuModelClass ; |
83 | |
84 | G_DEFINE_TYPE (GMenu, g_menu, G_TYPE_MENU_MODEL) |
85 | G_DEFINE_TYPE (GMenuItem, g_menu_item, G_TYPE_OBJECT) |
86 | |
87 | struct item |
88 | { |
89 | GHashTable *attributes; |
90 | GHashTable *links; |
91 | }; |
92 | |
93 | static gboolean |
94 | (GMenuModel *model) |
95 | { |
96 | GMenu * = G_MENU (model); |
97 | |
98 | return menu->mutable; |
99 | } |
100 | |
101 | static gint |
102 | (GMenuModel *model) |
103 | { |
104 | GMenu * = G_MENU (model); |
105 | |
106 | return menu->items->len; |
107 | } |
108 | |
109 | static void |
110 | (GMenuModel *model, |
111 | gint position, |
112 | GHashTable **table) |
113 | { |
114 | GMenu * = G_MENU (model); |
115 | |
116 | *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).attributes); |
117 | } |
118 | |
119 | static void |
120 | (GMenuModel *model, |
121 | gint position, |
122 | GHashTable **table) |
123 | { |
124 | GMenu * = 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 | */ |
155 | void |
156 | (GMenu *, |
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 | */ |
187 | void |
188 | (GMenu *, |
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 | */ |
205 | void |
206 | (GMenu *, |
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 | */ |
227 | void |
228 | (GMenu *) |
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 | */ |
246 | GMenu * |
247 | (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 | */ |
265 | void |
266 | (GMenu *, |
267 | gint position, |
268 | const gchar *label, |
269 | const gchar *detailed_action) |
270 | { |
271 | GMenuItem *; |
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 | */ |
290 | void |
291 | (GMenu *, |
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 | */ |
310 | void |
311 | (GMenu *, |
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 | */ |
331 | void |
332 | (GMenu *, |
333 | gint position, |
334 | const gchar *label, |
335 | GMenuModel *section) |
336 | { |
337 | GMenuItem *; |
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 | */ |
357 | void |
358 | (GMenu *, |
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 | */ |
377 | void |
378 | (GMenu *, |
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 | */ |
398 | void |
399 | (GMenu *, |
400 | gint position, |
401 | const gchar *label, |
402 | GMenuModel *) |
403 | { |
404 | GMenuItem *; |
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 | */ |
423 | void |
424 | (GMenu *, |
425 | const gchar *label, |
426 | GMenuModel *) |
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 | */ |
443 | void |
444 | (GMenu *, |
445 | const gchar *label, |
446 | GMenuModel *) |
447 | { |
448 | g_menu_insert_submenu (menu, position: -1, label, submenu); |
449 | } |
450 | |
451 | static void |
452 | (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 | */ |
478 | void |
479 | (GMenu *, |
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 | **/ |
498 | void |
499 | (GMenu *) |
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 | |
513 | static void |
514 | (GObject *object) |
515 | { |
516 | GMenu * = 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 | |
531 | static void |
532 | (GMenu *) |
533 | { |
534 | menu->items = g_array_new (FALSE, FALSE, element_size: sizeof (struct item)); |
535 | menu->mutable = TRUE; |
536 | } |
537 | |
538 | static void |
539 | (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 | |
553 | static void |
554 | (GMenuItem *) |
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 | |
581 | static void |
582 | (GObject *object) |
583 | { |
584 | GMenuItem * = 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 | |
593 | static void |
594 | (GMenuItem *) |
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 | |
601 | static void |
602 | (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 | */ |
614 | static gboolean |
615 | valid_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 | */ |
670 | void |
671 | (GMenuItem *, |
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 | */ |
714 | void |
715 | (GMenuItem *, |
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 | */ |
755 | void |
756 | (GMenuItem *, |
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 | */ |
788 | GVariant * |
789 | (GMenuItem *, |
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 | */ |
833 | gboolean |
834 | (GMenuItem *, |
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 | */ |
872 | GMenuModel * |
873 | (GMenuItem *, |
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 | */ |
902 | void |
903 | (GMenuItem *, |
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 | */ |
931 | void |
932 | (GMenuItem *, |
933 | GMenuModel *) |
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 | */ |
953 | void |
954 | (GMenuItem *, |
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 | */ |
1005 | void |
1006 | g_menu_item_set_action_and_target_value (GMenuItem *, |
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 | */ |
1055 | void |
1056 | g_menu_item_set_action_and_target (GMenuItem *, |
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 | */ |
1096 | void |
1097 | (GMenuItem *, |
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 | */ |
1131 | GMenuItem * |
1132 | (const gchar *label, |
1133 | const gchar *detailed_action) |
1134 | { |
1135 | GMenuItem *; |
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 | */ |
1162 | GMenuItem * |
1163 | (const gchar *label, |
1164 | GMenuModel *) |
1165 | { |
1166 | GMenuItem *; |
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 | */ |
1248 | GMenuItem * |
1249 | (const gchar *label, |
1250 | GMenuModel *section) |
1251 | { |
1252 | GMenuItem *; |
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 | */ |
1279 | GMenuItem * |
1280 | (GMenuModel *model, |
1281 | gint item_index) |
1282 | { |
1283 | GMenuModelClass *class = G_MENU_MODEL_GET_CLASS (model); |
1284 | GMenuItem *; |
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 | **/ |
1371 | void |
1372 | (GMenuItem *, |
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 | |