Spelling fixes
[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  * @change_state: the callback to connect to the "change-state" signal
397  *                of the action
398  *
399  * This struct defines a single action.  It is for use with
400  * g_simple_action_group_add_entries().
401  *
402  * The order of the items in the structure are intended to reflect
403  * frequency of use.  It is permissible to use an incomplete initialiser
404  * in order to leave some of the later values as %NULL.  All values
405  * after @name are optional.  Additional optional fields may be added in
406  * the future.
407  *
408  * See g_simple_action_group_add_entries() for an example.
409  **/
410
411 /**
412  * g_simple_action_group_add_entries:
413  * @simple: a #GSimpleActionGroup
414  * @entries: a pointer to the first item in an array of #GActionEntry
415  *           structs
416  * @n_entries: the length of @entries, or -1
417  * @user_data: the user data for signal connections
418  *
419  * A convenience function for creating multiple #GSimpleAction instances
420  * and adding them to the action group.
421  *
422  * Each action is constructed as per one #GActionEntry.
423  *
424  * <example>
425  * <title>Using g_simple_action_group_add_entries()</title>
426  * <programlisting>
427  * static void
428  * activate_quit (GSimpleAction *simple,
429  *                GVariant      *parameter,
430  *                gpointer       user_data)
431  * {
432  *   exit (0);
433  * }
434  *
435  * static void
436  * activate_print_string (GSimpleAction *simple,
437  *                        GVariant      *parameter,
438  *                        gpointer       user_data)
439  * {
440  *   g_print ("%s\n", g_variant_get_string (parameter, NULL));
441  * }
442  *
443  * static GActionGroup *
444  * create_action_group (void)
445  * {
446  *   const GActionEntry entries[] = {
447  *     { "quit",         activate_quit              },
448  *     { "print-string", activate_print_string, "s" }
449  *   };
450  *   GSimpleActionGroup *group;
451  *
452  *   group = g_simple_action_group_new ();
453  *   g_simple_action_group_add_entries (group, entries, G_N_ELEMENTS (entries), NULL);
454  *
455  *   return G_ACTION_GROUP (group);
456  * }
457  * </programlisting>
458  * </example>
459  *
460  * Since: 2.30
461  **/
462 void
463 g_simple_action_group_add_entries (GSimpleActionGroup *simple,
464                                    const GActionEntry *entries,
465                                    gint                n_entries,
466                                    gpointer            user_data)
467 {
468   gint i;
469
470   g_return_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple));
471   g_return_if_fail (entries != NULL || n_entries == 0);
472
473   for (i = 0; n_entries == -1 ? entries[i].name != NULL : i < n_entries; i++)
474     {
475       const GActionEntry *entry = &entries[i];
476       const GVariantType *parameter_type;
477       GSimpleAction *action;
478
479       if (entry->parameter_type)
480         {
481           if (!g_variant_type_string_is_valid (entry->parameter_type))
482             {
483               g_critical ("g_simple_action_group_add_entries: the type "
484                           "string '%s' given as the parameter type for "
485                           "action '%s' is not a valid GVariant type "
486                           "string.  This action will not be added.",
487                           entry->parameter_type, entry->name);
488               return;
489             }
490
491           parameter_type = G_VARIANT_TYPE (entry->parameter_type);
492         }
493       else
494         parameter_type = NULL;
495
496       if (entry->state)
497         {
498           GError *error = NULL;
499           GVariant *state;
500
501           state = g_variant_parse (NULL, entry->state, NULL, NULL, &error);
502           if (state == NULL)
503             {
504               g_critical ("g_simple_action_group_add_entries: GVariant could "
505                           "not parse the state value given for action '%s' "
506                           "('%s'): %s.  This action will not be added.",
507                           entry->name, entry->state, error->message);
508               g_error_free (error);
509               continue;
510             }
511
512           action = g_simple_action_new_stateful (entry->name,
513                                                  parameter_type,
514                                                  state);
515
516           g_variant_unref (state);
517         }
518       else
519         {
520           action = g_simple_action_new (entry->name,
521                                         parameter_type);
522         }
523
524       if (entry->activate != NULL)
525         g_signal_connect (action, "activate",
526                           G_CALLBACK (entry->activate), user_data);
527
528       if (entry->change_state != NULL)
529         g_signal_connect (action, "change-state",
530                           G_CALLBACK (entry->change_state), user_data);
531
532       g_simple_action_group_insert (simple, G_ACTION (action));
533       g_object_unref (action);
534     }
535 }