2
* Copyright 2012 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3, as published
6
* by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranties of
10
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
* PURPOSE. See the GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License along
14
* with this program. If not, see <http://www.gnu.org/licenses/>.
17
* Lars Uebernickel <lars.uebernickel@canonical.com>
18
* Ryan Lortie <desrt@desrt.ca>
21
#include <libhud-client/action-muxer.h>
26
* SECTION:gactionmuxer
27
* @short_description: Aggregate several action groups
29
* #GActionMuxer is a #GActionGroup that is capable of containing other
30
* #GActionGroup instances.
32
* The typical use is aggregating all of the actions applicable to a
33
* particular context into a single action group, with namespacing.
35
* Consider the case of two action groups -- one containing actions
36
* applicable to an entire application (such as 'quit') and one
37
* containing actions applicable to a particular window in the
38
* application (such as 'fullscreen').
40
* In this case, each of these action groups could be added to a
41
* #GActionMuxer with the prefixes "app" and "win", respectively. This
42
* would expose the actions as "app.quit" and "win.fullscreen" on the
43
* #GActionGroup interface presented by the #GActionMuxer.
45
* Activations and state change requests on the #GActionMuxer are wired
46
* through to the underlying action group in the expected way.
49
typedef GObjectClass GActionMuxerClass;
54
GActionGroup *global_actions;
55
GHashTable *groups; /* prefix -> subgroup */
56
GHashTable *reverse; /* subgroup -> prefix */
60
static void g_action_muxer_group_init (GActionGroupInterface *iface);
61
static void g_action_muxer_dispose (GObject *object);
62
static void g_action_muxer_finalize (GObject *object);
63
static void g_action_muxer_disconnect_group (GActionMuxer *muxer,
64
GActionGroup *subgroup);
65
static gchar ** g_action_muxer_list_actions (GActionGroup *group);
66
static void g_action_muxer_activate_action (GActionGroup *group,
67
const gchar *action_name,
69
static void g_action_muxer_change_action_state (GActionGroup *group,
70
const gchar *action_name,
72
static gboolean g_action_muxer_query_action (GActionGroup *group,
73
const gchar *action_name,
75
const GVariantType **parameter_type,
76
const GVariantType **state_type,
77
GVariant **state_hint,
79
static void g_action_muxer_action_added (GActionGroup *group,
82
static void g_action_muxer_action_removed (GActionGroup *group,
85
static void g_action_muxer_action_state_changed (GActionGroup *group,
89
static void g_action_muxer_action_enabled_changed (GActionGroup *group,
94
G_DEFINE_TYPE_WITH_CODE (GActionMuxer, g_action_muxer, G_TYPE_OBJECT,
95
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_action_muxer_group_init))
99
g_action_muxer_class_init (GObjectClass *klass)
101
klass->dispose = g_action_muxer_dispose;
102
klass->finalize = g_action_muxer_finalize;
106
g_action_muxer_init (GActionMuxer *muxer)
108
muxer->global_actions = NULL;
109
muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
110
muxer->reverse = g_hash_table_new (g_direct_hash, g_direct_equal);
114
g_action_muxer_group_init (GActionGroupInterface *iface)
116
iface->list_actions = g_action_muxer_list_actions;
117
iface->activate_action = g_action_muxer_activate_action;
118
iface->change_action_state = g_action_muxer_change_action_state;
119
iface->query_action = g_action_muxer_query_action;
123
g_action_muxer_dispose (GObject *object)
125
GActionMuxer *muxer = G_ACTION_MUXER (object);
127
GActionGroup *subgroup;
129
if (muxer->global_actions)
131
g_action_muxer_disconnect_group (muxer, muxer->global_actions);
132
g_clear_object (&muxer->global_actions);
135
g_hash_table_iter_init (&it, muxer->groups);
136
while (g_hash_table_iter_next (&it, NULL, (gpointer *) &subgroup))
137
g_action_muxer_disconnect_group (muxer, subgroup);
139
g_hash_table_remove_all (muxer->groups);
140
g_hash_table_remove_all (muxer->reverse);
144
g_action_muxer_finalize (GObject *object)
146
GActionMuxer *muxer = G_ACTION_MUXER (object);
148
g_hash_table_unref (muxer->groups);
149
g_hash_table_unref (muxer->reverse);
151
G_OBJECT_CLASS (g_action_muxer_parent_class)->finalize (object);
154
static GActionGroup *
155
g_action_muxer_lookup_group (GActionMuxer *muxer,
156
const gchar *full_name,
157
const gchar **action_name)
162
sep = strchr (full_name, '.');
167
prefix = g_strndup (full_name, sep - full_name);
168
group = g_hash_table_lookup (muxer->groups, prefix);
171
*action_name = sep + 1;
175
group = muxer->global_actions;
177
*action_name = full_name;
184
g_action_muxer_lookup_full_name (GActionMuxer *muxer,
185
GActionGroup *subgroup,
186
const gchar *action_name)
190
if (subgroup == muxer->global_actions)
191
return g_strdup (action_name);
193
if (g_hash_table_lookup_extended (muxer->reverse, subgroup, NULL, &prefix))
194
return g_strdup_printf ("%s.%s", (gchar *) prefix, action_name);
200
g_action_muxer_disconnect_group (GActionMuxer *muxer,
201
GActionGroup *subgroup)
206
actions = g_action_group_list_actions (subgroup);
207
for (action = actions; *action; action++)
208
g_action_muxer_action_removed (subgroup, *action, muxer);
209
g_strfreev (actions);
211
#pragma GCC diagnostic ignored "-Wpedantic"
212
g_signal_handlers_disconnect_by_func (subgroup, (gpointer) g_action_muxer_action_added, muxer);
213
g_signal_handlers_disconnect_by_func (subgroup, (gpointer) g_action_muxer_action_removed, muxer);
214
g_signal_handlers_disconnect_by_func (subgroup, (gpointer) g_action_muxer_action_enabled_changed, muxer);
215
g_signal_handlers_disconnect_by_func (subgroup, (gpointer) g_action_muxer_action_state_changed, muxer);
216
#pragma GCC diagnostic pop
220
g_action_muxer_list_actions (GActionGroup *group)
222
GActionMuxer *muxer = G_ACTION_MUXER (group);
226
GActionGroup *subgroup;
230
all_actions = g_array_sized_new (TRUE, FALSE, sizeof (gchar *), 8);
232
if (muxer->global_actions)
234
actions = g_action_group_list_actions (muxer->global_actions);
235
for (a = actions; *a; a++)
237
gchar *name = g_strdup (*a);
238
g_array_append_val (all_actions, name);
240
g_strfreev (actions);
243
g_hash_table_iter_init (&it, muxer->groups);
244
while (g_hash_table_iter_next (&it, (gpointer *) &prefix, (gpointer *) &subgroup))
246
actions = g_action_group_list_actions (subgroup);
247
for (a = actions; *a; a++)
249
gchar *full_name = g_strdup_printf ("%s.%s", prefix, *a);
250
g_array_append_val (all_actions, full_name);
252
g_strfreev (actions);
256
return (gchar **) g_array_free (all_actions, FALSE);
260
g_action_muxer_activate_action (GActionGroup *group,
261
const gchar *action_name,
264
GActionMuxer *muxer = G_ACTION_MUXER (group);
265
GActionGroup *subgroup;
268
g_return_if_fail (action_name != NULL);
270
subgroup = g_action_muxer_lookup_group (muxer, action_name, &action);
273
g_action_group_activate_action (subgroup, action, parameter);
277
g_action_muxer_change_action_state (GActionGroup *group,
278
const gchar *action_name,
281
GActionMuxer *muxer = G_ACTION_MUXER (group);
282
GActionGroup *subgroup;
285
g_return_if_fail (action_name != NULL);
287
subgroup = g_action_muxer_lookup_group (muxer, action_name, &action);
290
g_action_group_change_action_state (subgroup, action, value);
294
g_action_muxer_query_action (GActionGroup *group,
295
const gchar *action_name,
297
const GVariantType **parameter_type,
298
const GVariantType **state_type,
299
GVariant **state_hint,
302
GActionMuxer *muxer = G_ACTION_MUXER (group);
303
GActionGroup *subgroup;
306
g_return_val_if_fail (action_name != NULL, FALSE);
308
subgroup = g_action_muxer_lookup_group (muxer, action_name, &action);
313
return g_action_group_query_action (subgroup, action, enabled, parameter_type,
314
state_type, state_hint, state);
318
g_action_muxer_action_added (GActionGroup *group,
322
GActionMuxer *muxer = user_data;
325
full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
329
g_action_group_action_added (G_ACTION_GROUP (muxer), full_name);
335
g_action_muxer_action_removed (GActionGroup *group,
339
GActionMuxer *muxer = user_data;
342
full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
346
g_action_group_action_removed (G_ACTION_GROUP (muxer), full_name);
352
g_action_muxer_action_state_changed (GActionGroup *group,
357
GActionMuxer *muxer = user_data;
360
full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
364
g_action_group_action_state_changed (G_ACTION_GROUP (muxer), full_name, value);
370
g_action_muxer_action_enabled_changed (GActionGroup *group,
375
GActionMuxer *muxer = user_data;
378
full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
382
g_action_group_action_enabled_changed (G_ACTION_GROUP (muxer), full_name, enabled);
388
* g_action_muxer_new:
390
* Creates a new #GActionMuxer.
393
g_action_muxer_new (void)
395
return g_object_new (G_TYPE_ACTION_MUXER, NULL);
399
* g_action_muxer_insert:
400
* @muxer: a #GActionMuxer
401
* @prefix: (allow-none): the prefix string for the action group, or NULL
402
* @group: (allow-none): a #GActionGroup, or NULL
404
* Adds the actions in @group to the list of actions provided by @muxer.
405
* @prefix is prefixed to each action name, such that for each action
406
* <varname>x</varname> in @group, there is an equivalent action
407
* @prefix<literal>.</literal><varname>x</varname> in @muxer.
409
* For example, if @prefix is "<literal>app</literal>" and @group contains an
410
* action called "<literal>quit</literal>", then @muxer will now contain an
411
* action called "<literal>app.quit</literal>".
413
* If @prefix is <literal>NULL</literal>, the actions in @group will be added
414
* to @muxer without prefix.
416
* If @group is <literal>NULL</literal>, this function has the same effect as
417
* calling g_action_muxer_remove() with @prefix.
419
* There may only be one group per prefix (including the
420
* <literal>NULL</literal>-prefix). If a group has been added with @prefix in
421
* a previous call to this function, it will be removed.
423
* @prefix must not contain a dot ('.').
426
g_action_muxer_insert (GActionMuxer *muxer,
434
g_return_if_fail (G_IS_ACTION_MUXER (muxer));
435
g_return_if_fail (G_IS_ACTION_GROUP (group));
437
g_action_muxer_remove (muxer, prefix);
441
prefix_copy = g_strdup (prefix);
442
g_hash_table_insert (muxer->groups, prefix_copy, g_object_ref (group));
443
g_hash_table_insert (muxer->reverse, group, prefix_copy);
446
muxer->global_actions = g_object_ref (group);
448
actions = g_action_group_list_actions (group);
449
for (action = actions; *action; action++)
450
g_action_muxer_action_added (group, *action, muxer);
451
g_strfreev (actions);
453
g_signal_connect (group, "action-added", G_CALLBACK (g_action_muxer_action_added), muxer);
454
g_signal_connect (group, "action-removed", G_CALLBACK (g_action_muxer_action_removed), muxer);
455
g_signal_connect (group, "action-enabled-changed", G_CALLBACK (g_action_muxer_action_enabled_changed), muxer);
456
g_signal_connect (group, "action-state-changed", G_CALLBACK (g_action_muxer_action_state_changed), muxer);
460
* g_action_muxer_remove:
461
* @muxer: a #GActionMuxer
462
* @prefix: (allow-none): the prefix of the action group to remove, or NULL
464
* Removes a #GActionGroup from the #GActionMuxer.
467
g_action_muxer_remove (GActionMuxer *muxer,
470
GActionGroup *subgroup;
472
g_return_if_fail (G_IS_ACTION_MUXER (muxer));
474
subgroup = prefix ? g_hash_table_lookup (muxer->groups, prefix) : muxer->global_actions;
478
g_action_muxer_disconnect_group (muxer, subgroup);
482
g_hash_table_remove (muxer->groups, prefix);
483
g_hash_table_remove (muxer->reverse, subgroup);
486
g_clear_object (&muxer->global_actions);
490
* g_action_muxer_get:
491
* @muxer: a #GActionMuxer
492
* @prefix: (allow-none): the prefix of the action group to get, or NULL
494
* Looks for an action group and returns it if found
496
* Return value: (transfer none): Action group that matches @prefix
499
g_action_muxer_get (GActionMuxer * muxer, const gchar * prefix)
501
g_return_val_if_fail (G_IS_ACTION_MUXER (muxer), NULL);
503
return prefix ? g_hash_table_lookup (muxer->groups, prefix) : muxer->global_actions;