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