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