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 "gmenumodel.h" |
23 | |
24 | #include "glibintl.h" |
25 | #include "gmarshal-internal.h" |
26 | |
27 | /** |
28 | * SECTION:gmenumodel |
29 | * @title: GMenuModel |
30 | * @short_description: An abstract class representing the contents of a menu |
31 | * @include: gio/gio.h |
32 | * @see_also: #GActionGroup |
33 | * |
34 | * #GMenuModel represents the contents of a menu -- an ordered list of |
35 | * menu items. The items are associated with actions, which can be |
36 | * activated through them. Items can be grouped in sections, and may |
37 | * have submenus associated with them. Both items and sections usually |
38 | * have some representation data, such as labels or icons. The type of |
39 | * the associated action (ie whether it is stateful, and what kind of |
40 | * state it has) can influence the representation of the item. |
41 | * |
42 | * The conceptual model of menus in #GMenuModel is hierarchical: |
43 | * sections and submenus are again represented by #GMenuModels. |
44 | * Menus themselves do not define their own roles. Rather, the role |
45 | * of a particular #GMenuModel is defined by the item that references |
46 | * it (or, in the case of the 'root' menu, is defined by the context |
47 | * in which it is used). |
48 | * |
49 | * As an example, consider the visible portions of this menu: |
50 | * |
51 | * ## An example menu # {#menu-example} |
52 | * |
53 | * ![](menu-example.png) |
54 | * |
55 | * There are 8 "menus" visible in the screenshot: one menubar, two |
56 | * submenus and 5 sections: |
57 | * |
58 | * - the toplevel menubar (containing 4 items) |
59 | * - the View submenu (containing 3 sections) |
60 | * - the first section of the View submenu (containing 2 items) |
61 | * - the second section of the View submenu (containing 1 item) |
62 | * - the final section of the View submenu (containing 1 item) |
63 | * - the Highlight Mode submenu (containing 2 sections) |
64 | * - the Sources section (containing 2 items) |
65 | * - the Markup section (containing 2 items) |
66 | * |
67 | * The [example][menu-model] illustrates the conceptual connection between |
68 | * these 8 menus. Each large block in the figure represents a menu and the |
69 | * smaller blocks within the large block represent items in that menu. Some |
70 | * items contain references to other menus. |
71 | * |
72 | * ## A menu example # {#menu-model} |
73 | * |
74 | * ![](menu-model.png) |
75 | * |
76 | * Notice that the separators visible in the [example][menu-example] |
77 | * appear nowhere in the [menu model][menu-model]. This is because |
78 | * separators are not explicitly represented in the menu model. Instead, |
79 | * a separator is inserted between any two non-empty sections of a menu. |
80 | * Section items can have labels just like any other item. In that case, |
81 | * a display system may show a section header instead of a separator. |
82 | * |
83 | * The motivation for this abstract model of application controls is |
84 | * that modern user interfaces tend to make these controls available |
85 | * outside the application. Examples include global menus, jumplists, |
86 | * dash boards, etc. To support such uses, it is necessary to 'export' |
87 | * information about actions and their representation in menus, which |
88 | * is exactly what the [GActionGroup exporter][gio-GActionGroup-exporter] |
89 | * and the [GMenuModel exporter][gio-GMenuModel-exporter] do for |
90 | * #GActionGroup and #GMenuModel. The client-side counterparts to |
91 | * make use of the exported information are #GDBusActionGroup and |
92 | * #GDBusMenuModel. |
93 | * |
94 | * The API of #GMenuModel is very generic, with iterators for the |
95 | * attributes and links of an item, see g_menu_model_iterate_item_attributes() |
96 | * and g_menu_model_iterate_item_links(). The 'standard' attributes and |
97 | * link types have predefined names: %G_MENU_ATTRIBUTE_LABEL, |
98 | * %G_MENU_ATTRIBUTE_ACTION, %G_MENU_ATTRIBUTE_TARGET, %G_MENU_LINK_SECTION |
99 | * and %G_MENU_LINK_SUBMENU. |
100 | * |
101 | * Items in a #GMenuModel represent active controls if they refer to |
102 | * an action that can get activated when the user interacts with the |
103 | * menu item. The reference to the action is encoded by the string id |
104 | * in the %G_MENU_ATTRIBUTE_ACTION attribute. An action id uniquely |
105 | * identifies an action in an action group. Which action group(s) provide |
106 | * actions depends on the context in which the menu model is used. |
107 | * E.g. when the model is exported as the application menu of a |
108 | * #GtkApplication, actions can be application-wide or window-specific |
109 | * (and thus come from two different action groups). By convention, the |
110 | * application-wide actions have names that start with "app.", while the |
111 | * names of window-specific actions start with "win.". |
112 | * |
113 | * While a wide variety of stateful actions is possible, the following |
114 | * is the minimum that is expected to be supported by all users of exported |
115 | * menu information: |
116 | * - an action with no parameter type and no state |
117 | * - an action with no parameter type and boolean state |
118 | * - an action with string parameter type and string state |
119 | * |
120 | * ## Stateless |
121 | * |
122 | * A stateless action typically corresponds to an ordinary menu item. |
123 | * |
124 | * Selecting such a menu item will activate the action (with no parameter). |
125 | * |
126 | * ## Boolean State |
127 | * |
128 | * An action with a boolean state will most typically be used with a "toggle" |
129 | * or "switch" menu item. The state can be set directly, but activating the |
130 | * action (with no parameter) results in the state being toggled. |
131 | * |
132 | * Selecting a toggle menu item will activate the action. The menu item should |
133 | * be rendered as "checked" when the state is true. |
134 | * |
135 | * ## String Parameter and State |
136 | * |
137 | * Actions with string parameters and state will most typically be used to |
138 | * represent an enumerated choice over the items available for a group of |
139 | * radio menu items. Activating the action with a string parameter is |
140 | * equivalent to setting that parameter as the state. |
141 | * |
142 | * Radio menu items, in addition to being associated with the action, will |
143 | * have a target value. Selecting that menu item will result in activation |
144 | * of the action with the target value as the parameter. The menu item should |
145 | * be rendered as "selected" when the state of the action is equal to the |
146 | * target value of the menu item. |
147 | */ |
148 | |
149 | /** |
150 | * GMenuModel: |
151 | * |
152 | * #GMenuModel is an opaque structure type. You must access it using the |
153 | * functions below. |
154 | * |
155 | * Since: 2.32 |
156 | */ |
157 | |
158 | /** |
159 | * GMenuAttributeIter: |
160 | * |
161 | * #GMenuAttributeIter is an opaque structure type. You must access it |
162 | * using the functions below. |
163 | * |
164 | * Since: 2.32 |
165 | */ |
166 | |
167 | /** |
168 | * GMenuLinkIter: |
169 | * |
170 | * #GMenuLinkIter is an opaque structure type. You must access it using |
171 | * the functions below. |
172 | * |
173 | * Since: 2.32 |
174 | */ |
175 | |
176 | typedef struct |
177 | { |
178 | GMenuLinkIter ; |
179 | GHashTableIter ; |
180 | GHashTable *; |
181 | } ; |
182 | |
183 | typedef GMenuLinkIterClass ; |
184 | |
185 | static GType g_menu_link_hash_iter_get_type (void); |
186 | |
187 | G_DEFINE_TYPE (GMenuLinkHashIter, g_menu_link_hash_iter, G_TYPE_MENU_LINK_ITER) |
188 | |
189 | static gboolean |
190 | (GMenuLinkIter *link_iter, |
191 | const gchar **out_name, |
192 | GMenuModel **value) |
193 | { |
194 | GMenuLinkHashIter *iter = (GMenuLinkHashIter *) link_iter; |
195 | gpointer keyptr, valueptr; |
196 | |
197 | if (!g_hash_table_iter_next (iter: &iter->iter, key: &keyptr, value: &valueptr)) |
198 | return FALSE; |
199 | |
200 | *out_name = keyptr; |
201 | *value = g_object_ref (valueptr); |
202 | |
203 | return TRUE; |
204 | } |
205 | |
206 | static void |
207 | (GObject *object) |
208 | { |
209 | GMenuLinkHashIter *iter = (GMenuLinkHashIter *) object; |
210 | |
211 | g_hash_table_unref (hash_table: iter->table); |
212 | |
213 | G_OBJECT_CLASS (g_menu_link_hash_iter_parent_class) |
214 | ->finalize (object); |
215 | } |
216 | |
217 | static void |
218 | (GMenuLinkHashIter *iter) |
219 | { |
220 | } |
221 | |
222 | static void |
223 | (GMenuLinkHashIterClass *class) |
224 | { |
225 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
226 | |
227 | object_class->finalize = g_menu_link_hash_iter_finalize; |
228 | class->get_next = g_menu_link_hash_iter_get_next; |
229 | } |
230 | |
231 | |
232 | typedef struct |
233 | { |
234 | GMenuAttributeIter ; |
235 | GHashTableIter ; |
236 | GHashTable *; |
237 | } ; |
238 | |
239 | typedef GMenuAttributeIterClass ; |
240 | |
241 | static GType g_menu_attribute_hash_iter_get_type (void); |
242 | |
243 | G_DEFINE_TYPE (GMenuAttributeHashIter, g_menu_attribute_hash_iter, G_TYPE_MENU_ATTRIBUTE_ITER) |
244 | |
245 | static gboolean |
246 | (GMenuAttributeIter *attr_iter, |
247 | const gchar **name, |
248 | GVariant **value) |
249 | { |
250 | GMenuAttributeHashIter *iter = (GMenuAttributeHashIter *) attr_iter; |
251 | gpointer keyptr, valueptr; |
252 | |
253 | if (!g_hash_table_iter_next (iter: &iter->iter, key: &keyptr, value: &valueptr)) |
254 | return FALSE; |
255 | |
256 | *name = keyptr; |
257 | |
258 | *value = g_variant_ref (value: valueptr); |
259 | |
260 | return TRUE; |
261 | } |
262 | |
263 | static void |
264 | (GObject *object) |
265 | { |
266 | GMenuAttributeHashIter *iter = (GMenuAttributeHashIter *) object; |
267 | |
268 | g_hash_table_unref (hash_table: iter->table); |
269 | |
270 | G_OBJECT_CLASS (g_menu_attribute_hash_iter_parent_class) |
271 | ->finalize (object); |
272 | } |
273 | |
274 | static void |
275 | (GMenuAttributeHashIter *iter) |
276 | { |
277 | } |
278 | |
279 | static void |
280 | (GMenuAttributeHashIterClass *class) |
281 | { |
282 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
283 | |
284 | object_class->finalize = g_menu_attribute_hash_iter_finalize; |
285 | class->get_next = g_menu_attribute_hash_iter_get_next; |
286 | } |
287 | |
288 | G_DEFINE_ABSTRACT_TYPE (GMenuModel, g_menu_model, G_TYPE_OBJECT) |
289 | |
290 | |
291 | static guint ; |
292 | |
293 | static GMenuAttributeIter * |
294 | (GMenuModel *model, |
295 | gint item_index) |
296 | { |
297 | GHashTable *table = NULL; |
298 | GMenuAttributeIter *result; |
299 | |
300 | G_MENU_MODEL_GET_CLASS (model)->get_item_attributes (model, item_index, &table); |
301 | |
302 | if (table) |
303 | { |
304 | GMenuAttributeHashIter *iter = g_object_new (object_type: g_menu_attribute_hash_iter_get_type (), NULL); |
305 | g_hash_table_iter_init (iter: &iter->iter, hash_table: table); |
306 | iter->table = g_hash_table_ref (hash_table: table); |
307 | result = G_MENU_ATTRIBUTE_ITER (iter); |
308 | } |
309 | else |
310 | { |
311 | g_critical ("GMenuModel implementation '%s' doesn't override iterate_item_attributes() " |
312 | "and fails to return valid values from get_item_attributes()" , |
313 | G_OBJECT_TYPE_NAME (model)); |
314 | result = NULL; |
315 | } |
316 | |
317 | if (table != NULL) |
318 | g_hash_table_unref (hash_table: table); |
319 | |
320 | return result; |
321 | } |
322 | |
323 | static GVariant * |
324 | (GMenuModel *model, |
325 | gint item_index, |
326 | const gchar *attribute, |
327 | const GVariantType *expected_type) |
328 | { |
329 | GHashTable *table = NULL; |
330 | GVariant *value = NULL; |
331 | |
332 | G_MENU_MODEL_GET_CLASS (model) |
333 | ->get_item_attributes (model, item_index, &table); |
334 | |
335 | if (table != NULL) |
336 | { |
337 | value = g_hash_table_lookup (hash_table: table, key: attribute); |
338 | |
339 | if (value != NULL) |
340 | { |
341 | if (expected_type == NULL || g_variant_is_of_type (value, type: expected_type)) |
342 | value = g_variant_ref (value); |
343 | else |
344 | value = NULL; |
345 | } |
346 | } |
347 | else |
348 | g_assert_not_reached (); |
349 | |
350 | if (table != NULL) |
351 | g_hash_table_unref (hash_table: table); |
352 | |
353 | return value; |
354 | } |
355 | |
356 | static GMenuLinkIter * |
357 | (GMenuModel *model, |
358 | gint item_index) |
359 | { |
360 | GHashTable *table = NULL; |
361 | GMenuLinkIter *result; |
362 | |
363 | G_MENU_MODEL_GET_CLASS (model) |
364 | ->get_item_links (model, item_index, &table); |
365 | |
366 | if (table) |
367 | { |
368 | GMenuLinkHashIter *iter = g_object_new (object_type: g_menu_link_hash_iter_get_type (), NULL); |
369 | g_hash_table_iter_init (iter: &iter->iter, hash_table: table); |
370 | iter->table = g_hash_table_ref (hash_table: table); |
371 | result = G_MENU_LINK_ITER (iter); |
372 | } |
373 | else |
374 | { |
375 | g_critical ("GMenuModel implementation '%s' doesn't override iterate_item_links() " |
376 | "and fails to return valid values from get_item_links()" , |
377 | G_OBJECT_TYPE_NAME (model)); |
378 | result = NULL; |
379 | } |
380 | |
381 | if (table != NULL) |
382 | g_hash_table_unref (hash_table: table); |
383 | |
384 | return result; |
385 | } |
386 | |
387 | static GMenuModel * |
388 | (GMenuModel *model, |
389 | gint item_index, |
390 | const gchar *link) |
391 | { |
392 | GHashTable *table = NULL; |
393 | GMenuModel *value = NULL; |
394 | |
395 | G_MENU_MODEL_GET_CLASS (model) |
396 | ->get_item_links (model, item_index, &table); |
397 | |
398 | if (table != NULL) |
399 | value = g_hash_table_lookup (hash_table: table, key: link); |
400 | else |
401 | g_assert_not_reached (); |
402 | |
403 | if (value != NULL) |
404 | g_object_ref (value); |
405 | |
406 | if (table != NULL) |
407 | g_hash_table_unref (hash_table: table); |
408 | |
409 | return value; |
410 | } |
411 | |
412 | static void |
413 | (GMenuModel *model) |
414 | { |
415 | } |
416 | |
417 | static void |
418 | (GMenuModelClass *class) |
419 | { |
420 | class->iterate_item_attributes = g_menu_model_real_iterate_item_attributes; |
421 | class->get_item_attribute_value = g_menu_model_real_get_item_attribute_value; |
422 | class->iterate_item_links = g_menu_model_real_iterate_item_links; |
423 | class->get_item_link = g_menu_model_real_get_item_link; |
424 | |
425 | /** |
426 | * GMenuModel::items-changed: |
427 | * @model: the #GMenuModel that is changing |
428 | * @position: the position of the change |
429 | * @removed: the number of items removed |
430 | * @added: the number of items added |
431 | * |
432 | * Emitted when a change has occurred to the menu. |
433 | * |
434 | * The only changes that can occur to a menu is that items are removed |
435 | * or added. Items may not change (except by being removed and added |
436 | * back in the same location). This signal is capable of describing |
437 | * both of those changes (at the same time). |
438 | * |
439 | * The signal means that starting at the index @position, @removed |
440 | * items were removed and @added items were added in their place. If |
441 | * @removed is zero then only items were added. If @added is zero |
442 | * then only items were removed. |
443 | * |
444 | * As an example, if the menu contains items a, b, c, d (in that |
445 | * order) and the signal (2, 1, 3) occurs then the new composition of |
446 | * the menu will be a, b, _, _, _, d (with each _ representing some |
447 | * new item). |
448 | * |
449 | * Signal handlers may query the model (particularly the added items) |
450 | * and expect to see the results of the modification that is being |
451 | * reported. The signal is emitted after the modification. |
452 | **/ |
453 | g_menu_model_items_changed_signal = |
454 | g_signal_new (I_("items-changed" ), G_TYPE_MENU_MODEL, |
455 | signal_flags: G_SIGNAL_RUN_LAST, class_offset: 0, NULL, NULL, |
456 | c_marshaller: _g_cclosure_marshal_VOID__INT_INT_INT, |
457 | G_TYPE_NONE, |
458 | n_params: 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); |
459 | g_signal_set_va_marshaller (signal_id: g_menu_model_items_changed_signal, |
460 | G_TYPE_FROM_CLASS (class), |
461 | va_marshaller: _g_cclosure_marshal_VOID__INT_INT_INTv); |
462 | } |
463 | |
464 | /** |
465 | * g_menu_model_is_mutable: |
466 | * @model: a #GMenuModel |
467 | * |
468 | * Queries if @model is mutable. |
469 | * |
470 | * An immutable #GMenuModel will never emit the #GMenuModel::items-changed |
471 | * signal. Consumers of the model may make optimisations accordingly. |
472 | * |
473 | * Returns: %TRUE if the model is mutable (ie: "items-changed" may be |
474 | * emitted). |
475 | * |
476 | * Since: 2.32 |
477 | */ |
478 | gboolean |
479 | (GMenuModel *model) |
480 | { |
481 | return G_MENU_MODEL_GET_CLASS (model) |
482 | ->is_mutable (model); |
483 | } |
484 | |
485 | /** |
486 | * g_menu_model_get_n_items: |
487 | * @model: a #GMenuModel |
488 | * |
489 | * Query the number of items in @model. |
490 | * |
491 | * Returns: the number of items |
492 | * |
493 | * Since: 2.32 |
494 | */ |
495 | gint |
496 | (GMenuModel *model) |
497 | { |
498 | return G_MENU_MODEL_GET_CLASS (model) |
499 | ->get_n_items (model); |
500 | } |
501 | |
502 | /** |
503 | * g_menu_model_iterate_item_attributes: |
504 | * @model: a #GMenuModel |
505 | * @item_index: the index of the item |
506 | * |
507 | * Creates a #GMenuAttributeIter to iterate over the attributes of |
508 | * the item at position @item_index in @model. |
509 | * |
510 | * You must free the iterator with g_object_unref() when you are done. |
511 | * |
512 | * Returns: (transfer full): a new #GMenuAttributeIter |
513 | * |
514 | * Since: 2.32 |
515 | */ |
516 | GMenuAttributeIter * |
517 | (GMenuModel *model, |
518 | gint item_index) |
519 | { |
520 | return G_MENU_MODEL_GET_CLASS (model) |
521 | ->iterate_item_attributes (model, item_index); |
522 | } |
523 | |
524 | /** |
525 | * g_menu_model_get_item_attribute_value: |
526 | * @model: a #GMenuModel |
527 | * @item_index: the index of the item |
528 | * @attribute: the attribute to query |
529 | * @expected_type: (nullable): the expected type of the attribute, or |
530 | * %NULL |
531 | * |
532 | * Queries the item at position @item_index in @model for the attribute |
533 | * specified by @attribute. |
534 | * |
535 | * If @expected_type is non-%NULL then it specifies the expected type of |
536 | * the attribute. If it is %NULL then any type will be accepted. |
537 | * |
538 | * If the attribute exists and matches @expected_type (or if the |
539 | * expected type is unspecified) then the value is returned. |
540 | * |
541 | * If the attribute does not exist, or does not match the expected type |
542 | * then %NULL is returned. |
543 | * |
544 | * Returns: (nullable) (transfer full): the value of the attribute |
545 | * |
546 | * Since: 2.32 |
547 | */ |
548 | GVariant * |
549 | (GMenuModel *model, |
550 | gint item_index, |
551 | const gchar *attribute, |
552 | const GVariantType *expected_type) |
553 | { |
554 | return G_MENU_MODEL_GET_CLASS (model) |
555 | ->get_item_attribute_value (model, item_index, attribute, expected_type); |
556 | } |
557 | |
558 | /** |
559 | * g_menu_model_get_item_attribute: |
560 | * @model: a #GMenuModel |
561 | * @item_index: the index of the item |
562 | * @attribute: the attribute to query |
563 | * @format_string: a #GVariant format string |
564 | * @...: positional parameters, as per @format_string |
565 | * |
566 | * Queries item at position @item_index in @model for the attribute |
567 | * specified by @attribute. |
568 | * |
569 | * If the attribute exists and matches the #GVariantType corresponding |
570 | * to @format_string then @format_string is used to deconstruct the |
571 | * value into the positional parameters and %TRUE is returned. |
572 | * |
573 | * If the attribute does not exist, or it does exist but has the wrong |
574 | * type, then the positional parameters are ignored and %FALSE is |
575 | * returned. |
576 | * |
577 | * This function is a mix of g_menu_model_get_item_attribute_value() and |
578 | * g_variant_get(), followed by a g_variant_unref(). As such, |
579 | * @format_string must make a complete copy of the data (since the |
580 | * #GVariant may go away after the call to g_variant_unref()). In |
581 | * particular, no '&' characters are allowed in @format_string. |
582 | * |
583 | * Returns: %TRUE if the named attribute was found with the expected |
584 | * type |
585 | * |
586 | * Since: 2.32 |
587 | */ |
588 | gboolean |
589 | (GMenuModel *model, |
590 | gint item_index, |
591 | const gchar *attribute, |
592 | const gchar *format_string, |
593 | ...) |
594 | { |
595 | GVariant *value; |
596 | va_list ap; |
597 | |
598 | value = g_menu_model_get_item_attribute_value (model, item_index, attribute, NULL); |
599 | |
600 | if (value == NULL) |
601 | return FALSE; |
602 | |
603 | if (!g_variant_check_format_string (value, format_string, TRUE)) |
604 | { |
605 | g_variant_unref (value); |
606 | return FALSE; |
607 | } |
608 | |
609 | va_start (ap, format_string); |
610 | g_variant_get_va (value, format_string, NULL, app: &ap); |
611 | g_variant_unref (value); |
612 | va_end (ap); |
613 | |
614 | return TRUE; |
615 | } |
616 | |
617 | /** |
618 | * g_menu_model_iterate_item_links: |
619 | * @model: a #GMenuModel |
620 | * @item_index: the index of the item |
621 | * |
622 | * Creates a #GMenuLinkIter to iterate over the links of the item at |
623 | * position @item_index in @model. |
624 | * |
625 | * You must free the iterator with g_object_unref() when you are done. |
626 | * |
627 | * Returns: (transfer full): a new #GMenuLinkIter |
628 | * |
629 | * Since: 2.32 |
630 | */ |
631 | GMenuLinkIter * |
632 | (GMenuModel *model, |
633 | gint item_index) |
634 | { |
635 | return G_MENU_MODEL_GET_CLASS (model) |
636 | ->iterate_item_links (model, item_index); |
637 | } |
638 | |
639 | /** |
640 | * g_menu_model_get_item_link: |
641 | * @model: a #GMenuModel |
642 | * @item_index: the index of the item |
643 | * @link: the link to query |
644 | * |
645 | * Queries the item at position @item_index in @model for the link |
646 | * specified by @link. |
647 | * |
648 | * If the link exists, the linked #GMenuModel is returned. If the link |
649 | * does not exist, %NULL is returned. |
650 | * |
651 | * Returns: (nullable) (transfer full): the linked #GMenuModel, or %NULL |
652 | * |
653 | * Since: 2.32 |
654 | */ |
655 | GMenuModel * |
656 | (GMenuModel *model, |
657 | gint item_index, |
658 | const gchar *link) |
659 | { |
660 | return G_MENU_MODEL_GET_CLASS (model) |
661 | ->get_item_link (model, item_index, link); |
662 | } |
663 | |
664 | /** |
665 | * g_menu_model_items_changed: |
666 | * @model: a #GMenuModel |
667 | * @position: the position of the change |
668 | * @removed: the number of items removed |
669 | * @added: the number of items added |
670 | * |
671 | * Requests emission of the #GMenuModel::items-changed signal on @model. |
672 | * |
673 | * This function should never be called except by #GMenuModel |
674 | * subclasses. Any other calls to this function will very likely lead |
675 | * to a violation of the interface of the model. |
676 | * |
677 | * The implementation should update its internal representation of the |
678 | * menu before emitting the signal. The implementation should further |
679 | * expect to receive queries about the new state of the menu (and |
680 | * particularly added menu items) while signal handlers are running. |
681 | * |
682 | * The implementation must dispatch this call directly from a mainloop |
683 | * entry and not in response to calls -- particularly those from the |
684 | * #GMenuModel API. Said another way: the menu must not change while |
685 | * user code is running without returning to the mainloop. |
686 | * |
687 | * Since: 2.32 |
688 | */ |
689 | void |
690 | (GMenuModel *model, |
691 | gint position, |
692 | gint removed, |
693 | gint added) |
694 | { |
695 | g_signal_emit (instance: model, signal_id: g_menu_model_items_changed_signal, detail: 0, position, removed, added); |
696 | } |
697 | |
698 | struct |
699 | { |
700 | GQuark ; |
701 | GVariant *; |
702 | gboolean ; |
703 | }; |
704 | |
705 | G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GMenuAttributeIter, g_menu_attribute_iter, G_TYPE_OBJECT) |
706 | |
707 | /** |
708 | * g_menu_attribute_iter_get_next: |
709 | * @iter: a #GMenuAttributeIter |
710 | * @out_name: (out) (optional) (transfer none): the type of the attribute |
711 | * @value: (out) (optional) (transfer full): the attribute value |
712 | * |
713 | * This function combines g_menu_attribute_iter_next() with |
714 | * g_menu_attribute_iter_get_name() and g_menu_attribute_iter_get_value(). |
715 | * |
716 | * First the iterator is advanced to the next (possibly first) attribute. |
717 | * If that fails, then %FALSE is returned and there are no other |
718 | * effects. |
719 | * |
720 | * If successful, @name and @value are set to the name and value of the |
721 | * attribute that has just been advanced to. At this point, |
722 | * g_menu_attribute_iter_get_name() and g_menu_attribute_iter_get_value() will |
723 | * return the same values again. |
724 | * |
725 | * The value returned in @name remains valid for as long as the iterator |
726 | * remains at the current position. The value returned in @value must |
727 | * be unreffed using g_variant_unref() when it is no longer in use. |
728 | * |
729 | * Returns: %TRUE on success, or %FALSE if there is no additional |
730 | * attribute |
731 | * |
732 | * Since: 2.32 |
733 | */ |
734 | gboolean |
735 | (GMenuAttributeIter *iter, |
736 | const gchar **out_name, |
737 | GVariant **value) |
738 | { |
739 | const gchar *name; |
740 | |
741 | if (iter->priv->value) |
742 | { |
743 | g_variant_unref (value: iter->priv->value); |
744 | iter->priv->value = NULL; |
745 | } |
746 | |
747 | iter->priv->valid = G_MENU_ATTRIBUTE_ITER_GET_CLASS (iter) |
748 | ->get_next (iter, &name, &iter->priv->value); |
749 | |
750 | if (iter->priv->valid) |
751 | { |
752 | iter->priv->name = g_quark_from_string (string: name); |
753 | if (out_name) |
754 | *out_name = g_quark_to_string (quark: iter->priv->name); |
755 | |
756 | if (value) |
757 | *value = g_variant_ref (value: iter->priv->value); |
758 | } |
759 | |
760 | return iter->priv->valid; |
761 | } |
762 | |
763 | /** |
764 | * g_menu_attribute_iter_next: |
765 | * @iter: a #GMenuAttributeIter |
766 | * |
767 | * Attempts to advance the iterator to the next (possibly first) |
768 | * attribute. |
769 | * |
770 | * %TRUE is returned on success, or %FALSE if there are no more |
771 | * attributes. |
772 | * |
773 | * You must call this function when you first acquire the iterator |
774 | * to advance it to the first attribute (and determine if the first |
775 | * attribute exists at all). |
776 | * |
777 | * Returns: %TRUE on success, or %FALSE when there are no more attributes |
778 | * |
779 | * Since: 2.32 |
780 | */ |
781 | gboolean |
782 | (GMenuAttributeIter *iter) |
783 | { |
784 | return g_menu_attribute_iter_get_next (iter, NULL, NULL); |
785 | } |
786 | |
787 | /** |
788 | * g_menu_attribute_iter_get_name: |
789 | * @iter: a #GMenuAttributeIter |
790 | * |
791 | * Gets the name of the attribute at the current iterator position, as |
792 | * a string. |
793 | * |
794 | * The iterator is not advanced. |
795 | * |
796 | * Returns: the name of the attribute |
797 | * |
798 | * Since: 2.32 |
799 | */ |
800 | const gchar * |
801 | (GMenuAttributeIter *iter) |
802 | { |
803 | g_return_val_if_fail (iter->priv->valid, 0); |
804 | |
805 | return g_quark_to_string (quark: iter->priv->name); |
806 | } |
807 | |
808 | /** |
809 | * g_menu_attribute_iter_get_value: |
810 | * @iter: a #GMenuAttributeIter |
811 | * |
812 | * Gets the value of the attribute at the current iterator position. |
813 | * |
814 | * The iterator is not advanced. |
815 | * |
816 | * Returns: (transfer full): the value of the current attribute |
817 | * |
818 | * Since: 2.32 |
819 | */ |
820 | GVariant * |
821 | (GMenuAttributeIter *iter) |
822 | { |
823 | g_return_val_if_fail (iter->priv->valid, NULL); |
824 | |
825 | return g_variant_ref (value: iter->priv->value); |
826 | } |
827 | |
828 | static void |
829 | (GObject *object) |
830 | { |
831 | GMenuAttributeIter *iter = G_MENU_ATTRIBUTE_ITER (object); |
832 | |
833 | if (iter->priv->value) |
834 | g_variant_unref (value: iter->priv->value); |
835 | |
836 | G_OBJECT_CLASS (g_menu_attribute_iter_parent_class) |
837 | ->finalize (object); |
838 | } |
839 | |
840 | static void |
841 | (GMenuAttributeIter *iter) |
842 | { |
843 | iter->priv = g_menu_attribute_iter_get_instance_private (self: iter); |
844 | } |
845 | |
846 | static void |
847 | (GMenuAttributeIterClass *class) |
848 | { |
849 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
850 | |
851 | object_class->finalize = g_menu_attribute_iter_finalize; |
852 | } |
853 | |
854 | struct |
855 | { |
856 | GQuark ; |
857 | GMenuModel *; |
858 | gboolean ; |
859 | }; |
860 | |
861 | G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GMenuLinkIter, g_menu_link_iter, G_TYPE_OBJECT) |
862 | |
863 | /** |
864 | * g_menu_link_iter_get_next: |
865 | * @iter: a #GMenuLinkIter |
866 | * @out_link: (out) (optional) (transfer none): the name of the link |
867 | * @value: (out) (optional) (transfer full): the linked #GMenuModel |
868 | * |
869 | * This function combines g_menu_link_iter_next() with |
870 | * g_menu_link_iter_get_name() and g_menu_link_iter_get_value(). |
871 | * |
872 | * First the iterator is advanced to the next (possibly first) link. |
873 | * If that fails, then %FALSE is returned and there are no other effects. |
874 | * |
875 | * If successful, @out_link and @value are set to the name and #GMenuModel |
876 | * of the link that has just been advanced to. At this point, |
877 | * g_menu_link_iter_get_name() and g_menu_link_iter_get_value() will return the |
878 | * same values again. |
879 | * |
880 | * The value returned in @out_link remains valid for as long as the iterator |
881 | * remains at the current position. The value returned in @value must |
882 | * be unreffed using g_object_unref() when it is no longer in use. |
883 | * |
884 | * Returns: %TRUE on success, or %FALSE if there is no additional link |
885 | * |
886 | * Since: 2.32 |
887 | */ |
888 | gboolean |
889 | (GMenuLinkIter *iter, |
890 | const gchar **out_link, |
891 | GMenuModel **value) |
892 | { |
893 | const gchar *name; |
894 | |
895 | if (iter->priv->value) |
896 | { |
897 | g_object_unref (object: iter->priv->value); |
898 | iter->priv->value = NULL; |
899 | } |
900 | |
901 | iter->priv->valid = G_MENU_LINK_ITER_GET_CLASS (iter) |
902 | ->get_next (iter, &name, &iter->priv->value); |
903 | |
904 | if (iter->priv->valid) |
905 | { |
906 | g_assert (name != NULL); |
907 | |
908 | iter->priv->name = g_quark_from_string (string: name); |
909 | if (out_link) |
910 | *out_link = g_quark_to_string (quark: iter->priv->name); |
911 | |
912 | if (value) |
913 | *value = g_object_ref (iter->priv->value); |
914 | } |
915 | |
916 | return iter->priv->valid; |
917 | } |
918 | |
919 | /** |
920 | * g_menu_link_iter_next: |
921 | * @iter: a #GMenuLinkIter |
922 | * |
923 | * Attempts to advance the iterator to the next (possibly first) |
924 | * link. |
925 | * |
926 | * %TRUE is returned on success, or %FALSE if there are no more links. |
927 | * |
928 | * You must call this function when you first acquire the iterator to |
929 | * advance it to the first link (and determine if the first link exists |
930 | * at all). |
931 | * |
932 | * Returns: %TRUE on success, or %FALSE when there are no more links |
933 | * |
934 | * Since: 2.32 |
935 | */ |
936 | gboolean |
937 | (GMenuLinkIter *iter) |
938 | { |
939 | return g_menu_link_iter_get_next (iter, NULL, NULL); |
940 | } |
941 | |
942 | /** |
943 | * g_menu_link_iter_get_name: |
944 | * @iter: a #GMenuLinkIter |
945 | * |
946 | * Gets the name of the link at the current iterator position. |
947 | * |
948 | * The iterator is not advanced. |
949 | * |
950 | * Returns: the type of the link |
951 | * |
952 | * Since: 2.32 |
953 | */ |
954 | const gchar * |
955 | (GMenuLinkIter *iter) |
956 | { |
957 | g_return_val_if_fail (iter->priv->valid, 0); |
958 | |
959 | return g_quark_to_string (quark: iter->priv->name); |
960 | } |
961 | |
962 | /** |
963 | * g_menu_link_iter_get_value: |
964 | * @iter: a #GMenuLinkIter |
965 | * |
966 | * Gets the linked #GMenuModel at the current iterator position. |
967 | * |
968 | * The iterator is not advanced. |
969 | * |
970 | * Returns: (transfer full): the #GMenuModel that is linked to |
971 | * |
972 | * Since: 2.32 |
973 | */ |
974 | GMenuModel * |
975 | (GMenuLinkIter *iter) |
976 | { |
977 | g_return_val_if_fail (iter->priv->valid, NULL); |
978 | |
979 | return g_object_ref (iter->priv->value); |
980 | } |
981 | |
982 | static void |
983 | (GObject *object) |
984 | { |
985 | GMenuLinkIter *iter = G_MENU_LINK_ITER (object); |
986 | |
987 | if (iter->priv->value) |
988 | g_object_unref (object: iter->priv->value); |
989 | |
990 | G_OBJECT_CLASS (g_menu_link_iter_parent_class) |
991 | ->finalize (object); |
992 | } |
993 | |
994 | static void |
995 | (GMenuLinkIter *iter) |
996 | { |
997 | iter->priv = g_menu_link_iter_get_instance_private (self: iter); |
998 | } |
999 | |
1000 | static void |
1001 | (GMenuLinkIterClass *class) |
1002 | { |
1003 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
1004 | |
1005 | object_class->finalize = g_menu_link_iter_finalize; |
1006 | } |
1007 | |