~ubuntu-branches/ubuntu/trusty/hud/trusty-updates

« back to all changes in this revision

Viewing changes to libhud-client/action-muxer.c

  • Committer: Package Import Robot
  • Author(s): Ubuntu daily release
  • Date: 2014-01-20 19:43:59 UTC
  • mfrom: (1.1.26)
  • Revision ID: package-import@ubuntu.com-20140120194359-jxxxqtd4ql9elvpf
Tags: 13.10.1+14.04.20140120-0ubuntu1
* New rebuild forced
* Automatic snapshot from revision 362

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2012 Canonical Ltd.
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 *
 
16
 * Authors:
 
17
 *     Lars Uebernickel <lars.uebernickel@canonical.com>
 
18
 *     Ryan Lortie <desrt@desrt.ca>
 
19
 */
 
20
 
 
21
#include <libhud-client/action-muxer.h>
 
22
 
 
23
#include <string.h>
 
24
 
 
25
/*
 
26
 * SECTION:gactionmuxer
 
27
 * @short_description: Aggregate several action groups
 
28
 *
 
29
 * #GActionMuxer is a #GActionGroup that is capable of containing other
 
30
 * #GActionGroup instances.
 
31
 *
 
32
 * The typical use is aggregating all of the actions applicable to a
 
33
 * particular context into a single action group, with namespacing.
 
34
 *
 
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').
 
39
 *
 
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.
 
44
 *
 
45
 * Activations and state change requests on the #GActionMuxer are wired
 
46
 * through to the underlying action group in the expected way.
 
47
 */
 
48
 
 
49
typedef GObjectClass GActionMuxerClass;
 
50
 
 
51
struct _GActionMuxer
 
52
{
 
53
  GObject parent;
 
54
  GActionGroup *global_actions;
 
55
  GHashTable *groups;  /* prefix -> subgroup */
 
56
  GHashTable *reverse; /* subgroup -> prefix */
 
57
};
 
58
 
 
59
 
 
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,
 
68
                                                       GVariant     *parameter);
 
69
static void     g_action_muxer_change_action_state    (GActionGroup *group,
 
70
                                                       const gchar  *action_name,
 
71
                                                       GVariant     *value);
 
72
static gboolean g_action_muxer_query_action           (GActionGroup        *group,
 
73
                                                       const gchar         *action_name,
 
74
                                                       gboolean            *enabled,
 
75
                                                       const GVariantType **parameter_type,
 
76
                                                       const GVariantType **state_type,
 
77
                                                       GVariant           **state_hint,
 
78
                                                       GVariant           **state);
 
79
static void     g_action_muxer_action_added           (GActionGroup *group,
 
80
                                                       gchar        *action_name,
 
81
                                                       gpointer      user_data);
 
82
static void     g_action_muxer_action_removed         (GActionGroup *group,
 
83
                                                       gchar        *action_name,
 
84
                                                       gpointer      user_data);
 
85
static void     g_action_muxer_action_state_changed   (GActionGroup *group,
 
86
                                                       gchar        *action_name,
 
87
                                                       GVariant     *value,
 
88
                                                       gpointer      user_data);
 
89
static void     g_action_muxer_action_enabled_changed (GActionGroup *group,
 
90
                                                       gchar        *action_name,
 
91
                                                       gboolean      enabled,
 
92
                                                       gpointer      user_data);
 
93
 
 
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))
 
96
 
 
97
 
 
98
static void
 
99
g_action_muxer_class_init (GObjectClass *klass)
 
100
{
 
101
  klass->dispose = g_action_muxer_dispose;
 
102
  klass->finalize = g_action_muxer_finalize;
 
103
}
 
104
 
 
105
static void
 
106
g_action_muxer_init (GActionMuxer *muxer)
 
107
{
 
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);
 
111
}
 
112
 
 
113
static void
 
114
g_action_muxer_group_init (GActionGroupInterface *iface)
 
115
{
 
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;
 
120
}
 
121
 
 
122
static void
 
123
g_action_muxer_dispose (GObject *object)
 
124
{
 
125
  GActionMuxer *muxer = G_ACTION_MUXER (object);
 
126
  GHashTableIter it;
 
127
  GActionGroup *subgroup;
 
128
 
 
129
  if (muxer->global_actions)
 
130
    {
 
131
      g_action_muxer_disconnect_group (muxer, muxer->global_actions);
 
132
      g_clear_object (&muxer->global_actions);
 
133
    }
 
134
 
 
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);
 
138
 
 
139
  g_hash_table_remove_all (muxer->groups);
 
140
  g_hash_table_remove_all (muxer->reverse);
 
141
}
 
142
 
 
143
static void
 
144
g_action_muxer_finalize (GObject *object)
 
145
{
 
146
  GActionMuxer *muxer = G_ACTION_MUXER (object);
 
147
 
 
148
  g_hash_table_unref (muxer->groups);
 
149
  g_hash_table_unref (muxer->reverse);
 
150
 
 
151
  G_OBJECT_CLASS (g_action_muxer_parent_class)->finalize (object);
 
152
}
 
153
 
 
154
static GActionGroup *
 
155
g_action_muxer_lookup_group (GActionMuxer *muxer,
 
156
                             const gchar  *full_name,
 
157
                             const gchar **action_name)
 
158
{
 
159
  const gchar *sep;
 
160
  GActionGroup *group;
 
161
 
 
162
  sep = strchr (full_name, '.');
 
163
 
 
164
  if (sep)
 
165
    {
 
166
      gchar *prefix;
 
167
      prefix = g_strndup (full_name, sep - full_name);
 
168
      group = g_hash_table_lookup (muxer->groups, prefix);
 
169
      g_free (prefix);
 
170
      if (action_name)
 
171
        *action_name = sep + 1;
 
172
    }
 
173
  else
 
174
    {
 
175
      group = muxer->global_actions;
 
176
      if (action_name)
 
177
        *action_name = full_name;
 
178
    }
 
179
 
 
180
  return group;
 
181
}
 
182
 
 
183
static gchar *
 
184
g_action_muxer_lookup_full_name (GActionMuxer *muxer,
 
185
                                 GActionGroup *subgroup,
 
186
                                 const gchar  *action_name)
 
187
{
 
188
  gpointer prefix;
 
189
 
 
190
  if (subgroup == muxer->global_actions)
 
191
    return g_strdup (action_name);
 
192
 
 
193
  if (g_hash_table_lookup_extended (muxer->reverse, subgroup, NULL, &prefix))
 
194
    return g_strdup_printf ("%s.%s", (gchar *) prefix, action_name);
 
195
 
 
196
  return NULL;
 
197
}
 
198
 
 
199
static void
 
200
g_action_muxer_disconnect_group (GActionMuxer *muxer,
 
201
                                 GActionGroup *subgroup)
 
202
{
 
203
  gchar **actions;
 
204
  gchar **action;
 
205
 
 
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);
 
210
 
 
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
 
217
}
 
218
 
 
219
static gchar **
 
220
g_action_muxer_list_actions (GActionGroup *group)
 
221
{
 
222
  GActionMuxer *muxer = G_ACTION_MUXER (group);
 
223
  GHashTableIter it;
 
224
  GArray *all_actions;
 
225
  gchar *prefix;
 
226
  GActionGroup *subgroup;
 
227
  gchar **actions;
 
228
  gchar **a;
 
229
 
 
230
  all_actions = g_array_sized_new (TRUE, FALSE, sizeof (gchar *), 8);
 
231
 
 
232
  if (muxer->global_actions)
 
233
    {
 
234
      actions = g_action_group_list_actions (muxer->global_actions);
 
235
      for (a = actions; *a; a++)
 
236
        {
 
237
          gchar *name = g_strdup (*a);
 
238
          g_array_append_val (all_actions, name);
 
239
        }
 
240
      g_strfreev (actions);
 
241
    }
 
242
 
 
243
  g_hash_table_iter_init (&it, muxer->groups);
 
244
  while (g_hash_table_iter_next (&it, (gpointer *) &prefix, (gpointer *) &subgroup))
 
245
    {
 
246
      actions = g_action_group_list_actions (subgroup);
 
247
      for (a = actions; *a; a++)
 
248
        {
 
249
          gchar *full_name = g_strdup_printf ("%s.%s", prefix, *a);
 
250
          g_array_append_val (all_actions, full_name);
 
251
        }
 
252
      g_strfreev (actions);
 
253
    }
 
254
 
 
255
 
 
256
  return (gchar **) g_array_free (all_actions, FALSE);
 
257
}
 
258
 
 
259
static void
 
260
g_action_muxer_activate_action (GActionGroup  *group,
 
261
                                const gchar   *action_name,
 
262
                                GVariant      *parameter)
 
263
{
 
264
  GActionMuxer *muxer = G_ACTION_MUXER (group);
 
265
  GActionGroup *subgroup;
 
266
  const gchar *action;
 
267
 
 
268
  g_return_if_fail (action_name != NULL);
 
269
 
 
270
  subgroup = g_action_muxer_lookup_group (muxer, action_name, &action);
 
271
 
 
272
  if (subgroup)
 
273
    g_action_group_activate_action (subgroup, action, parameter);
 
274
}
 
275
 
 
276
static void
 
277
g_action_muxer_change_action_state (GActionGroup  *group,
 
278
                                    const gchar   *action_name,
 
279
                                    GVariant      *value)
 
280
{
 
281
  GActionMuxer *muxer = G_ACTION_MUXER (group);
 
282
  GActionGroup *subgroup;
 
283
  const gchar *action;
 
284
 
 
285
  g_return_if_fail (action_name != NULL);
 
286
 
 
287
  subgroup = g_action_muxer_lookup_group (muxer, action_name, &action);
 
288
 
 
289
  if (subgroup)
 
290
    g_action_group_change_action_state (subgroup, action, value);
 
291
}
 
292
 
 
293
static gboolean
 
294
g_action_muxer_query_action (GActionGroup        *group,
 
295
                             const gchar         *action_name,
 
296
                             gboolean            *enabled,
 
297
                             const GVariantType **parameter_type,
 
298
                             const GVariantType **state_type,
 
299
                             GVariant           **state_hint,
 
300
                             GVariant           **state)
 
301
{
 
302
  GActionMuxer *muxer = G_ACTION_MUXER (group);
 
303
  GActionGroup *subgroup;
 
304
  const gchar *action;
 
305
 
 
306
  g_return_val_if_fail (action_name != NULL, FALSE);
 
307
 
 
308
  subgroup = g_action_muxer_lookup_group (muxer, action_name, &action);
 
309
 
 
310
  if (!subgroup)
 
311
    return FALSE;
 
312
 
 
313
  return g_action_group_query_action (subgroup, action, enabled, parameter_type,
 
314
                                      state_type, state_hint, state);
 
315
}
 
316
 
 
317
static void
 
318
g_action_muxer_action_added (GActionGroup *group,
 
319
                             gchar        *action_name,
 
320
                             gpointer      user_data)
 
321
{
 
322
  GActionMuxer *muxer = user_data;
 
323
  gchar *full_name;
 
324
 
 
325
  full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
 
326
 
 
327
  if (full_name)
 
328
    {
 
329
      g_action_group_action_added (G_ACTION_GROUP (muxer), full_name);
 
330
      g_free (full_name);
 
331
    }
 
332
}
 
333
 
 
334
static void
 
335
g_action_muxer_action_removed (GActionGroup *group,
 
336
                               gchar        *action_name,
 
337
                               gpointer      user_data)
 
338
{
 
339
  GActionMuxer *muxer = user_data;
 
340
  gchar *full_name;
 
341
 
 
342
  full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
 
343
 
 
344
  if (full_name)
 
345
    {
 
346
      g_action_group_action_removed (G_ACTION_GROUP (muxer), full_name);
 
347
      g_free (full_name);
 
348
    }
 
349
}
 
350
 
 
351
static void
 
352
g_action_muxer_action_state_changed (GActionGroup *group,
 
353
                                     gchar        *action_name,
 
354
                                     GVariant     *value,
 
355
                                     gpointer      user_data)
 
356
{
 
357
  GActionMuxer *muxer = user_data;
 
358
  gchar *full_name;
 
359
 
 
360
  full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
 
361
 
 
362
  if (full_name)
 
363
    {
 
364
      g_action_group_action_state_changed (G_ACTION_GROUP (muxer), full_name, value);
 
365
      g_free (full_name);
 
366
    }
 
367
}
 
368
 
 
369
static void
 
370
g_action_muxer_action_enabled_changed (GActionGroup *group,
 
371
                                       gchar        *action_name,
 
372
                                       gboolean      enabled,
 
373
                                       gpointer      user_data)
 
374
{
 
375
  GActionMuxer *muxer = user_data;
 
376
  gchar *full_name;
 
377
 
 
378
  full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
 
379
 
 
380
  if (full_name)
 
381
    {
 
382
      g_action_group_action_enabled_changed (G_ACTION_GROUP (muxer), full_name, enabled);
 
383
      g_free (full_name);
 
384
    }
 
385
}
 
386
 
 
387
/*
 
388
 * g_action_muxer_new:
 
389
 *
 
390
 * Creates a new #GActionMuxer.
 
391
 */
 
392
GActionMuxer *
 
393
g_action_muxer_new (void)
 
394
{
 
395
  return g_object_new (G_TYPE_ACTION_MUXER, NULL);
 
396
}
 
397
 
 
398
/*
 
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
 
403
 *
 
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.
 
408
 *
 
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>".
 
412
 *
 
413
 * If @prefix is <literal>NULL</literal>, the actions in @group will be added
 
414
 * to @muxer without prefix.
 
415
 *
 
416
 * If @group is <literal>NULL</literal>, this function has the same effect as
 
417
 * calling g_action_muxer_remove() with @prefix.
 
418
 *
 
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.
 
422
 *
 
423
 * @prefix must not contain a dot ('.').
 
424
 */
 
425
void
 
426
g_action_muxer_insert (GActionMuxer *muxer,
 
427
                       const gchar  *prefix,
 
428
                       GActionGroup *group)
 
429
{
 
430
  gchar *prefix_copy;
 
431
  gchar **actions;
 
432
  gchar **action;
 
433
 
 
434
  g_return_if_fail (G_IS_ACTION_MUXER (muxer));
 
435
  g_return_if_fail (G_IS_ACTION_GROUP (group));
 
436
 
 
437
  g_action_muxer_remove (muxer, prefix);
 
438
 
 
439
  if (prefix)
 
440
    {
 
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);
 
444
    }
 
445
  else
 
446
    muxer->global_actions = g_object_ref (group);
 
447
 
 
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);
 
452
 
 
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);
 
457
}
 
458
 
 
459
/*
 
460
 * g_action_muxer_remove:
 
461
 * @muxer: a #GActionMuxer
 
462
 * @prefix: (allow-none): the prefix of the action group to remove, or NULL
 
463
 *
 
464
 * Removes a #GActionGroup from the #GActionMuxer.
 
465
 */
 
466
void
 
467
g_action_muxer_remove (GActionMuxer *muxer,
 
468
                       const gchar  *prefix)
 
469
{
 
470
  GActionGroup *subgroup;
 
471
 
 
472
  g_return_if_fail (G_IS_ACTION_MUXER (muxer));
 
473
 
 
474
  subgroup = prefix ? g_hash_table_lookup (muxer->groups, prefix) : muxer->global_actions;
 
475
  if (!subgroup)
 
476
    return;
 
477
 
 
478
  g_action_muxer_disconnect_group (muxer, subgroup);
 
479
 
 
480
  if (prefix)
 
481
    {
 
482
      g_hash_table_remove (muxer->groups, prefix);
 
483
      g_hash_table_remove (muxer->reverse, subgroup);
 
484
    }
 
485
  else
 
486
    g_clear_object (&muxer->global_actions);
 
487
}
 
488
 
 
489
/*
 
490
 * g_action_muxer_get:
 
491
 * @muxer: a #GActionMuxer
 
492
 * @prefix: (allow-none): the prefix of the action group to get, or NULL
 
493
 *
 
494
 * Looks for an action group and returns it if found
 
495
 *
 
496
 * Return value: (transfer none): Action group that matches @prefix
 
497
 */
 
498
GActionGroup *
 
499
g_action_muxer_get (GActionMuxer * muxer, const gchar * prefix)
 
500
{
 
501
  g_return_val_if_fail (G_IS_ACTION_MUXER (muxer), NULL);
 
502
 
 
503
  return prefix ? g_hash_table_lookup (muxer->groups, prefix) : muxer->global_actions;
 
504
}