/src/glib/gio/gsimpleactiongroup.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright © 2010 Codethink Limited |
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, |
12 | | * but 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 |
17 | | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | * Authors: Ryan Lortie <desrt@desrt.ca> |
20 | | */ |
21 | | |
22 | | #include "config.h" |
23 | | |
24 | | #include "gsimpleactiongroup.h" |
25 | | |
26 | | #include "gsimpleaction.h" |
27 | | #include "gactionmap.h" |
28 | | #include "gaction.h" |
29 | | |
30 | | /** |
31 | | * SECTION:gsimpleactiongroup |
32 | | * @title: GSimpleActionGroup |
33 | | * @short_description: A simple GActionGroup implementation |
34 | | * @include: gio/gio.h |
35 | | * |
36 | | * #GSimpleActionGroup is a hash table filled with #GAction objects, |
37 | | * implementing the #GActionGroup and #GActionMap interfaces. |
38 | | **/ |
39 | | |
40 | | struct _GSimpleActionGroupPrivate |
41 | | { |
42 | | GHashTable *table; /* string -> GAction */ |
43 | | }; |
44 | | |
45 | | static void g_simple_action_group_iface_init (GActionGroupInterface *); |
46 | | static void g_simple_action_group_map_iface_init (GActionMapInterface *); |
47 | | G_DEFINE_TYPE_WITH_CODE (GSimpleActionGroup, |
48 | | g_simple_action_group, G_TYPE_OBJECT, |
49 | | G_ADD_PRIVATE (GSimpleActionGroup) |
50 | | G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, |
51 | | g_simple_action_group_iface_init); |
52 | | G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_MAP, |
53 | | g_simple_action_group_map_iface_init)) |
54 | | |
55 | | static gchar ** |
56 | | g_simple_action_group_list_actions (GActionGroup *group) |
57 | 0 | { |
58 | 0 | GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group); |
59 | 0 | GHashTableIter iter; |
60 | 0 | gint n, i = 0; |
61 | 0 | gchar **keys; |
62 | 0 | gpointer key; |
63 | |
|
64 | 0 | n = g_hash_table_size (simple->priv->table); |
65 | 0 | keys = g_new (gchar *, n + 1); |
66 | |
|
67 | 0 | g_hash_table_iter_init (&iter, simple->priv->table); |
68 | 0 | while (g_hash_table_iter_next (&iter, &key, NULL)) |
69 | 0 | keys[i++] = g_strdup (key); |
70 | 0 | g_assert_cmpint (i, ==, n); |
71 | 0 | keys[n] = NULL; |
72 | |
|
73 | 0 | return keys; |
74 | 0 | } |
75 | | |
76 | | static gboolean |
77 | | g_simple_action_group_query_action (GActionGroup *group, |
78 | | const gchar *action_name, |
79 | | gboolean *enabled, |
80 | | const GVariantType **parameter_type, |
81 | | const GVariantType **state_type, |
82 | | GVariant **state_hint, |
83 | | GVariant **state) |
84 | 0 | { |
85 | 0 | GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group); |
86 | 0 | GAction *action; |
87 | |
|
88 | 0 | action = g_hash_table_lookup (simple->priv->table, action_name); |
89 | |
|
90 | 0 | if (action == NULL) |
91 | 0 | return FALSE; |
92 | | |
93 | 0 | if (enabled) |
94 | 0 | *enabled = g_action_get_enabled (action); |
95 | |
|
96 | 0 | if (parameter_type) |
97 | 0 | *parameter_type = g_action_get_parameter_type (action); |
98 | |
|
99 | 0 | if (state_type) |
100 | 0 | *state_type = g_action_get_state_type (action); |
101 | |
|
102 | 0 | if (state_hint) |
103 | 0 | *state_hint = g_action_get_state_hint (action); |
104 | |
|
105 | 0 | if (state) |
106 | 0 | *state = g_action_get_state (action); |
107 | |
|
108 | 0 | return TRUE; |
109 | 0 | } |
110 | | |
111 | | static void |
112 | | g_simple_action_group_change_state (GActionGroup *group, |
113 | | const gchar *action_name, |
114 | | GVariant *value) |
115 | 0 | { |
116 | 0 | GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group); |
117 | 0 | GAction *action; |
118 | |
|
119 | 0 | action = g_hash_table_lookup (simple->priv->table, action_name); |
120 | |
|
121 | 0 | if (action == NULL) |
122 | 0 | return; |
123 | | |
124 | 0 | g_action_change_state (action, value); |
125 | 0 | } |
126 | | |
127 | | static void |
128 | | g_simple_action_group_activate (GActionGroup *group, |
129 | | const gchar *action_name, |
130 | | GVariant *parameter) |
131 | 0 | { |
132 | 0 | GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group); |
133 | 0 | GAction *action; |
134 | |
|
135 | 0 | action = g_hash_table_lookup (simple->priv->table, action_name); |
136 | |
|
137 | 0 | if (action == NULL) |
138 | 0 | return; |
139 | | |
140 | 0 | g_action_activate (action, parameter); |
141 | 0 | } |
142 | | |
143 | | static void |
144 | | action_enabled_notify (GAction *action, |
145 | | GParamSpec *pspec, |
146 | | gpointer user_data) |
147 | 0 | { |
148 | 0 | g_action_group_action_enabled_changed (user_data, |
149 | 0 | g_action_get_name (action), |
150 | 0 | g_action_get_enabled (action)); |
151 | 0 | } |
152 | | |
153 | | static void |
154 | | action_state_notify (GAction *action, |
155 | | GParamSpec *pspec, |
156 | | gpointer user_data) |
157 | 0 | { |
158 | 0 | GVariant *value; |
159 | |
|
160 | 0 | value = g_action_get_state (action); |
161 | 0 | g_action_group_action_state_changed (user_data, |
162 | 0 | g_action_get_name (action), |
163 | 0 | value); |
164 | 0 | g_variant_unref (value); |
165 | 0 | } |
166 | | |
167 | | static void |
168 | | g_simple_action_group_disconnect (gpointer key, |
169 | | gpointer value, |
170 | | gpointer user_data) |
171 | 0 | { |
172 | 0 | g_signal_handlers_disconnect_by_func (value, action_enabled_notify, |
173 | 0 | user_data); |
174 | 0 | g_signal_handlers_disconnect_by_func (value, action_state_notify, |
175 | 0 | user_data); |
176 | 0 | } |
177 | | |
178 | | static GAction * |
179 | | g_simple_action_group_lookup_action (GActionMap *action_map, |
180 | | const gchar *action_name) |
181 | 0 | { |
182 | 0 | GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (action_map); |
183 | |
|
184 | 0 | return g_hash_table_lookup (simple->priv->table, action_name); |
185 | 0 | } |
186 | | |
187 | | static void |
188 | | g_simple_action_group_add_action (GActionMap *action_map, |
189 | | GAction *action) |
190 | 0 | { |
191 | 0 | GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (action_map); |
192 | 0 | const gchar *action_name; |
193 | 0 | GAction *old_action; |
194 | |
|
195 | 0 | action_name = g_action_get_name (action); |
196 | 0 | if (action_name == NULL) |
197 | 0 | { |
198 | 0 | g_critical ("The supplied action has no name. You must set the " |
199 | 0 | "GAction:name property when creating an action."); |
200 | 0 | return; |
201 | 0 | } |
202 | | |
203 | 0 | old_action = g_hash_table_lookup (simple->priv->table, action_name); |
204 | |
|
205 | 0 | if (old_action != action) |
206 | 0 | { |
207 | 0 | if (old_action != NULL) |
208 | 0 | { |
209 | 0 | g_action_group_action_removed (G_ACTION_GROUP (simple), |
210 | 0 | action_name); |
211 | 0 | g_simple_action_group_disconnect (NULL, old_action, simple); |
212 | 0 | } |
213 | |
|
214 | 0 | g_signal_connect (action, "notify::enabled", |
215 | 0 | G_CALLBACK (action_enabled_notify), simple); |
216 | |
|
217 | 0 | if (g_action_get_state_type (action) != NULL) |
218 | 0 | g_signal_connect (action, "notify::state", |
219 | 0 | G_CALLBACK (action_state_notify), simple); |
220 | |
|
221 | 0 | g_hash_table_insert (simple->priv->table, |
222 | 0 | g_strdup (action_name), |
223 | 0 | g_object_ref (action)); |
224 | |
|
225 | 0 | g_action_group_action_added (G_ACTION_GROUP (simple), action_name); |
226 | 0 | } |
227 | 0 | } |
228 | | |
229 | | static void |
230 | | g_simple_action_group_remove_action (GActionMap *action_map, |
231 | | const gchar *action_name) |
232 | 0 | { |
233 | 0 | GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (action_map); |
234 | 0 | GAction *action; |
235 | |
|
236 | 0 | action = g_hash_table_lookup (simple->priv->table, action_name); |
237 | |
|
238 | 0 | if (action != NULL) |
239 | 0 | { |
240 | 0 | g_action_group_action_removed (G_ACTION_GROUP (simple), action_name); |
241 | 0 | g_simple_action_group_disconnect (NULL, action, simple); |
242 | 0 | g_hash_table_remove (simple->priv->table, action_name); |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | static void |
247 | | g_simple_action_group_finalize (GObject *object) |
248 | 0 | { |
249 | 0 | GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (object); |
250 | |
|
251 | 0 | g_hash_table_foreach (simple->priv->table, |
252 | 0 | g_simple_action_group_disconnect, |
253 | 0 | simple); |
254 | 0 | g_hash_table_unref (simple->priv->table); |
255 | |
|
256 | 0 | G_OBJECT_CLASS (g_simple_action_group_parent_class) |
257 | 0 | ->finalize (object); |
258 | 0 | } |
259 | | |
260 | | static void |
261 | | g_simple_action_group_init (GSimpleActionGroup *simple) |
262 | 0 | { |
263 | 0 | simple->priv = g_simple_action_group_get_instance_private (simple); |
264 | 0 | simple->priv->table = g_hash_table_new_full (g_str_hash, g_str_equal, |
265 | 0 | g_free, g_object_unref); |
266 | 0 | } |
267 | | |
268 | | static void |
269 | | g_simple_action_group_class_init (GSimpleActionGroupClass *class) |
270 | 0 | { |
271 | 0 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
272 | |
|
273 | 0 | object_class->finalize = g_simple_action_group_finalize; |
274 | 0 | } |
275 | | |
276 | | static void |
277 | | g_simple_action_group_iface_init (GActionGroupInterface *iface) |
278 | 0 | { |
279 | 0 | iface->list_actions = g_simple_action_group_list_actions; |
280 | 0 | iface->query_action = g_simple_action_group_query_action; |
281 | 0 | iface->change_action_state = g_simple_action_group_change_state; |
282 | 0 | iface->activate_action = g_simple_action_group_activate; |
283 | 0 | } |
284 | | |
285 | | static void |
286 | | g_simple_action_group_map_iface_init (GActionMapInterface *iface) |
287 | 0 | { |
288 | 0 | iface->add_action = g_simple_action_group_add_action; |
289 | 0 | iface->remove_action = g_simple_action_group_remove_action; |
290 | 0 | iface->lookup_action = g_simple_action_group_lookup_action; |
291 | 0 | } |
292 | | |
293 | | /** |
294 | | * g_simple_action_group_new: |
295 | | * |
296 | | * Creates a new, empty, #GSimpleActionGroup. |
297 | | * |
298 | | * Returns: a new #GSimpleActionGroup |
299 | | * |
300 | | * Since: 2.28 |
301 | | **/ |
302 | | GSimpleActionGroup * |
303 | | g_simple_action_group_new (void) |
304 | 0 | { |
305 | 0 | return g_object_new (G_TYPE_SIMPLE_ACTION_GROUP, NULL); |
306 | 0 | } |
307 | | |
308 | | /** |
309 | | * g_simple_action_group_lookup: |
310 | | * @simple: a #GSimpleActionGroup |
311 | | * @action_name: the name of an action |
312 | | * |
313 | | * Looks up the action with the name @action_name in the group. |
314 | | * |
315 | | * If no such action exists, returns %NULL. |
316 | | * |
317 | | * Returns: (transfer none): a #GAction, or %NULL |
318 | | * |
319 | | * Since: 2.28 |
320 | | * |
321 | | * Deprecated: 2.38: Use g_action_map_lookup_action() |
322 | | */ |
323 | | GAction * |
324 | | g_simple_action_group_lookup (GSimpleActionGroup *simple, |
325 | | const gchar *action_name) |
326 | 0 | { |
327 | 0 | g_return_val_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple), NULL); |
328 | | |
329 | 0 | return g_action_map_lookup_action (G_ACTION_MAP (simple), action_name); |
330 | 0 | } |
331 | | |
332 | | /** |
333 | | * g_simple_action_group_insert: |
334 | | * @simple: a #GSimpleActionGroup |
335 | | * @action: a #GAction |
336 | | * |
337 | | * Adds an action to the action group. |
338 | | * |
339 | | * If the action group already contains an action with the same name as |
340 | | * @action then the old action is dropped from the group. |
341 | | * |
342 | | * The action group takes its own reference on @action. |
343 | | * |
344 | | * Since: 2.28 |
345 | | * |
346 | | * Deprecated: 2.38: Use g_action_map_add_action() |
347 | | **/ |
348 | | void |
349 | | g_simple_action_group_insert (GSimpleActionGroup *simple, |
350 | | GAction *action) |
351 | 0 | { |
352 | 0 | g_return_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple)); |
353 | | |
354 | 0 | g_action_map_add_action (G_ACTION_MAP (simple), action); |
355 | 0 | } |
356 | | |
357 | | /** |
358 | | * g_simple_action_group_remove: |
359 | | * @simple: a #GSimpleActionGroup |
360 | | * @action_name: the name of the action |
361 | | * |
362 | | * Removes the named action from the action group. |
363 | | * |
364 | | * If no action of this name is in the group then nothing happens. |
365 | | * |
366 | | * Since: 2.28 |
367 | | * |
368 | | * Deprecated: 2.38: Use g_action_map_remove_action() |
369 | | **/ |
370 | | void |
371 | | g_simple_action_group_remove (GSimpleActionGroup *simple, |
372 | | const gchar *action_name) |
373 | 0 | { |
374 | 0 | g_return_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple)); |
375 | | |
376 | 0 | g_action_map_remove_action (G_ACTION_MAP (simple), action_name); |
377 | 0 | } |
378 | | |
379 | | |
380 | | /** |
381 | | * g_simple_action_group_add_entries: |
382 | | * @simple: a #GSimpleActionGroup |
383 | | * @entries: (array length=n_entries): a pointer to the first item in |
384 | | * an array of #GActionEntry structs |
385 | | * @n_entries: the length of @entries, or -1 |
386 | | * @user_data: the user data for signal connections |
387 | | * |
388 | | * A convenience function for creating multiple #GSimpleAction instances |
389 | | * and adding them to the action group. |
390 | | * |
391 | | * Since: 2.30 |
392 | | * |
393 | | * Deprecated: 2.38: Use g_action_map_add_action_entries() |
394 | | **/ |
395 | | void |
396 | | g_simple_action_group_add_entries (GSimpleActionGroup *simple, |
397 | | const GActionEntry *entries, |
398 | | gint n_entries, |
399 | | gpointer user_data) |
400 | 0 | { |
401 | 0 | g_action_map_add_action_entries (G_ACTION_MAP (simple), entries, n_entries, user_data); |
402 | 0 | } |