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