Add g_simple_action_group_add_entries()
[platform/upstream/glib.git] / gio / gsimpleactiongroup.c
1 /*
2  * Copyright © 2010 Codethink Limited
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published
6  * by the Free Software Foundation; either version 2 of the licence or (at
7  * 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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Authors: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "gsimpleactiongroup.h"
23
24 #include "gsimpleaction.h"
25 #include "gaction.h"
26
27 /**
28  * SECTION:gsimpleactiongroup
29  * @title: GSimpleActionGroup
30  * @short_description: A simple GActionGroup implementation
31  *
32  * #GSimpleActionGroup is a hash table filled with #GAction objects,
33  * implementing the #GActionGroup interface.
34  **/
35
36 struct _GSimpleActionGroupPrivate
37 {
38   GHashTable *table;  /* string -> GAction */
39 };
40
41 static void g_simple_action_group_iface_init (GActionGroupInterface *);
42 G_DEFINE_TYPE_WITH_CODE (GSimpleActionGroup,
43   g_simple_action_group, G_TYPE_OBJECT,
44   G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
45     g_simple_action_group_iface_init))
46
47 static gchar **
48 g_simple_action_group_list_actions (GActionGroup *group)
49 {
50   GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group);
51   GHashTableIter iter;
52   gint n, i = 0;
53   gchar **keys;
54   gpointer key;
55
56   n = g_hash_table_size (simple->priv->table);
57   keys = g_new (gchar *, n + 1);
58
59   g_hash_table_iter_init (&iter, simple->priv->table);
60   while (g_hash_table_iter_next (&iter, &key, NULL))
61     keys[i++] = g_strdup (key);
62   g_assert_cmpint (i, ==, n);
63   keys[n] = NULL;
64
65   return keys;
66 }
67
68 static gboolean
69 g_simple_action_group_has_action (GActionGroup *group,
70                                   const gchar  *action_name)
71 {
72   GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group);
73
74   return g_hash_table_lookup (simple->priv->table, action_name) != NULL;
75 }
76
77 static const GVariantType *
78 g_simple_action_group_get_parameter_type (GActionGroup *group,
79                                           const gchar  *action_name)
80 {
81   GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group);
82   GAction *action;
83
84   action = g_hash_table_lookup (simple->priv->table, action_name);
85
86   if (action == NULL)
87     return NULL;
88
89   return g_action_get_parameter_type (action);
90 }
91
92 static const GVariantType *
93 g_simple_action_group_get_state_type (GActionGroup *group,
94                                       const gchar  *action_name)
95 {
96   GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group);
97   GAction *action;
98
99   action = g_hash_table_lookup (simple->priv->table, action_name);
100
101   if (action == NULL)
102     return NULL;
103
104   return g_action_get_state_type (action);
105 }
106
107 static GVariant *
108 g_simple_action_group_get_state_hint (GActionGroup *group,
109                                        const gchar  *action_name)
110 {
111   GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group);
112   GAction *action;
113
114   action = g_hash_table_lookup (simple->priv->table, action_name);
115
116   if (action == NULL)
117     return NULL;
118
119   return g_action_get_state_hint (action);
120 }
121
122 static gboolean
123 g_simple_action_group_get_enabled (GActionGroup *group,
124                                    const gchar  *action_name)
125 {
126   GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group);
127   GAction *action;
128
129   action = g_hash_table_lookup (simple->priv->table, action_name);
130
131   if (action == NULL)
132     return FALSE;
133
134   return g_action_get_enabled (action);
135 }
136
137 static GVariant *
138 g_simple_action_group_get_state (GActionGroup *group,
139                                  const gchar  *action_name)
140 {
141   GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group);
142   GAction *action;
143
144   action = g_hash_table_lookup (simple->priv->table, action_name);
145
146   if (action == NULL)
147     return NULL;
148
149   return g_action_get_state (action);
150 }
151
152 static void
153 g_simple_action_group_change_state (GActionGroup *group,
154                                     const gchar  *action_name,
155                                     GVariant     *value)
156 {
157   GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group);
158   GAction *action;
159
160   action = g_hash_table_lookup (simple->priv->table, action_name);
161
162   if (action == NULL)
163     return;
164
165   g_action_change_state (action, value);
166 }
167
168 static void
169 g_simple_action_group_activate (GActionGroup *group,
170                                 const gchar  *action_name,
171                                 GVariant     *parameter)
172 {
173   GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (group);
174   GAction *action;
175
176   action = g_hash_table_lookup (simple->priv->table, action_name);
177
178   if (action == NULL)
179     return;
180
181   g_action_activate (action, parameter);
182 }
183
184 static void
185 action_enabled_notify (GAction     *action,
186                        GParamSpec  *pspec,
187                        gpointer     user_data)
188 {
189   g_action_group_action_enabled_changed (user_data,
190                                          g_action_get_name (action),
191                                          g_action_get_enabled (action));
192 }
193
194 static void
195 action_state_notify (GAction    *action,
196                      GParamSpec *pspec,
197                      gpointer    user_data)
198 {
199   GVariant *value;
200
201   value = g_action_get_state (action);
202   g_action_group_action_state_changed (user_data,
203                                        g_action_get_name (action),
204                                        value);
205   g_variant_unref (value);
206 }
207
208 static void
209 g_simple_action_group_disconnect (gpointer key,
210                                   gpointer value,
211                                   gpointer user_data)
212 {
213   g_signal_handlers_disconnect_by_func (value, action_enabled_notify,
214                                         user_data);
215   g_signal_handlers_disconnect_by_func (value, action_state_notify,
216                                         user_data);
217 }
218
219 static void
220 g_simple_action_group_finalize (GObject *object)
221 {
222   GSimpleActionGroup *simple = G_SIMPLE_ACTION_GROUP (object);
223
224   g_hash_table_foreach (simple->priv->table,
225                         g_simple_action_group_disconnect,
226                         simple);
227   g_hash_table_unref (simple->priv->table);
228
229   G_OBJECT_CLASS (g_simple_action_group_parent_class)
230     ->finalize (object);
231 }
232
233 static void
234 g_simple_action_group_init (GSimpleActionGroup *simple)
235 {
236   simple->priv = G_TYPE_INSTANCE_GET_PRIVATE (simple,
237                                               G_TYPE_SIMPLE_ACTION_GROUP,
238                                               GSimpleActionGroupPrivate);
239   simple->priv->table = g_hash_table_new_full (g_str_hash, g_str_equal,
240                                                g_free, g_object_unref);
241 }
242
243 static void
244 g_simple_action_group_class_init (GSimpleActionGroupClass *class)
245 {
246   GObjectClass *object_class = G_OBJECT_CLASS (class);
247
248   object_class->finalize = g_simple_action_group_finalize;
249
250   g_type_class_add_private (class, sizeof (GSimpleActionGroupPrivate));
251 }
252
253 static void
254 g_simple_action_group_iface_init (GActionGroupInterface *iface)
255 {
256   iface->list_actions = g_simple_action_group_list_actions;
257   iface->has_action = g_simple_action_group_has_action;
258   iface->get_action_parameter_type = g_simple_action_group_get_parameter_type;
259   iface->get_action_state_type = g_simple_action_group_get_state_type;
260   iface->get_action_state_hint = g_simple_action_group_get_state_hint;
261   iface->get_action_enabled = g_simple_action_group_get_enabled;
262   iface->get_action_state = g_simple_action_group_get_state;
263   iface->change_action_state = g_simple_action_group_change_state;
264   iface->activate_action = g_simple_action_group_activate;
265 }
266
267 /**
268  * g_simple_action_group_new:
269  *
270  * Creates a new, empty, #GSimpleActionGroup.
271  *
272  * Returns: a new #GSimpleActionGroup
273  *
274  * Since: 2.28
275  **/
276 GSimpleActionGroup *
277 g_simple_action_group_new (void)
278 {
279   return g_object_new (G_TYPE_SIMPLE_ACTION_GROUP, NULL);
280 }
281
282 /**
283  * g_simple_action_group_lookup:
284  * @simple: a #GSimpleActionGroup
285  * @action_name: the name of an action
286  *
287  * Looks up the action with the name @action_name in the group.
288  *
289  * If no such action exists, returns %NULL.
290  *
291  * Returns: (transfer none): a #GAction, or %NULL
292  *
293  * Since: 2.28
294  **/
295 GAction *
296 g_simple_action_group_lookup (GSimpleActionGroup *simple,
297                               const gchar        *action_name)
298 {
299   g_return_val_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple), NULL);
300
301   return g_hash_table_lookup (simple->priv->table, action_name);
302 }
303
304 /**
305  * g_simple_action_group_insert:
306  * @simple: a #GSimpleActionGroup
307  * @action: a #GAction
308  *
309  * Adds an action to the action group.
310  *
311  * If the action group already contains an action with the same name as
312  * @action then the old action is dropped from the group.
313  *
314  * The action group takes its own reference on @action.
315  *
316  * Since: 2.28
317  **/
318 void
319 g_simple_action_group_insert (GSimpleActionGroup *simple,
320                               GAction            *action)
321 {
322   const gchar *action_name;
323   GAction *old_action;
324
325   g_return_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple));
326   g_return_if_fail (G_IS_ACTION (action));
327
328   action_name = g_action_get_name (action);
329   old_action = g_hash_table_lookup (simple->priv->table, action_name);
330
331   if (old_action != action)
332     {
333       if (old_action != NULL)
334         {
335           g_action_group_action_removed (G_ACTION_GROUP (simple),
336                                          action_name);
337           g_simple_action_group_disconnect (NULL, old_action, simple);
338         }
339
340       g_signal_connect (action, "notify::enabled",
341                         G_CALLBACK (action_enabled_notify), simple);
342
343       if (g_action_get_state_type (action) != NULL)
344         g_signal_connect (action, "notify::state",
345                           G_CALLBACK (action_state_notify), simple);
346
347       g_hash_table_insert (simple->priv->table,
348                            g_strdup (action_name),
349                            g_object_ref (action));
350
351       g_action_group_action_added (G_ACTION_GROUP (simple), action_name);
352     }
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 void
367 g_simple_action_group_remove (GSimpleActionGroup *simple,
368                               const gchar        *action_name)
369 {
370   GAction *action;
371
372   g_return_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple));
373
374   action = g_hash_table_lookup (simple->priv->table, action_name);
375
376   if (action != NULL)
377     {
378       g_action_group_action_removed (G_ACTION_GROUP (simple), action_name);
379       g_simple_action_group_disconnect (NULL, action, simple);
380       g_hash_table_remove (simple->priv->table, action_name);
381     }
382 }
383
384 /**
385  * GActionEntry:
386  * @name: the name of the action
387  * @activate: the callback to connect to the "activate" signal of the
388  *            action
389  * @parameter_type: the type of the parameter that must be passed to the
390  *                  activate function for this action, given as a single
391  *                  GVariant type string (or %NULL for no parameter)
392  * @state: the initial state for this action, given in GVariant text
393  *         format.  The state is parsed with no extra type information,
394  *         so type tags must be added to the string if they are
395  *         necessary.
396  *
397  * This struct defines a single action.  It is for use with
398  * g_simple_action_group_add_entries().
399  *
400  * The order of the items in the structure are intended to reflect
401  * frequency of use.  It is permissible to use an incomplete initialiser
402  * in order to leave some of the later values as %NULL.  All values
403  * after @name are optional.  Additional optional fields may be added in
404  * the future.
405  **/
406
407 /**
408  * g_simple_action_group_add_entries:
409  * @simple: a #GSimpleActionGroup
410  * @entries: a pointer to the first item in an array of #GActionEntry
411  *           structs
412  * @n_entries: the length of @entries, or -1
413  * @user_data: the user data for signal connections
414  *
415  * A convenience function for creating multiple #GSimpleAction instances
416  * and adding them to the action group.
417  *
418  * Each action is constructed as per one #GActionEntry.
419  *
420  * Since: 2.30
421  **/
422 void
423 g_simple_action_group_add_entries (GSimpleActionGroup *simple,
424                                    const GActionEntry *entries,
425                                    gint                n_entries,
426                                    gpointer            user_data)
427 {
428   gint i;
429
430   g_return_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple));
431   g_return_if_fail (entries != NULL || n_entries == 0);
432
433   for (i = 0; n_entries == -1 ? entries[i].name != NULL : i < n_entries; i++)
434     {
435       const GActionEntry *entry = &entries[i];
436       const GVariantType *parameter_type;
437       GSimpleAction *action;
438
439       if (entry->parameter_type)
440         {
441           if (!g_variant_type_string_is_valid (entry->parameter_type))
442             {
443               g_critical ("g_simple_action_group_add_entries: the type "
444                           "string '%s' given as the parameter type for "
445                           "action '%s' is not a valid GVariant type "
446                           "string.  This action will not be added.",
447                           entry->parameter_type, entry->name);
448               return;
449             }
450
451           parameter_type = G_VARIANT_TYPE (entry->parameter_type);
452         }
453       else
454         parameter_type = NULL;
455
456       if (entry->state)
457         {
458           GError *error = NULL;
459           GVariant *state;
460
461           state = g_variant_parse (NULL, entry->state, NULL, NULL, &error);
462           if (state == NULL)
463             {
464               g_critical ("g_simple_action_group_add_entries: GVariant could "
465                           "not parse the state value given for action '%s' "
466                           "('%s'): %s.  This action will not be added.",
467                           entry->name, entry->state, error->message);
468               g_error_free (error);
469               continue;
470             }
471
472           action = g_simple_action_new_stateful (entry->name,
473                                                  parameter_type,
474                                                  state);
475
476           g_variant_unref (state);
477         }
478       else
479         {
480           action = g_simple_action_new (entry->name,
481                                         parameter_type);
482         }
483
484       if (entry->activate != NULL)
485         g_signal_connect (action, "activate",
486                           G_CALLBACK (entry->activate), user_data);
487
488       g_simple_action_group_insert (simple, G_ACTION (action));
489       g_object_unref (action);
490     }
491 }