nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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, 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 _GMenuItem |
||
64 | { |
||
65 | GObject parent_instance; |
||
66 | |||
67 | GHashTable *attributes; |
||
68 | GHashTable *links; |
||
69 | gboolean cow; |
||
70 | }; |
||
71 | |||
72 | typedef GObjectClass GMenuItemClass; |
||
73 | |||
74 | struct _GMenu |
||
75 | { |
||
76 | GMenuModel parent_instance; |
||
77 | |||
78 | GArray *items; |
||
79 | gboolean mutable; |
||
80 | }; |
||
81 | |||
82 | typedef GMenuModelClass GMenuClass; |
||
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 | g_menu_is_mutable (GMenuModel *model) |
||
95 | { |
||
96 | GMenu *menu = G_MENU (model); |
||
97 | |||
98 | return menu->mutable; |
||
99 | } |
||
100 | |||
101 | static gint |
||
102 | g_menu_get_n_items (GMenuModel *model) |
||
103 | { |
||
104 | GMenu *menu = G_MENU (model); |
||
105 | |||
106 | return menu->items->len; |
||
107 | } |
||
108 | |||
109 | static void |
||
110 | g_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 | |||
119 | static void |
||
120 | g_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 | */ |
||
155 | void |
||
156 | g_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 || position > menu->items->len) |
||
166 | position = menu->items->len; |
||
167 | |||
168 | new_item.attributes = g_hash_table_ref (item->attributes); |
||
169 | new_item.links = g_hash_table_ref (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, 0, 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 | g_menu_prepend_item (GMenu *menu, |
||
189 | GMenuItem *item) |
||
190 | { |
||
191 | g_menu_insert_item (menu, 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 | g_menu_append_item (GMenu *menu, |
||
207 | GMenuItem *item) |
||
208 | { |
||
209 | g_menu_insert_item (menu, -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 | g_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 | */ |
||
246 | GMenu * |
||
247 | g_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: (allow-none): the section label, or %NULL |
||
257 | * @detailed_action: (allow-none): 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 | g_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, menu_item); |
||
275 | g_object_unref (menu_item); |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * g_menu_prepend: |
||
280 | * @menu: a #GMenu |
||
281 | * @label: (allow-none): the section label, or %NULL |
||
282 | * @detailed_action: (allow-none): 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 | g_menu_prepend (GMenu *menu, |
||
292 | const gchar *label, |
||
293 | const gchar *detailed_action) |
||
294 | { |
||
295 | g_menu_insert (menu, 0, label, detailed_action); |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * g_menu_append: |
||
300 | * @menu: a #GMenu |
||
301 | * @label: (allow-none): the section label, or %NULL |
||
302 | * @detailed_action: (allow-none): 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 | g_menu_append (GMenu *menu, |
||
312 | const gchar *label, |
||
313 | const gchar *detailed_action) |
||
314 | { |
||
315 | g_menu_insert (menu, -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: (allow-none): 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 | g_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, menu_item); |
||
341 | g_object_unref (menu_item); |
||
342 | } |
||
343 | |||
344 | |||
345 | /** |
||
346 | * g_menu_prepend_section: |
||
347 | * @menu: a #GMenu |
||
348 | * @label: (allow-none): 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 | g_menu_prepend_section (GMenu *menu, |
||
359 | const gchar *label, |
||
360 | GMenuModel *section) |
||
361 | { |
||
362 | g_menu_insert_section (menu, 0, label, section); |
||
363 | } |
||
364 | |||
365 | /** |
||
366 | * g_menu_append_section: |
||
367 | * @menu: a #GMenu |
||
368 | * @label: (allow-none): 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 | g_menu_append_section (GMenu *menu, |
||
379 | const gchar *label, |
||
380 | GMenuModel *section) |
||
381 | { |
||
382 | g_menu_insert_section (menu, -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: (allow-none): 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 | g_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, menu_item); |
||
408 | g_object_unref (menu_item); |
||
409 | } |
||
410 | |||
411 | /** |
||
412 | * g_menu_prepend_submenu: |
||
413 | * @menu: a #GMenu |
||
414 | * @label: (allow-none): 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 | g_menu_prepend_submenu (GMenu *menu, |
||
425 | const gchar *label, |
||
426 | GMenuModel *submenu) |
||
427 | { |
||
428 | g_menu_insert_submenu (menu, 0, label, submenu); |
||
429 | } |
||
430 | |||
431 | /** |
||
432 | * g_menu_append_submenu: |
||
433 | * @menu: a #GMenu |
||
434 | * @label: (allow-none): 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 | g_menu_append_submenu (GMenu *menu, |
||
445 | const gchar *label, |
||
446 | GMenuModel *submenu) |
||
447 | { |
||
448 | g_menu_insert_submenu (menu, -1, label, submenu); |
||
449 | } |
||
450 | |||
451 | static void |
||
452 | g_menu_clear_item (struct item *item) |
||
453 | { |
||
454 | if (item->attributes != NULL) |
||
455 | g_hash_table_unref (item->attributes); |
||
456 | if (item->links != NULL) |
||
457 | g_hash_table_unref (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 | g_menu_remove (GMenu *menu, |
||
480 | gint position) |
||
481 | { |
||
482 | g_return_if_fail (G_IS_MENU (menu)); |
||
483 | g_return_if_fail (0 <= position && position < menu->items->len); |
||
484 | |||
485 | g_menu_clear_item (&g_array_index (menu->items, struct item, position)); |
||
486 | g_array_remove_index (menu->items, position); |
||
487 | g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 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 | g_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 (&g_array_index (menu->items, struct item, i)); |
||
508 | g_array_set_size (menu->items, 0); |
||
509 | |||
510 | g_menu_model_items_changed (G_MENU_MODEL (menu), 0, n, 0); |
||
511 | } |
||
512 | |||
513 | static void |
||
514 | g_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 (menu->items, FALSE); |
||
523 | for (i = 0; i < n_items; i++) |
||
524 | g_menu_clear_item (&items[i]); |
||
525 | g_free (items); |
||
526 | |||
527 | G_OBJECT_CLASS (g_menu_parent_class) |
||
528 | ->finalize (object); |
||
529 | } |
||
530 | |||
531 | static void |
||
532 | g_menu_init (GMenu *menu) |
||
533 | { |
||
534 | menu->items = g_array_new (FALSE, FALSE, sizeof (struct item)); |
||
535 | menu->mutable = TRUE; |
||
536 | } |
||
537 | |||
538 | static void |
||
539 | g_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 | |||
553 | static void |
||
554 | g_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 (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); |
||
564 | g_hash_table_iter_init (&iter, menu_item->attributes); |
||
565 | while (g_hash_table_iter_next (&iter, &key, &val)) |
||
566 | g_hash_table_insert (new, g_strdup (key), g_variant_ref (val)); |
||
567 | g_hash_table_unref (menu_item->attributes); |
||
568 | menu_item->attributes = new; |
||
569 | |||
570 | new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); |
||
571 | g_hash_table_iter_init (&iter, menu_item->links); |
||
572 | while (g_hash_table_iter_next (&iter, &key, &val)) |
||
573 | g_hash_table_insert (new, g_strdup (key), g_object_ref (val)); |
||
574 | g_hash_table_unref (menu_item->links); |
||
575 | menu_item->links = new; |
||
576 | |||
577 | menu_item->cow = FALSE; |
||
578 | } |
||
579 | } |
||
580 | |||
581 | static void |
||
582 | g_menu_item_finalize (GObject *object) |
||
583 | { |
||
584 | GMenuItem *menu_item = G_MENU_ITEM (object); |
||
585 | |||
586 | g_hash_table_unref (menu_item->attributes); |
||
587 | g_hash_table_unref (menu_item->links); |
||
588 | |||
589 | G_OBJECT_CLASS (g_menu_item_parent_class) |
||
590 | ->finalize (object); |
||
591 | } |
||
592 | |||
593 | static void |
||
594 | g_menu_item_init (GMenuItem *menu_item) |
||
595 | { |
||
596 | menu_item->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); |
||
597 | menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); |
||
598 | menu_item->cow = FALSE; |
||
599 | } |
||
600 | |||
601 | static void |
||
602 | g_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 | */ |
||
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: (allow-none): 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 | g_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 (menu_item->attributes, g_strdup (attribute), g_variant_ref_sink (value)); |
||
683 | else |
||
684 | g_hash_table_remove (menu_item->attributes, attribute); |
||
685 | } |
||
686 | |||
687 | /** |
||
688 | * g_menu_item_set_attribute: |
||
689 | * @menu_item: a #GMenuItem |
||
690 | * @attribute: the attribute to set |
||
691 | * @format_string: (allow-none): 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 | g_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, &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: (allow-none): 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 | g_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 (menu_item->links, g_strdup (link), g_object_ref (model)); |
||
768 | else |
||
769 | g_hash_table_remove (menu_item->links, 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: (allow-none): 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: (transfer full): the attribute value, or %NULL |
||
785 | * |
||
786 | * Since: 2.34 |
||
787 | */ |
||
788 | GVariant * |
||
789 | g_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 (menu_item->attributes, attribute); |
||
799 | |||
800 | if (value != NULL) |
||
801 | { |
||
802 | if (expected_type == NULL || g_variant_is_of_type (value, 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 | g_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 (menu_item->attributes, 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, &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: (transfer full): the link, or %NULL |
||
869 | * |
||
870 | * Since: 2.34 |
||
871 | */ |
||
872 | GMenuModel * |
||
873 | g_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 (menu_item->links, 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: (allow-none): 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 | g_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 (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: (allow-none): 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 | g_menu_item_set_submenu (GMenuItem *menu_item, |
||
933 | GMenuModel *submenu) |
||
934 | { |
||
935 | g_menu_item_set_link (menu_item, G_MENU_LINK_SUBMENU, submenu); |
||
936 | } |
||
937 | |||
938 | /** |
||
939 | * g_menu_item_set_section: |
||
940 | * @menu_item: a #GMenuItem |
||
941 | * @section: (allow-none): 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 | g_menu_item_set_section (GMenuItem *menu_item, |
||
955 | GMenuModel *section) |
||
956 | { |
||
957 | g_menu_item_set_link (menu_item, G_MENU_LINK_SECTION, section); |
||
958 | } |
||
959 | |||
960 | /** |
||
961 | * g_menu_item_set_action_and_target_value: |
||
962 | * @menu_item: a #GMenuItem |
||
963 | * @action: (allow-none): the name of the action for this item |
||
964 | * @target_value: (allow-none): 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 *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 (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, action_value); |
||
1023 | g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_TARGET, target_value); |
||
1024 | } |
||
1025 | |||
1026 | /** |
||
1027 | * g_menu_item_set_action_and_target: |
||
1028 | * @menu_item: a #GMenuItem |
||
1029 | * @action: (allow-none): the name of the action for this item |
||
1030 | * @format_string: (allow-none): 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 *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, &ap); |
||
1069 | va_end (ap); |
||
1070 | } |
||
1071 | else |
||
1072 | value = NULL; |
||
1073 | |||
1074 | g_menu_item_set_action_and_target_value (menu_item, action, 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 | g_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_action, &name, &target, &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, name, target); |
||
1108 | if (target) |
||
1109 | g_variant_unref (target); |
||
1110 | g_free (name); |
||
1111 | } |
||
1112 | |||
1113 | /** |
||
1114 | * g_menu_item_new: |
||
1115 | * @label: (allow-none): the section label, or %NULL |
||
1116 | * @detailed_action: (allow-none): 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 | g_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: (allow-none): 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 | g_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: (allow-none): 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 | g_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 | */ |
||
1279 | GMenuItem * |
||
1280 | g_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 (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, &attribute, &value)) |
||
1319 | g_hash_table_insert (menu_item->attributes, g_strdup (attribute), value); |
||
1320 | g_object_unref (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 (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, &link, &value)) |
||
1344 | g_hash_table_insert (menu_item->links, g_strdup (link), value); |
||
1345 | g_object_unref (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 | g_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 | } |