~ubuntu-branches/ubuntu/utopic/qmenumodel/utopic-proposed

« back to all changes in this revision

Viewing changes to libqmenumodel/src/gtk/gtkactionmuxer.c

  • Committer: Package Import Robot
  • Author(s): Ubuntu daily release
  • Date: 2013-08-12 18:38:17 UTC
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: package-import@ubuntu.com-20130812183817-iu42w3g66bork08k
Tags: upstream-0.2.6+13.10.20130812
Import upstream version 0.2.6+13.10.20130812

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2011 Canonical 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 of the licence, 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 Public
 
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 
16
 *
 
17
 * Author: Ryan Lortie <desrt@desrt.ca>
 
18
 */
 
19
 
 
20
#include "config.h"
 
21
 
 
22
#include "gtkactionmuxer.h"
 
23
 
 
24
#include "gtkactionobservable.h"
 
25
#include "gtkactionobserver.h"
 
26
 
 
27
#include <string.h>
 
28
 
 
29
/**
 
30
 * SECTION:gtkactionmuxer
 
31
 * @short_description: Aggregate and monitor several action groups
 
32
 *
 
33
 * #GtkActionMuxer is a #GActionGroup and #GtkActionObservable that is
 
34
 * capable of containing other #GActionGroup instances.
 
35
 *
 
36
 * The typical use is aggregating all of the actions applicable to a
 
37
 * particular context into a single action group, with namespacing.
 
38
 *
 
39
 * Consider the case of two action groups -- one containing actions
 
40
 * applicable to an entire application (such as 'quit') and one
 
41
 * containing actions applicable to a particular window in the
 
42
 * application (such as 'fullscreen').
 
43
 *
 
44
 * In this case, each of these action groups could be added to a
 
45
 * #GtkActionMuxer with the prefixes "app" and "win", respectively.  This
 
46
 * would expose the actions as "app.quit" and "win.fullscreen" on the
 
47
 * #GActionGroup interface presented by the #GtkActionMuxer.
 
48
 *
 
49
 * Activations and state change requests on the #GtkActionMuxer are wired
 
50
 * through to the underlying action group in the expected way.
 
51
 *
 
52
 * This class is typically only used at the site of "consumption" of
 
53
 * actions (eg: when displaying a menu that contains many actions on
 
54
 * different objects).
 
55
 */
 
56
 
 
57
static void     gtk_action_muxer_group_iface_init         (GActionGroupInterface        *iface);
 
58
static void     gtk_action_muxer_observable_iface_init    (GtkActionObservableInterface *iface);
 
59
 
 
60
typedef GObjectClass GtkActionMuxerClass;
 
61
 
 
62
struct _GtkActionMuxer
 
63
{
 
64
  GObject parent_instance;
 
65
 
 
66
  GHashTable *observed_actions;
 
67
  GHashTable *groups;
 
68
  GtkActionMuxer *parent;
 
69
};
 
70
 
 
71
G_DEFINE_TYPE_WITH_CODE (GtkActionMuxer, gtk_action_muxer, G_TYPE_OBJECT,
 
72
                         G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, gtk_action_muxer_group_iface_init)
 
73
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTION_OBSERVABLE, gtk_action_muxer_observable_iface_init))
 
74
 
 
75
enum
 
76
{
 
77
  PROP_0,
 
78
  PROP_PARENT,
 
79
  NUM_PROPERTIES
 
80
};
 
81
 
 
82
static GParamSpec *properties[NUM_PROPERTIES];
 
83
 
 
84
typedef struct
 
85
{
 
86
  GtkActionMuxer *muxer;
 
87
  GSList       *watchers;
 
88
  gchar        *fullname;
 
89
} Action;
 
90
 
 
91
typedef struct
 
92
{
 
93
  GtkActionMuxer *muxer;
 
94
  GActionGroup *group;
 
95
  gchar        *prefix;
 
96
  gulong        handler_ids[4];
 
97
} Group;
 
98
 
 
99
static void
 
100
gtk_action_muxer_append_group_actions (gpointer key,
 
101
                                       gpointer value,
 
102
                                       gpointer user_data)
 
103
{
 
104
  const gchar *prefix = key;
 
105
  Group *group = value;
 
106
  GArray *actions = user_data;
 
107
  gchar **group_actions;
 
108
  gchar **action;
 
109
 
 
110
  group_actions = g_action_group_list_actions (group->group);
 
111
  for (action = group_actions; *action; action++)
 
112
    {
 
113
      gchar *fullname;
 
114
 
 
115
      fullname = g_strconcat (prefix, ".", *action, NULL);
 
116
      g_array_append_val (actions, fullname);
 
117
    }
 
118
 
 
119
  g_strfreev (group_actions);
 
120
}
 
121
 
 
122
static gchar **
 
123
gtk_action_muxer_list_actions (GActionGroup *action_group)
 
124
{
 
125
  GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group);
 
126
  GArray *actions;
 
127
 
 
128
  actions = g_array_new (TRUE, FALSE, sizeof (gchar *));
 
129
 
 
130
  for ( ; muxer != NULL; muxer = muxer->parent)
 
131
    {
 
132
      g_hash_table_foreach (muxer->groups,
 
133
                            gtk_action_muxer_append_group_actions,
 
134
                            actions);
 
135
    }
 
136
 
 
137
  return (gchar **) g_array_free (actions, FALSE);
 
138
}
 
139
 
 
140
static Group *
 
141
gtk_action_muxer_find_group (GtkActionMuxer  *muxer,
 
142
                             const gchar     *full_name,
 
143
                             const gchar    **action_name)
 
144
{
 
145
  const gchar *dot;
 
146
  gchar *prefix;
 
147
  Group *group;
 
148
 
 
149
  dot = strchr (full_name, '.');
 
150
 
 
151
  if (!dot)
 
152
    return NULL;
 
153
 
 
154
  prefix = g_strndup (full_name, dot - full_name);
 
155
  group = g_hash_table_lookup (muxer->groups, prefix);
 
156
  g_free (prefix);
 
157
 
 
158
  if (action_name)
 
159
    *action_name = dot + 1;
 
160
 
 
161
  return group;
 
162
}
 
163
 
 
164
static void
 
165
gtk_action_muxer_action_enabled_changed (GtkActionMuxer *muxer,
 
166
                                         const gchar    *action_name,
 
167
                                         gboolean        enabled)
 
168
{
 
169
  Action *action;
 
170
  GSList *node;
 
171
 
 
172
  action = g_hash_table_lookup (muxer->observed_actions, action_name);
 
173
  for (node = action ? action->watchers : NULL; node; node = node->next)
 
174
    gtk_action_observer_action_enabled_changed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name, enabled);
 
175
  g_action_group_action_enabled_changed (G_ACTION_GROUP (muxer), action_name, enabled);
 
176
}
 
177
 
 
178
static void
 
179
gtk_action_muxer_group_action_enabled_changed (GActionGroup *action_group,
 
180
                                               const gchar  *action_name,
 
181
                                               gboolean      enabled,
 
182
                                               gpointer      user_data)
 
183
{
 
184
  Group *group = user_data;
 
185
  gchar *fullname;
 
186
 
 
187
  fullname = g_strconcat (group->prefix, ".", action_name, NULL);
 
188
  gtk_action_muxer_action_enabled_changed (group->muxer, fullname, enabled);
 
189
 
 
190
  g_free (fullname);
 
191
}
 
192
 
 
193
static void
 
194
gtk_action_muxer_parent_action_enabled_changed (GActionGroup *action_group,
 
195
                                                const gchar  *action_name,
 
196
                                                gboolean      enabled,
 
197
                                                gpointer      user_data)
 
198
{
 
199
  GtkActionMuxer *muxer = user_data;
 
200
 
 
201
  gtk_action_muxer_action_enabled_changed (muxer, action_name, enabled);
 
202
}
 
203
 
 
204
static void
 
205
gtk_action_muxer_action_state_changed (GtkActionMuxer *muxer,
 
206
                                       const gchar    *action_name,
 
207
                                       GVariant       *state)
 
208
{
 
209
  Action *action;
 
210
  GSList *node;
 
211
 
 
212
  action = g_hash_table_lookup (muxer->observed_actions, action_name);
 
213
  for (node = action ? action->watchers : NULL; node; node = node->next)
 
214
    gtk_action_observer_action_state_changed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name, state);
 
215
  g_action_group_action_state_changed (G_ACTION_GROUP (muxer), action_name, state);
 
216
}
 
217
 
 
218
static void
 
219
gtk_action_muxer_group_action_state_changed (GActionGroup *action_group,
 
220
                                             const gchar  *action_name,
 
221
                                             GVariant     *state,
 
222
                                             gpointer      user_data)
 
223
{
 
224
  Group *group = user_data;
 
225
  gchar *fullname;
 
226
 
 
227
  fullname = g_strconcat (group->prefix, ".", action_name, NULL);
 
228
  gtk_action_muxer_action_state_changed (group->muxer, fullname, state);
 
229
 
 
230
  g_free (fullname);
 
231
}
 
232
 
 
233
static void
 
234
gtk_action_muxer_parent_action_state_changed (GActionGroup *action_group,
 
235
                                              const gchar  *action_name,
 
236
                                              GVariant     *state,
 
237
                                              gpointer      user_data)
 
238
{
 
239
  GtkActionMuxer *muxer = user_data;
 
240
 
 
241
  gtk_action_muxer_action_state_changed (muxer, action_name, state);
 
242
}
 
243
 
 
244
static void
 
245
gtk_action_muxer_action_added (GtkActionMuxer *muxer,
 
246
                               const gchar    *action_name,
 
247
                               GActionGroup   *original_group,
 
248
                               const gchar    *orignal_action_name)
 
249
{
 
250
  const GVariantType *parameter_type;
 
251
  gboolean enabled;
 
252
  GVariant *state;
 
253
  Action *action;
 
254
 
 
255
  action = g_hash_table_lookup (muxer->observed_actions, action_name);
 
256
 
 
257
  if (action && action->watchers &&
 
258
      g_action_group_query_action (original_group, orignal_action_name,
 
259
                                   &enabled, &parameter_type, NULL, NULL, &state))
 
260
    {
 
261
      GSList *node;
 
262
 
 
263
      for (node = action->watchers; node; node = node->next)
 
264
        gtk_action_observer_action_added (node->data,
 
265
                                        GTK_ACTION_OBSERVABLE (muxer),
 
266
                                        action_name, parameter_type, enabled, state);
 
267
 
 
268
      if (state)
 
269
        g_variant_unref (state);
 
270
    }
 
271
 
 
272
  g_action_group_action_added (G_ACTION_GROUP (muxer), action_name);
 
273
}
 
274
 
 
275
static void
 
276
gtk_action_muxer_action_added_to_group (GActionGroup *action_group,
 
277
                                        const gchar  *action_name,
 
278
                                        gpointer      user_data)
 
279
{
 
280
  Group *group = user_data;
 
281
  gchar *fullname;
 
282
 
 
283
  fullname = g_strconcat (group->prefix, ".", action_name, NULL);
 
284
  gtk_action_muxer_action_added (group->muxer, fullname, action_group, action_name);
 
285
 
 
286
  g_free (fullname);
 
287
}
 
288
 
 
289
static void
 
290
gtk_action_muxer_action_added_to_parent (GActionGroup *action_group,
 
291
                                         const gchar  *action_name,
 
292
                                         gpointer      user_data)
 
293
{
 
294
  GtkActionMuxer *muxer = user_data;
 
295
 
 
296
  gtk_action_muxer_action_added (muxer, action_name, action_group, action_name);
 
297
}
 
298
 
 
299
static void
 
300
gtk_action_muxer_action_removed (GtkActionMuxer *muxer,
 
301
                                 const gchar    *action_name)
 
302
{
 
303
  Action *action;
 
304
  GSList *node;
 
305
 
 
306
  action = g_hash_table_lookup (muxer->observed_actions, action_name);
 
307
  for (node = action ? action->watchers : NULL; node; node = node->next)
 
308
    gtk_action_observer_action_removed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name);
 
309
  g_action_group_action_removed (G_ACTION_GROUP (muxer), action_name);
 
310
}
 
311
 
 
312
static void
 
313
gtk_action_muxer_action_removed_from_group (GActionGroup *action_group,
 
314
                                            const gchar  *action_name,
 
315
                                            gpointer      user_data)
 
316
{
 
317
  Group *group = user_data;
 
318
  gchar *fullname;
 
319
 
 
320
  fullname = g_strconcat (group->prefix, ".", action_name, NULL);
 
321
  gtk_action_muxer_action_removed (group->muxer, fullname);
 
322
 
 
323
  g_free (fullname);
 
324
}
 
325
 
 
326
static void
 
327
gtk_action_muxer_action_removed_from_parent (GActionGroup *action_group,
 
328
                                             const gchar  *action_name,
 
329
                                             gpointer      user_data)
 
330
{
 
331
  GtkActionMuxer *muxer = user_data;
 
332
 
 
333
  gtk_action_muxer_action_removed (muxer, action_name);
 
334
}
 
335
 
 
336
static gboolean
 
337
gtk_action_muxer_query_action (GActionGroup        *action_group,
 
338
                               const gchar         *action_name,
 
339
                               gboolean            *enabled,
 
340
                               const GVariantType **parameter_type,
 
341
                               const GVariantType **state_type,
 
342
                               GVariant           **state_hint,
 
343
                               GVariant           **state)
 
344
{
 
345
  GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group);
 
346
  Group *group;
 
347
  const gchar *unprefixed_name;
 
348
 
 
349
  group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
 
350
 
 
351
  if (group)
 
352
    return g_action_group_query_action (group->group, unprefixed_name, enabled,
 
353
                                        parameter_type, state_type, state_hint, state);
 
354
 
 
355
  if (muxer->parent)
 
356
    return g_action_group_query_action (G_ACTION_GROUP (muxer->parent), action_name,
 
357
                                        enabled, parameter_type,
 
358
                                        state_type, state_hint, state);
 
359
 
 
360
  return FALSE;
 
361
}
 
362
 
 
363
static void
 
364
gtk_action_muxer_activate_action (GActionGroup *action_group,
 
365
                                  const gchar  *action_name,
 
366
                                  GVariant     *parameter)
 
367
{
 
368
  GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group);
 
369
  Group *group;
 
370
  const gchar *unprefixed_name;
 
371
 
 
372
  group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
 
373
 
 
374
  if (group)
 
375
    g_action_group_activate_action (group->group, unprefixed_name, parameter);
 
376
  else if (muxer->parent)
 
377
    g_action_group_activate_action (G_ACTION_GROUP (muxer->parent), action_name, parameter);
 
378
}
 
379
 
 
380
static void
 
381
gtk_action_muxer_change_action_state (GActionGroup *action_group,
 
382
                                      const gchar  *action_name,
 
383
                                      GVariant     *state)
 
384
{
 
385
  GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group);
 
386
  Group *group;
 
387
  const gchar *unprefixed_name;
 
388
 
 
389
  group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
 
390
 
 
391
  if (group)
 
392
    g_action_group_change_action_state (group->group, unprefixed_name, state);
 
393
  else if (muxer->parent)
 
394
    g_action_group_change_action_state (G_ACTION_GROUP (muxer->parent), action_name, state);
 
395
}
 
396
 
 
397
static void
 
398
gtk_action_muxer_unregister_internal (Action   *action,
 
399
                                      gpointer  observer)
 
400
{
 
401
  GtkActionMuxer *muxer = action->muxer;
 
402
  GSList **ptr;
 
403
 
 
404
  for (ptr = &action->watchers; *ptr; ptr = &(*ptr)->next)
 
405
    if ((*ptr)->data == observer)
 
406
      {
 
407
        *ptr = g_slist_remove (*ptr, observer);
 
408
 
 
409
        if (action->watchers == NULL)
 
410
            g_hash_table_remove (muxer->observed_actions, action->fullname);
 
411
 
 
412
        break;
 
413
      }
 
414
}
 
415
 
 
416
static void
 
417
gtk_action_muxer_weak_notify (gpointer  data,
 
418
                              GObject  *where_the_object_was)
 
419
{
 
420
  Action *action = data;
 
421
 
 
422
  gtk_action_muxer_unregister_internal (action, where_the_object_was);
 
423
}
 
424
 
 
425
static void
 
426
gtk_action_muxer_register_observer (GtkActionObservable *observable,
 
427
                                    const gchar         *name,
 
428
                                    GtkActionObserver   *observer)
 
429
{
 
430
  GtkActionMuxer *muxer = GTK_ACTION_MUXER (observable);
 
431
  Action *action;
 
432
 
 
433
  action = g_hash_table_lookup (muxer->observed_actions, name);
 
434
 
 
435
  if (action == NULL)
 
436
    {
 
437
      action = g_slice_new (Action);
 
438
      action->muxer = muxer;
 
439
      action->fullname = g_strdup (name);
 
440
      action->watchers = NULL;
 
441
 
 
442
      g_hash_table_insert (muxer->observed_actions, action->fullname, action);
 
443
    }
 
444
 
 
445
  action->watchers = g_slist_prepend (action->watchers, observer);
 
446
  g_object_weak_ref (G_OBJECT (observer), gtk_action_muxer_weak_notify, action);
 
447
}
 
448
 
 
449
static void
 
450
gtk_action_muxer_unregister_observer (GtkActionObservable *observable,
 
451
                                      const gchar         *name,
 
452
                                      GtkActionObserver   *observer)
 
453
{
 
454
  GtkActionMuxer *muxer = GTK_ACTION_MUXER (observable);
 
455
  Action *action;
 
456
 
 
457
  action = g_hash_table_lookup (muxer->observed_actions, name);
 
458
  g_object_weak_unref (G_OBJECT (observer), gtk_action_muxer_weak_notify, action);
 
459
  gtk_action_muxer_unregister_internal (action, observer);
 
460
}
 
461
 
 
462
static void
 
463
gtk_action_muxer_free_group (gpointer data)
 
464
{
 
465
  Group *group = data;
 
466
  gint i;
 
467
 
 
468
  /* 'for loop' or 'four loop'? */
 
469
  for (i = 0; i < 4; i++)
 
470
    g_signal_handler_disconnect (group->group, group->handler_ids[i]);
 
471
 
 
472
  g_object_unref (group->group);
 
473
  g_free (group->prefix);
 
474
 
 
475
  g_slice_free (Group, group);
 
476
}
 
477
 
 
478
static void
 
479
gtk_action_muxer_free_action (gpointer data)
 
480
{
 
481
  Action *action = data;
 
482
  GSList *it;
 
483
 
 
484
  for (it = action->watchers; it; it = it->next)
 
485
    g_object_weak_unref (G_OBJECT (it->data), gtk_action_muxer_weak_notify, action);
 
486
 
 
487
  g_slist_free (action->watchers);
 
488
  g_free (action->fullname);
 
489
 
 
490
  g_slice_free (Action, action);
 
491
}
 
492
 
 
493
static void
 
494
gtk_action_muxer_finalize (GObject *object)
 
495
{
 
496
  GtkActionMuxer *muxer = GTK_ACTION_MUXER (object);
 
497
 
 
498
  g_assert_cmpint (g_hash_table_size (muxer->observed_actions), ==, 0);
 
499
  g_hash_table_unref (muxer->observed_actions);
 
500
  g_hash_table_unref (muxer->groups);
 
501
 
 
502
  G_OBJECT_CLASS (gtk_action_muxer_parent_class)
 
503
    ->finalize (object);
 
504
}
 
505
 
 
506
static void
 
507
gtk_action_muxer_dispose (GObject *object)
 
508
{
 
509
  GtkActionMuxer *muxer = GTK_ACTION_MUXER (object);
 
510
 
 
511
  if (muxer->parent)
 
512
  {
 
513
    g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_added_to_parent, muxer);
 
514
    g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_removed_from_parent, muxer);
 
515
    g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_enabled_changed, muxer);
 
516
    g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_state_changed, muxer);
 
517
 
 
518
    g_clear_object (&muxer->parent);
 
519
  }
 
520
 
 
521
  g_hash_table_remove_all (muxer->observed_actions);
 
522
 
 
523
  G_OBJECT_CLASS (gtk_action_muxer_parent_class)
 
524
    ->dispose (object);
 
525
}
 
526
 
 
527
static void
 
528
gtk_action_muxer_get_property (GObject    *object,
 
529
                               guint       property_id,
 
530
                               GValue     *value,
 
531
                               GParamSpec *pspec)
 
532
{
 
533
  GtkActionMuxer *muxer = GTK_ACTION_MUXER (object);
 
534
 
 
535
  switch (property_id)
 
536
    {
 
537
    case PROP_PARENT:
 
538
      g_value_set_object (value, gtk_action_muxer_get_parent (muxer));
 
539
      break;
 
540
 
 
541
    default:
 
542
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
543
    }
 
544
}
 
545
 
 
546
static void
 
547
gtk_action_muxer_set_property (GObject      *object,
 
548
                               guint         property_id,
 
549
                               const GValue *value,
 
550
                               GParamSpec   *pspec)
 
551
{
 
552
  GtkActionMuxer *muxer = GTK_ACTION_MUXER (object);
 
553
 
 
554
  switch (property_id)
 
555
    {
 
556
    case PROP_PARENT:
 
557
      gtk_action_muxer_set_parent (muxer, g_value_get_object (value));
 
558
      break;
 
559
 
 
560
    default:
 
561
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
562
    }
 
563
}
 
564
 
 
565
static void
 
566
gtk_action_muxer_init (GtkActionMuxer *muxer)
 
567
{
 
568
  muxer->observed_actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gtk_action_muxer_free_action);
 
569
  muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gtk_action_muxer_free_group);
 
570
}
 
571
 
 
572
static void
 
573
gtk_action_muxer_observable_iface_init (GtkActionObservableInterface *iface)
 
574
{
 
575
  iface->register_observer = gtk_action_muxer_register_observer;
 
576
  iface->unregister_observer = gtk_action_muxer_unregister_observer;
 
577
}
 
578
 
 
579
static void
 
580
gtk_action_muxer_group_iface_init (GActionGroupInterface *iface)
 
581
{
 
582
  iface->list_actions = gtk_action_muxer_list_actions;
 
583
  iface->query_action = gtk_action_muxer_query_action;
 
584
  iface->activate_action = gtk_action_muxer_activate_action;
 
585
  iface->change_action_state = gtk_action_muxer_change_action_state;
 
586
}
 
587
 
 
588
static void
 
589
gtk_action_muxer_class_init (GObjectClass *class)
 
590
{
 
591
  class->get_property = gtk_action_muxer_get_property;
 
592
  class->set_property = gtk_action_muxer_set_property;
 
593
  class->finalize = gtk_action_muxer_finalize;
 
594
  class->dispose = gtk_action_muxer_dispose;
 
595
 
 
596
  properties[PROP_PARENT] = g_param_spec_object ("parent", "Parent",
 
597
                                                 "The parent muxer",
 
598
                                                 GTK_TYPE_ACTION_MUXER,
 
599
                                                 G_PARAM_READWRITE |
 
600
                                                 G_PARAM_STATIC_STRINGS);
 
601
 
 
602
  g_object_class_install_properties (class, NUM_PROPERTIES, properties);
 
603
}
 
604
 
 
605
/**
 
606
 * gtk_action_muxer_insert:
 
607
 * @muxer: a #GtkActionMuxer
 
608
 * @prefix: the prefix string for the action group
 
609
 * @action_group: a #GActionGroup
 
610
 *
 
611
 * Adds the actions in @action_group to the list of actions provided by
 
612
 * @muxer.  @prefix is prefixed to each action name, such that for each
 
613
 * action <varname>x</varname> in @action_group, there is an equivalent
 
614
 * action @prefix<literal>.</literal><varname>x</varname> in @muxer.
 
615
 *
 
616
 * For example, if @prefix is "<literal>app</literal>" and @action_group
 
617
 * contains an action called "<literal>quit</literal>", then @muxer will
 
618
 * now contain an action called "<literal>app.quit</literal>".
 
619
 *
 
620
 * If any #GtkActionObservers are registered for actions in the group,
 
621
 * "action_added" notifications will be emitted, as appropriate.
 
622
 *
 
623
 * @prefix must not contain a dot ('.').
 
624
 */
 
625
void
 
626
gtk_action_muxer_insert (GtkActionMuxer *muxer,
 
627
                         const gchar    *prefix,
 
628
                         GActionGroup   *action_group)
 
629
{
 
630
  gchar **actions;
 
631
  Group *group;
 
632
  gint i;
 
633
 
 
634
  /* TODO: diff instead of ripout and replace */
 
635
  gtk_action_muxer_remove (muxer, prefix);
 
636
 
 
637
  group = g_slice_new (Group);
 
638
  group->muxer = muxer;
 
639
  group->group = g_object_ref (action_group);
 
640
  group->prefix = g_strdup (prefix);
 
641
 
 
642
  g_hash_table_insert (muxer->groups, group->prefix, group);
 
643
 
 
644
  actions = g_action_group_list_actions (group->group);
 
645
  for (i = 0; actions[i]; i++)
 
646
    gtk_action_muxer_action_added_to_group (group->group, actions[i], group);
 
647
  g_strfreev (actions);
 
648
 
 
649
  group->handler_ids[0] = g_signal_connect (group->group, "action-added",
 
650
                                            G_CALLBACK (gtk_action_muxer_action_added_to_group), group);
 
651
  group->handler_ids[1] = g_signal_connect (group->group, "action-removed",
 
652
                                            G_CALLBACK (gtk_action_muxer_action_removed_from_group), group);
 
653
  group->handler_ids[2] = g_signal_connect (group->group, "action-enabled-changed",
 
654
                                            G_CALLBACK (gtk_action_muxer_group_action_enabled_changed), group);
 
655
  group->handler_ids[3] = g_signal_connect (group->group, "action-state-changed",
 
656
                                            G_CALLBACK (gtk_action_muxer_group_action_state_changed), group);
 
657
}
 
658
 
 
659
/**
 
660
 * gtk_action_muxer_remove:
 
661
 * @muxer: a #GtkActionMuxer
 
662
 * @prefix: the prefix of the action group to remove
 
663
 *
 
664
 * Removes a #GActionGroup from the #GtkActionMuxer.
 
665
 *
 
666
 * If any #GtkActionObservers are registered for actions in the group,
 
667
 * "action_removed" notifications will be emitted, as appropriate.
 
668
 */
 
669
void
 
670
gtk_action_muxer_remove (GtkActionMuxer *muxer,
 
671
                         const gchar    *prefix)
 
672
{
 
673
  Group *group;
 
674
 
 
675
  group = g_hash_table_lookup (muxer->groups, prefix);
 
676
 
 
677
  if (group != NULL)
 
678
    {
 
679
      gchar **actions;
 
680
      gint i;
 
681
 
 
682
      g_hash_table_steal (muxer->groups, prefix);
 
683
 
 
684
      actions = g_action_group_list_actions (group->group);
 
685
      for (i = 0; actions[i]; i++)
 
686
        gtk_action_muxer_action_removed_from_group (group->group, actions[i], group);
 
687
      g_strfreev (actions);
 
688
 
 
689
      gtk_action_muxer_free_group (group);
 
690
    }
 
691
}
 
692
 
 
693
/**
 
694
 * gtk_action_muxer_new:
 
695
 *
 
696
 * Creates a new #GtkActionMuxer.
 
697
 */
 
698
GtkActionMuxer *
 
699
gtk_action_muxer_new (void)
 
700
{
 
701
  return g_object_new (GTK_TYPE_ACTION_MUXER, NULL);
 
702
}
 
703
 
 
704
/**
 
705
 * gtk_action_muxer_get_parent:
 
706
 * @muxer: a #GtkActionMuxer
 
707
 *
 
708
 * Returns: (transfer none): the parent of @muxer, or NULL.
 
709
 */
 
710
GtkActionMuxer *
 
711
gtk_action_muxer_get_parent (GtkActionMuxer *muxer)
 
712
{
 
713
  g_return_val_if_fail (GTK_IS_ACTION_MUXER (muxer), NULL);
 
714
 
 
715
  return muxer->parent;
 
716
}
 
717
 
 
718
/**
 
719
 * gtk_action_muxer_set_parent:
 
720
 * @muxer: a #GtkActionMuxer
 
721
 * @parent: (allow-none): the new parent #GtkActionMuxer
 
722
 *
 
723
 * Sets the parent of @muxer to @parent.
 
724
 */
 
725
void
 
726
gtk_action_muxer_set_parent (GtkActionMuxer *muxer,
 
727
                             GtkActionMuxer *parent)
 
728
{
 
729
  g_return_if_fail (GTK_IS_ACTION_MUXER (muxer));
 
730
  g_return_if_fail (parent == NULL || GTK_IS_ACTION_MUXER (parent));
 
731
 
 
732
  if (muxer->parent == parent)
 
733
    return;
 
734
 
 
735
  if (muxer->parent != NULL)
 
736
    {
 
737
      gchar **actions;
 
738
      gchar **it;
 
739
 
 
740
      actions = g_action_group_list_actions (G_ACTION_GROUP (muxer->parent));
 
741
      for (it = actions; *it; it++)
 
742
        gtk_action_muxer_action_removed (muxer, *it);
 
743
      g_strfreev (actions);
 
744
 
 
745
      g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_added_to_parent, muxer);
 
746
      g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_removed_from_parent, muxer);
 
747
      g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_enabled_changed, muxer);
 
748
      g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_state_changed, muxer);
 
749
 
 
750
      g_object_unref (muxer->parent);
 
751
    }
 
752
 
 
753
  muxer->parent = parent;
 
754
 
 
755
  if (muxer->parent != NULL)
 
756
    {
 
757
      gchar **actions;
 
758
      gchar **it;
 
759
 
 
760
      g_object_ref (muxer->parent);
 
761
 
 
762
      actions = g_action_group_list_actions (G_ACTION_GROUP (muxer->parent));
 
763
      for (it = actions; *it; it++)
 
764
        gtk_action_muxer_action_added (muxer, *it, G_ACTION_GROUP (muxer->parent), *it);
 
765
      g_strfreev (actions);
 
766
 
 
767
      g_signal_connect (muxer->parent, "action-added",
 
768
                        G_CALLBACK (gtk_action_muxer_action_added_to_parent), muxer);
 
769
      g_signal_connect (muxer->parent, "action-removed",
 
770
                        G_CALLBACK (gtk_action_muxer_action_removed_from_parent), muxer);
 
771
      g_signal_connect (muxer->parent, "action-enabled-changed",
 
772
                        G_CALLBACK (gtk_action_muxer_parent_action_enabled_changed), muxer);
 
773
      g_signal_connect (muxer->parent, "action-state-changed",
 
774
                        G_CALLBACK (gtk_action_muxer_parent_action_state_changed), muxer);
 
775
    }
 
776
 
 
777
  g_object_notify_by_pspec (G_OBJECT (muxer), properties[PROP_PARENT]);
 
778
}