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