]> Pileus Git - ~andy/gtk/blob - gtk/gactionmuxer.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gactionmuxer.c
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 "gactionmuxer.h"
23
24 #include "gactionobservable.h"
25 #include "gactionobserver.h"
26
27 #include <string.h>
28
29 /*
30  * SECTION:gactionmuxer
31  * @short_description: Aggregate and monitor several action groups
32  *
33  * #GActionMuxer is a #GActionGroup and #GActionObservable 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  * #GActionMuxer 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 #GActionMuxer.
48  *
49  * Activations and state change requests on the #GActionMuxer 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     g_action_muxer_group_iface_init         (GActionGroupInterface      *iface);
58 static void     g_action_muxer_observable_iface_init    (GActionObservableInterface *iface);
59
60 typedef GObjectClass GActionMuxerClass;
61
62 struct _GActionMuxer
63 {
64   GObject parent_instance;
65
66   GHashTable *observed_actions;
67   GHashTable *groups;
68   GActionMuxer *parent;
69 };
70
71 G_DEFINE_TYPE_WITH_CODE (GActionMuxer, g_action_muxer, G_TYPE_OBJECT,
72                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_action_muxer_group_iface_init)
73                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVABLE, g_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   GActionMuxer *muxer;
87   GSList       *watchers;
88   gchar        *fullname;
89 } Action;
90
91 typedef struct
92 {
93   GActionMuxer *muxer;
94   GActionGroup *group;
95   gchar        *prefix;
96   gulong        handler_ids[4];
97 } Group;
98
99 static void
100 g_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 g_action_muxer_list_actions (GActionGroup *action_group)
124 {
125   GActionMuxer *muxer = G_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                             g_action_muxer_append_group_actions,
134                             actions);
135     }
136
137   return (gchar **) g_array_free (actions, FALSE);
138 }
139
140 static Group *
141 g_action_muxer_find_group (GActionMuxer  *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 g_action_muxer_action_enabled_changed (GActionMuxer *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     g_action_observer_action_enabled_changed (node->data, G_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 g_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   g_action_muxer_action_enabled_changed (group->muxer, fullname, enabled);
189
190   g_free (fullname);
191 }
192
193 static void
194 g_action_muxer_parent_action_enabled_changed (GActionGroup *action_group,
195                                               const gchar  *action_name,
196                                               gboolean      enabled,
197                                               gpointer      user_data)
198 {
199   GActionMuxer *muxer = user_data;
200
201   g_action_muxer_action_enabled_changed (muxer, action_name, enabled);
202 }
203
204 static void
205 g_action_muxer_action_state_changed (GActionMuxer *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     g_action_observer_action_state_changed (node->data, G_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 g_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   g_action_muxer_action_state_changed (group->muxer, fullname, state);
229
230   g_free (fullname);
231 }
232
233 static void
234 g_action_muxer_parent_action_state_changed (GActionGroup *action_group,
235                                             const gchar  *action_name,
236                                             GVariant     *state,
237                                             gpointer      user_data)
238 {
239   GActionMuxer *muxer = user_data;
240
241   g_action_muxer_action_state_changed (muxer, action_name, state);
242 }
243
244 static void
245 g_action_muxer_action_added (GActionMuxer *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         g_action_observer_action_added (node->data,
265                                         G_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 g_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   g_action_muxer_action_added (group->muxer, fullname, action_group, action_name);
285
286   g_free (fullname);
287 }
288
289 static void
290 g_action_muxer_action_added_to_parent (GActionGroup *action_group,
291                                        const gchar  *action_name,
292                                        gpointer      user_data)
293 {
294   GActionMuxer *muxer = user_data;
295
296   g_action_muxer_action_added (muxer, action_name, action_group, action_name);
297 }
298
299 static void
300 g_action_muxer_action_removed (GActionMuxer *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     g_action_observer_action_removed (node->data, G_ACTION_OBSERVABLE (muxer), action_name);
309   g_action_group_action_removed (G_ACTION_GROUP (muxer), action_name);
310 }
311
312 static void
313 g_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   g_action_muxer_action_removed (group->muxer, fullname);
322
323   g_free (fullname);
324 }
325
326 static void
327 g_action_muxer_action_removed_from_parent (GActionGroup *action_group,
328                                            const gchar  *action_name,
329                                            gpointer      user_data)
330 {
331   GActionMuxer *muxer = user_data;
332
333   g_action_muxer_action_removed (muxer, action_name);
334 }
335
336 static gboolean
337 g_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   GActionMuxer *muxer = G_ACTION_MUXER (action_group);
346   Group *group;
347   const gchar *unprefixed_name;
348
349   group = g_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 g_action_muxer_activate_action (GActionGroup *action_group,
365                                 const gchar  *action_name,
366                                 GVariant     *parameter)
367 {
368   GActionMuxer *muxer = G_ACTION_MUXER (action_group);
369   Group *group;
370   const gchar *unprefixed_name;
371
372   group = g_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 g_action_muxer_change_action_state (GActionGroup *action_group,
382                                     const gchar  *action_name,
383                                     GVariant     *state)
384 {
385   GActionMuxer *muxer = G_ACTION_MUXER (action_group);
386   Group *group;
387   const gchar *unprefixed_name;
388
389   group = g_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 g_action_muxer_unregister_internal (Action   *action,
399                                     gpointer  observer)
400 {
401   GActionMuxer *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 g_action_muxer_weak_notify (gpointer  data,
418                             GObject  *where_the_object_was)
419 {
420   Action *action = data;
421
422   g_action_muxer_unregister_internal (action, where_the_object_was);
423 }
424
425 static void
426 g_action_muxer_register_observer (GActionObservable *observable,
427                                   const gchar       *name,
428                                   GActionObserver   *observer)
429 {
430   GActionMuxer *muxer = G_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), g_action_muxer_weak_notify, action);
447 }
448
449 static void
450 g_action_muxer_unregister_observer (GActionObservable *observable,
451                                     const gchar       *name,
452                                     GActionObserver   *observer)
453 {
454   GActionMuxer *muxer = G_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), g_action_muxer_weak_notify, action);
459   g_action_muxer_unregister_internal (action, observer);
460 }
461
462 static void
463 g_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 g_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), g_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 g_action_muxer_finalize (GObject *object)
495 {
496   GActionMuxer *muxer = G_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 (g_action_muxer_parent_class)
503     ->finalize (object);
504 }
505
506 static void
507 g_action_muxer_dispose (GObject *object)
508 {
509   GActionMuxer *muxer = G_ACTION_MUXER (object);
510
511   if (muxer->parent)
512   {
513     g_signal_handlers_disconnect_by_func (muxer->parent, g_action_muxer_action_added_to_parent, muxer);
514     g_signal_handlers_disconnect_by_func (muxer->parent, g_action_muxer_action_removed_from_parent, muxer);
515     g_signal_handlers_disconnect_by_func (muxer->parent, g_action_muxer_parent_action_enabled_changed, muxer);
516     g_signal_handlers_disconnect_by_func (muxer->parent, g_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 (g_action_muxer_parent_class)
524     ->dispose (object);
525 }
526
527 static void
528 g_action_muxer_get_property (GObject    *object,
529                              guint       property_id,
530                              GValue     *value,
531                              GParamSpec *pspec)
532 {
533   GActionMuxer *muxer = G_ACTION_MUXER (object);
534
535   switch (property_id)
536     {
537     case PROP_PARENT:
538       g_value_set_object (value, g_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 g_action_muxer_set_property (GObject      *object,
548                              guint         property_id,
549                              const GValue *value,
550                              GParamSpec   *pspec)
551 {
552   GActionMuxer *muxer = G_ACTION_MUXER (object);
553
554   switch (property_id)
555     {
556     case PROP_PARENT:
557       g_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 g_action_muxer_init (GActionMuxer *muxer)
567 {
568   muxer->observed_actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_action_muxer_free_action);
569   muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_action_muxer_free_group);
570 }
571
572 static void
573 g_action_muxer_observable_iface_init (GActionObservableInterface *iface)
574 {
575   iface->register_observer = g_action_muxer_register_observer;
576   iface->unregister_observer = g_action_muxer_unregister_observer;
577 }
578
579 static void
580 g_action_muxer_group_iface_init (GActionGroupInterface *iface)
581 {
582   iface->list_actions = g_action_muxer_list_actions;
583   iface->query_action = g_action_muxer_query_action;
584   iface->activate_action = g_action_muxer_activate_action;
585   iface->change_action_state = g_action_muxer_change_action_state;
586 }
587
588 static void
589 g_action_muxer_class_init (GObjectClass *class)
590 {
591   class->get_property = g_action_muxer_get_property;
592   class->set_property = g_action_muxer_set_property;
593   class->finalize = g_action_muxer_finalize;
594   class->dispose = g_action_muxer_dispose;
595
596   properties[PROP_PARENT] = g_param_spec_object ("parent", "Parent",
597                                                  "The parent muxer",
598                                                  G_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  * g_action_muxer_insert:
607  * @muxer: a #GActionMuxer
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 #GActionObservers 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 g_action_muxer_insert (GActionMuxer *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   g_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     g_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 (g_action_muxer_action_added_to_group), group);
651   group->handler_ids[1] = g_signal_connect (group->group, "action-removed",
652                                             G_CALLBACK (g_action_muxer_action_removed_from_group), group);
653   group->handler_ids[2] = g_signal_connect (group->group, "action-enabled-changed",
654                                             G_CALLBACK (g_action_muxer_group_action_enabled_changed), group);
655   group->handler_ids[3] = g_signal_connect (group->group, "action-state-changed",
656                                             G_CALLBACK (g_action_muxer_group_action_state_changed), group);
657 }
658
659 /*
660  * g_action_muxer_remove:
661  * @muxer: a #GActionMuxer
662  * @prefix: the prefix of the action group to remove
663  *
664  * Removes a #GActionGroup from the #GActionMuxer.
665  *
666  * If any #GActionObservers are registered for actions in the group,
667  * "action_removed" notifications will be emitted, as appropriate.
668  */
669 void
670 g_action_muxer_remove (GActionMuxer *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         g_action_muxer_action_removed_from_group (group->group, actions[i], group);
687       g_strfreev (actions);
688
689       g_action_muxer_free_group (group);
690     }
691 }
692
693 /*
694  * g_action_muxer_new:
695  *
696  * Creates a new #GActionMuxer.
697  */
698 GActionMuxer *
699 g_action_muxer_new (void)
700 {
701   return g_object_new (G_TYPE_ACTION_MUXER, NULL);
702 }
703
704 /* g_action_muxer_get_parent:
705  * @muxer: a #GActionMuxer
706  *
707  * Returns: (transfer-none): the parent of @muxer, or NULL.
708  */
709 GActionMuxer *
710 g_action_muxer_get_parent (GActionMuxer *muxer)
711 {
712   g_return_val_if_fail (G_IS_ACTION_MUXER (muxer), NULL);
713
714   return muxer->parent;
715 }
716
717 /* g_action_muxer_set_parent:
718  * @muxer: a #GActionMuxer
719  * @parent: (allow-none): the new parent #GActionMuxer
720  *
721  * Sets the parent of @muxer to @parent.
722  */
723 void
724 g_action_muxer_set_parent (GActionMuxer *muxer,
725                            GActionMuxer *parent)
726 {
727   g_return_if_fail (G_IS_ACTION_MUXER (muxer));
728   g_return_if_fail (parent == NULL || G_IS_ACTION_MUXER (parent));
729
730   if (muxer->parent == parent)
731     return;
732
733   if (muxer->parent != NULL)
734     {
735       gchar **actions;
736       gchar **it;
737
738       actions = g_action_group_list_actions (G_ACTION_GROUP (muxer->parent));
739       for (it = actions; *it; it++)
740         g_action_muxer_action_removed (muxer, *it);
741       g_strfreev (actions);
742
743       g_signal_handlers_disconnect_by_func (muxer->parent, g_action_muxer_action_added_to_parent, muxer);
744       g_signal_handlers_disconnect_by_func (muxer->parent, g_action_muxer_action_removed_from_parent, muxer);
745       g_signal_handlers_disconnect_by_func (muxer->parent, g_action_muxer_parent_action_enabled_changed, muxer);
746       g_signal_handlers_disconnect_by_func (muxer->parent, g_action_muxer_parent_action_state_changed, muxer);
747
748       g_object_unref (muxer->parent);
749     }
750
751   muxer->parent = parent;
752
753   if (muxer->parent != NULL)
754     {
755       gchar **actions;
756       gchar **it;
757
758       g_object_ref (muxer->parent);
759
760       actions = g_action_group_list_actions (G_ACTION_GROUP (muxer->parent));
761       for (it = actions; *it; it++)
762         g_action_muxer_action_added (muxer, *it, G_ACTION_GROUP (muxer->parent), *it);
763       g_strfreev (actions);
764
765       g_signal_connect (muxer->parent, "action-added",
766                         G_CALLBACK (g_action_muxer_action_added_to_parent), muxer);
767       g_signal_connect (muxer->parent, "action-removed",
768                         G_CALLBACK (g_action_muxer_action_removed_from_parent), muxer);
769       g_signal_connect (muxer->parent, "action-enabled-changed",
770                         G_CALLBACK (g_action_muxer_parent_action_enabled_changed), muxer);
771       g_signal_connect (muxer->parent, "action-state-changed",
772                         G_CALLBACK (g_action_muxer_parent_action_state_changed), muxer);
773     }
774
775   g_object_notify_by_pspec (G_OBJECT (muxer), properties[PROP_PARENT]);
776 }