diff --git a/src/Makefile.am b/src/Makefile.am index 58e92bd35..e60daf261 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -129,13 +129,13 @@ shell_public_headers_h = \ shell-wm.h \ shell-xfixes-cursor.h -shell_private_sources = \ - gactionmuxer.h \ - gactionmuxer.c \ - gactionobservable.h \ - gactionobservable.c \ - gactionobserver.h \ - gactionobserver.c \ +shell_private_sources = \ + gtkactionmuxer.h \ + gtkactionmuxer.c \ + gtkactionobservable.h \ + gtkactionobservable.c \ + gtkactionobserver.h \ + gtkactionobserver.c \ gtkmenutracker.c \ gtkmenutracker.h \ $(NULL) diff --git a/src/gactionmuxer.c b/src/gactionmuxer.c deleted file mode 100644 index 780fbdd11..000000000 --- a/src/gactionmuxer.c +++ /dev/null @@ -1,546 +0,0 @@ -/* - * Copyright © 2011 Canonical Limited - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - * Author: Ryan Lortie - */ - -#include "config.h" - -#include "gactionmuxer.h" - -#include "gactionobservable.h" -#include "gactionobserver.h" - -#include - -#include - -/* - * SECTION:gactionmuxer - * @short_description: Aggregate and monitor several action groups - * - * #GActionMuxer is a #GActionGroup and #GActionObservable that is - * capable of containing other #GActionGroup instances. - * - * The typical use is aggregating all of the actions applicable to a - * particular context into a single action group, with namespacing. - * - * Consider the case of two action groups -- one containing actions - * applicable to an entire application (such as 'quit') and one - * containing actions applicable to a particular window in the - * application (such as 'fullscreen'). - * - * In this case, each of these action groups could be added to a - * #GActionMuxer with the prefixes "app" and "win", respectively. This - * would expose the actions as "app.quit" and "win.fullscreen" on the - * #GActionGroup interface presented by the #GActionMuxer. - * - * Activations and state change requests on the #GActionMuxer are wired - * through to the underlying action group in the expected way. - * - * This class is typically only used at the site of "consumption" of - * actions (eg: when displaying a menu that contains many actions on - * different objects). - */ - -static void g_action_muxer_group_iface_init (GActionGroupInterface *iface); -static void g_action_muxer_observable_iface_init (GActionObservableInterface *iface); - -typedef GObjectClass GActionMuxerClass; - -struct _GActionMuxer -{ - GObject parent_instance; - - GHashTable *actions; - GHashTable *groups; -}; - -G_DEFINE_TYPE_WITH_CODE (GActionMuxer, g_action_muxer, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_action_muxer_group_iface_init) - G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVABLE, g_action_muxer_observable_iface_init)) - -typedef struct -{ - GActionMuxer *muxer; - GSList *watchers; - gchar *fullname; -} Action; - -typedef struct -{ - GActionMuxer *muxer; - GActionGroup *group; - gchar *prefix; - gulong handler_ids[4]; -} Group; - -static gchar ** -g_action_muxer_list_actions (GActionGroup *action_group) -{ - GActionMuxer *muxer = G_ACTION_MUXER (action_group); - GHashTableIter iter; - gchar *key; - gchar **keys; - gsize i; - - keys = g_new (gchar *, g_hash_table_size (muxer->actions) + 1); - - i = 0; - g_hash_table_iter_init (&iter, muxer->actions); - while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) - keys[i++] = g_strdup (key); - keys[i] = NULL; - - return keys; -} - -static Group * -g_action_muxer_find_group (GActionMuxer *muxer, - const gchar **name) -{ - const gchar *dot; - gchar *prefix; - Group *group; - - dot = strchr (*name, '.'); - - if (!dot) - return NULL; - - prefix = g_strndup (*name, dot - *name); - group = g_hash_table_lookup (muxer->groups, prefix); - g_free (prefix); - - *name = dot + 1; - - return group; -} - -static Action * -g_action_muxer_lookup_action (GActionMuxer *muxer, - const gchar *prefix, - const gchar *action_name, - gchar **fullname) -{ - Action *action; - - *fullname = g_strconcat (prefix, ".", action_name, NULL); - action = g_hash_table_lookup (muxer->actions, *fullname); - - return action; -} - -static void -g_action_muxer_action_enabled_changed (GActionGroup *action_group, - const gchar *action_name, - gboolean enabled, - gpointer user_data) -{ - Group *group = user_data; - gchar *fullname; - Action *action; - GSList *node; - - action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname); - for (node = action ? action->watchers : NULL; node; node = node->next) - g_action_observer_action_enabled_changed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname, enabled); - g_action_group_action_enabled_changed (G_ACTION_GROUP (group->muxer), fullname, enabled); - g_free (fullname); -} - -static void -g_action_muxer_action_state_changed (GActionGroup *action_group, - const gchar *action_name, - GVariant *state, - gpointer user_data) -{ - Group *group = user_data; - gchar *fullname; - Action *action; - GSList *node; - - action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname); - for (node = action ? action->watchers : NULL; node; node = node->next) - g_action_observer_action_state_changed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname, state); - g_action_group_action_state_changed (G_ACTION_GROUP (group->muxer), fullname, state); - g_free (fullname); -} - -static void -g_action_muxer_action_added (GActionGroup *action_group, - const gchar *action_name, - gpointer user_data) -{ - const GVariantType *parameter_type; - Group *group = user_data; - gboolean enabled; - GVariant *state; - - if (g_action_group_query_action (group->group, action_name, &enabled, ¶meter_type, NULL, NULL, &state)) - { - gchar *fullname; - Action *action; - GSList *node; - - action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname); - - for (node = action ? action->watchers : NULL; node; node = node->next) - g_action_observer_action_added (node->data, - G_ACTION_OBSERVABLE (group->muxer), - fullname, parameter_type, enabled, state); - - g_action_group_action_added (G_ACTION_GROUP (group->muxer), fullname); - - if (state) - g_variant_unref (state); - - g_free (fullname); - } -} - -static void -g_action_muxer_action_removed (GActionGroup *action_group, - const gchar *action_name, - gpointer user_data) -{ - Group *group = user_data; - gchar *fullname; - Action *action; - GSList *node; - - action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname); - for (node = action ? action->watchers : NULL; node; node = node->next) - g_action_observer_action_removed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname); - g_action_group_action_removed (G_ACTION_GROUP (group->muxer), fullname); - g_free (fullname); -} - -static gboolean -g_action_muxer_query_action (GActionGroup *action_group, - const gchar *action_name, - gboolean *enabled, - const GVariantType **parameter_type, - const GVariantType **state_type, - GVariant **state_hint, - GVariant **state) -{ - GActionMuxer *muxer = G_ACTION_MUXER (action_group); - Group *group; - - group = g_action_muxer_find_group (muxer, &action_name); - - if (!group) - return FALSE; - - return g_action_group_query_action (group->group, action_name, enabled, - parameter_type, state_type, state_hint, state); -} - -static GVariant * -get_platform_data (void) -{ - gchar time[32]; - GVariantBuilder *builder; - GVariant *result; - - g_snprintf (time, 32, "_TIME%d", clutter_get_current_event_time ()); - - builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); - - g_variant_builder_add (builder, "{sv}", "desktop-startup-id", - g_variant_new_string (time)); - - result = g_variant_builder_end (builder); - g_variant_builder_unref (builder); - - return result; -} - -static void -g_action_muxer_activate_action (GActionGroup *action_group, - const gchar *action_name, - GVariant *parameter) -{ - GActionMuxer *muxer = G_ACTION_MUXER (action_group); - Group *group; - - group = g_action_muxer_find_group (muxer, &action_name); - - if (group) - { - if (G_IS_REMOTE_ACTION_GROUP (group->group)) - g_remote_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (group->group), - action_name, - parameter, - get_platform_data ()); - else - g_action_group_activate_action (group->group, action_name, parameter); - } -} - -static void -g_action_muxer_change_action_state (GActionGroup *action_group, - const gchar *action_name, - GVariant *state) -{ - GActionMuxer *muxer = G_ACTION_MUXER (action_group); - Group *group; - - group = g_action_muxer_find_group (muxer, &action_name); - - if (group) - { - if (G_IS_REMOTE_ACTION_GROUP (group->group)) - g_remote_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (group->group), - action_name, - state, - get_platform_data ()); - else - g_action_group_change_action_state (group->group, action_name, state); - } -} - -static void -g_action_muxer_unregister_internal (Action *action, - gpointer observer) -{ - GActionMuxer *muxer = action->muxer; - GSList **ptr; - - for (ptr = &action->watchers; *ptr; ptr = &(*ptr)->next) - if ((*ptr)->data == observer) - { - *ptr = g_slist_remove (*ptr, observer); - - if (action->watchers == NULL) - { - g_hash_table_remove (muxer->actions, action->fullname); - g_free (action->fullname); - - g_slice_free (Action, action); - - g_object_unref (muxer); - } - - break; - } -} - -static void -g_action_muxer_weak_notify (gpointer data, - GObject *where_the_object_was) -{ - Action *action = data; - - g_action_muxer_unregister_internal (action, where_the_object_was); -} - -static void -g_action_muxer_register_observer (GActionObservable *observable, - const gchar *name, - GActionObserver *observer) -{ - GActionMuxer *muxer = G_ACTION_MUXER (observable); - Action *action; - - action = g_hash_table_lookup (muxer->actions, name); - - if (action == NULL) - { - action = g_slice_new (Action); - action->muxer = g_object_ref (muxer); - action->fullname = g_strdup (name); - action->watchers = NULL; - - g_hash_table_insert (muxer->actions, action->fullname, action); - } - - action->watchers = g_slist_prepend (action->watchers, observer); - g_object_weak_ref (G_OBJECT (observer), g_action_muxer_weak_notify, action); -} - -static void -g_action_muxer_unregister_observer (GActionObservable *observable, - const gchar *name, - GActionObserver *observer) -{ - GActionMuxer *muxer = G_ACTION_MUXER (observable); - Action *action; - - action = g_hash_table_lookup (muxer->actions, name); - g_object_weak_unref (G_OBJECT (observer), g_action_muxer_weak_notify, action); - g_action_muxer_unregister_internal (action, observer); -} - -static void -g_action_muxer_free_group (gpointer data) -{ - Group *group = data; - gint i; - - /* 'for loop' or 'four loop'? */ - for (i = 0; i < 4; i++) - g_signal_handler_disconnect (group->group, group->handler_ids[i]); - - g_object_unref (group->group); - g_free (group->prefix); - - g_slice_free (Group, group); -} - -static void -g_action_muxer_finalize (GObject *object) -{ - GActionMuxer *muxer = G_ACTION_MUXER (object); - - g_assert_cmpint (g_hash_table_size (muxer->actions), ==, 0); - g_hash_table_unref (muxer->actions); - g_hash_table_unref (muxer->groups); - - G_OBJECT_CLASS (g_action_muxer_parent_class) - ->finalize (object); -} - -static void -g_action_muxer_init (GActionMuxer *muxer) -{ - muxer->actions = g_hash_table_new (g_str_hash, g_str_equal); - muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_action_muxer_free_group); -} - -static void -g_action_muxer_observable_iface_init (GActionObservableInterface *iface) -{ - iface->register_observer = g_action_muxer_register_observer; - iface->unregister_observer = g_action_muxer_unregister_observer; -} - -static void -g_action_muxer_group_iface_init (GActionGroupInterface *iface) -{ - iface->list_actions = g_action_muxer_list_actions; - iface->query_action = g_action_muxer_query_action; - iface->activate_action = g_action_muxer_activate_action; - iface->change_action_state = g_action_muxer_change_action_state; -} - -static void -g_action_muxer_class_init (GObjectClass *class) -{ - class->finalize = g_action_muxer_finalize; -} - -/* - * g_action_muxer_insert: - * @muxer: a #GActionMuxer - * @prefix: the prefix string for the action group - * @action_group: a #GActionGroup - * - * Adds the actions in @action_group to the list of actions provided by - * @muxer. @prefix is prefixed to each action name, such that for each - * action x in @action_group, there is an equivalent - * action @prefix.x in @muxer. - * - * For example, if @prefix is "app" and @action_group - * contains an action called "quit", then @muxer will - * now contain an action called "app.quit". - * - * If any #GActionObservers are registered for actions in the group, - * "action_added" notifications will be emitted, as appropriate. - * - * @prefix must not contain a dot ('.'). - */ -void -g_action_muxer_insert (GActionMuxer *muxer, - const gchar *prefix, - GActionGroup *action_group) -{ - gchar **actions; - Group *group; - gint i; - - /* TODO: diff instead of ripout and replace */ - g_action_muxer_remove (muxer, prefix); - - group = g_slice_new (Group); - group->muxer = muxer; - group->group = g_object_ref (action_group); - group->prefix = g_strdup (prefix); - - g_hash_table_insert (muxer->groups, group->prefix, group); - - actions = g_action_group_list_actions (group->group); - for (i = 0; actions[i]; i++) - g_action_muxer_action_added (group->group, actions[i], group); - g_strfreev (actions); - - group->handler_ids[0] = g_signal_connect (group->group, "action-added", - G_CALLBACK (g_action_muxer_action_added), group); - group->handler_ids[1] = g_signal_connect (group->group, "action-removed", - G_CALLBACK (g_action_muxer_action_removed), group); - group->handler_ids[2] = g_signal_connect (group->group, "action-enabled-changed", - G_CALLBACK (g_action_muxer_action_enabled_changed), group); - group->handler_ids[3] = g_signal_connect (group->group, "action-state-changed", - G_CALLBACK (g_action_muxer_action_state_changed), group); -} - -/* - * g_action_muxer_remove: - * @muxer: a #GActionMuxer - * @prefix: the prefix of the action group to remove - * - * Removes a #GActionGroup from the #GActionMuxer. - * - * If any #GActionObservers are registered for actions in the group, - * "action_removed" notifications will be emitted, as appropriate. - */ -void -g_action_muxer_remove (GActionMuxer *muxer, - const gchar *prefix) -{ - Group *group; - - group = g_hash_table_lookup (muxer->groups, prefix); - - if (group != NULL) - { - gchar **actions; - gint i; - - g_hash_table_steal (muxer->groups, prefix); - - actions = g_action_group_list_actions (group->group); - for (i = 0; actions[i]; i++) - g_action_muxer_action_removed (group->group, actions[i], group); - g_strfreev (actions); - - g_action_muxer_free_group (group); - } -} - -/* - * g_action_muxer_new: - * - * Creates a new #GActionMuxer. - */ -GActionMuxer * -g_action_muxer_new (void) -{ - return g_object_new (G_TYPE_ACTION_MUXER, NULL); -} diff --git a/src/gactionmuxer.h b/src/gactionmuxer.h deleted file mode 100644 index adafd03b2..000000000 --- a/src/gactionmuxer.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright © 2011 Canonical Limited - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - * Author: Ryan Lortie - */ - -#ifndef __G_ACTION_MUXER_H__ -#define __G_ACTION_MUXER_H__ - -#include - -G_BEGIN_DECLS - -#define G_TYPE_ACTION_MUXER (g_action_muxer_get_type ()) -#define G_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ - G_TYPE_ACTION_MUXER, GActionMuxer)) -#define G_IS_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ - G_TYPE_ACTION_MUXER)) - -typedef struct _GActionMuxer GActionMuxer; - -G_GNUC_INTERNAL -GType g_action_muxer_get_type (void); -G_GNUC_INTERNAL -GActionMuxer * g_action_muxer_new (void); - -G_GNUC_INTERNAL -void g_action_muxer_insert (GActionMuxer *muxer, - const gchar *prefix, - GActionGroup *group); - -G_GNUC_INTERNAL -void g_action_muxer_remove (GActionMuxer *muxer, - const gchar *prefix); - -G_END_DECLS - -#endif /* __G_ACTION_MUXER_H__ */ diff --git a/src/gactionobservable.c b/src/gactionobservable.c deleted file mode 100644 index 5d1c6526c..000000000 --- a/src/gactionobservable.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright © 2011 Canonical Limited - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2 of the - * licence or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - * - * Authors: Ryan Lortie - */ - -#include "config.h" - -#include "gactionobservable.h" - -G_DEFINE_INTERFACE (GActionObservable, g_action_observable, G_TYPE_OBJECT) - -/* - * SECTION:gactionobserable - * @short_description: an interface implemented by objects that report - * changes to actions - */ - -void -g_action_observable_default_init (GActionObservableInterface *iface) -{ -} - -/* - * g_action_observable_register_observer: - * @observable: a #GActionObservable - * @action_name: the name of the action - * @observer: the #GActionObserver to which the events will be reported - * - * Registers @observer as being interested in changes to @action_name on - * @observable. - */ -void -g_action_observable_register_observer (GActionObservable *observable, - const gchar *action_name, - GActionObserver *observer) -{ - g_return_if_fail (G_IS_ACTION_OBSERVABLE (observable)); - - G_ACTION_OBSERVABLE_GET_IFACE (observable) - ->register_observer (observable, action_name, observer); -} - -/* - * g_action_observable_unregister_observer: - * @observable: a #GActionObservable - * @action_name: the name of the action - * @observer: the #GActionObserver to which the events will be reported - * - * Removes the registration of @observer as being interested in changes - * to @action_name on @observable. - * - * If the observer was registered multiple times, it must be - * unregistered an equal number of times. - */ -void -g_action_observable_unregister_observer (GActionObservable *observable, - const gchar *action_name, - GActionObserver *observer) -{ - g_return_if_fail (G_IS_ACTION_OBSERVABLE (observable)); - - G_ACTION_OBSERVABLE_GET_IFACE (observable) - ->unregister_observer (observable, action_name, observer); -} diff --git a/src/gactionobservable.h b/src/gactionobservable.h deleted file mode 100644 index 1d5d1aa2d..000000000 --- a/src/gactionobservable.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright © 2011 Canonical Limited - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2 of the - * licence or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - * - * Authors: Ryan Lortie - */ - -#ifndef __G_ACTION_OBSERVABLE_H__ -#define __G_ACTION_OBSERVABLE_H__ - -#include "gactionobserver.h" - -G_BEGIN_DECLS - -#define G_TYPE_ACTION_OBSERVABLE (g_action_observable_get_type ()) -#define G_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ - G_TYPE_ACTION_OBSERVABLE, GActionObservable)) -#define G_IS_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ - G_TYPE_ACTION_OBSERVABLE)) -#define G_ACTION_OBSERVABLE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ - G_TYPE_ACTION_OBSERVABLE, GActionObservableInterface)) - -typedef struct _GActionObservableInterface GActionObservableInterface; - -struct _GActionObservableInterface -{ - GTypeInterface g_iface; - - void (* register_observer) (GActionObservable *observable, - const gchar *action_name, - GActionObserver *observer); - void (* unregister_observer) (GActionObservable *observable, - const gchar *action_name, - GActionObserver *observer); -}; - -G_GNUC_INTERNAL -GType g_action_observable_get_type (void); -G_GNUC_INTERNAL -void g_action_observable_register_observer (GActionObservable *observable, - const gchar *action_name, - GActionObserver *observer); -G_GNUC_INTERNAL -void g_action_observable_unregister_observer (GActionObservable *observable, - const gchar *action_name, - GActionObserver *observer); - -G_END_DECLS - -#endif /* __G_ACTION_OBSERVABLE_H__ */ diff --git a/src/gactionobserver.h b/src/gactionobserver.h deleted file mode 100644 index eb15c3ab4..000000000 --- a/src/gactionobserver.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright © 2011 Canonical Limited - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2 of the - * licence or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - * - * Authors: Ryan Lortie - */ - -#ifndef __G_ACTION_OBSERVER_H__ -#define __G_ACTION_OBSERVER_H__ - -#include - -G_BEGIN_DECLS - -#define G_TYPE_ACTION_OBSERVER (g_action_observer_get_type ()) -#define G_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ - G_TYPE_ACTION_OBSERVER, GActionObserver)) -#define G_IS_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ - G_TYPE_ACTION_OBSERVER)) -#define G_ACTION_OBSERVER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ - G_TYPE_ACTION_OBSERVER, GActionObserverInterface)) - -typedef struct _GActionObserverInterface GActionObserverInterface; -typedef struct _GActionObservable GActionObservable; -typedef struct _GActionObserver GActionObserver; - -struct _GActionObserverInterface -{ - GTypeInterface g_iface; - - void (* action_added) (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name, - const GVariantType *parameter_type, - gboolean enabled, - GVariant *state); - void (* action_enabled_changed) (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name, - gboolean enabled); - void (* action_state_changed) (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name, - GVariant *state); - void (* action_removed) (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name); -}; - -G_GNUC_INTERNAL -GType g_action_observer_get_type (void); -G_GNUC_INTERNAL -void g_action_observer_action_added (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name, - const GVariantType *parameter_type, - gboolean enabled, - GVariant *state); -G_GNUC_INTERNAL -void g_action_observer_action_enabled_changed (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name, - gboolean enabled); -G_GNUC_INTERNAL -void g_action_observer_action_state_changed (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name, - GVariant *state); -G_GNUC_INTERNAL -void g_action_observer_action_removed (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name); - -G_END_DECLS - -#endif /* __G_ACTION_OBSERVER_H__ */ diff --git a/src/gtkactionmuxer.c b/src/gtkactionmuxer.c new file mode 100644 index 000000000..4618564ae --- /dev/null +++ b/src/gtkactionmuxer.c @@ -0,0 +1,778 @@ +/* + * Copyright © 2011 Canonical Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Ryan Lortie + */ + +#include "config.h" + +#include "gtkactionmuxer.h" + +#include "gtkactionobservable.h" +#include "gtkactionobserver.h" + +#include + +/** + * SECTION:gtkactionmuxer + * @short_description: Aggregate and monitor several action groups + * + * #GtkActionMuxer is a #GActionGroup and #GtkActionObservable that is + * capable of containing other #GActionGroup instances. + * + * The typical use is aggregating all of the actions applicable to a + * particular context into a single action group, with namespacing. + * + * Consider the case of two action groups -- one containing actions + * applicable to an entire application (such as 'quit') and one + * containing actions applicable to a particular window in the + * application (such as 'fullscreen'). + * + * In this case, each of these action groups could be added to a + * #GtkActionMuxer with the prefixes "app" and "win", respectively. This + * would expose the actions as "app.quit" and "win.fullscreen" on the + * #GActionGroup interface presented by the #GtkActionMuxer. + * + * Activations and state change requests on the #GtkActionMuxer are wired + * through to the underlying action group in the expected way. + * + * This class is typically only used at the site of "consumption" of + * actions (eg: when displaying a menu that contains many actions on + * different objects). + */ + +static void gtk_action_muxer_group_iface_init (GActionGroupInterface *iface); +static void gtk_action_muxer_observable_iface_init (GtkActionObservableInterface *iface); + +typedef GObjectClass GtkActionMuxerClass; + +struct _GtkActionMuxer +{ + GObject parent_instance; + + GHashTable *observed_actions; + GHashTable *groups; + GtkActionMuxer *parent; +}; + +G_DEFINE_TYPE_WITH_CODE (GtkActionMuxer, gtk_action_muxer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, gtk_action_muxer_group_iface_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTION_OBSERVABLE, gtk_action_muxer_observable_iface_init)) + +enum +{ + PROP_0, + PROP_PARENT, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES]; + +typedef struct +{ + GtkActionMuxer *muxer; + GSList *watchers; + gchar *fullname; +} Action; + +typedef struct +{ + GtkActionMuxer *muxer; + GActionGroup *group; + gchar *prefix; + gulong handler_ids[4]; +} Group; + +static void +gtk_action_muxer_append_group_actions (gpointer key, + gpointer value, + gpointer user_data) +{ + const gchar *prefix = key; + Group *group = value; + GArray *actions = user_data; + gchar **group_actions; + gchar **action; + + group_actions = g_action_group_list_actions (group->group); + for (action = group_actions; *action; action++) + { + gchar *fullname; + + fullname = g_strconcat (prefix, ".", *action, NULL); + g_array_append_val (actions, fullname); + } + + g_strfreev (group_actions); +} + +static gchar ** +gtk_action_muxer_list_actions (GActionGroup *action_group) +{ + GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group); + GArray *actions; + + actions = g_array_new (TRUE, FALSE, sizeof (gchar *)); + + for ( ; muxer != NULL; muxer = muxer->parent) + { + g_hash_table_foreach (muxer->groups, + gtk_action_muxer_append_group_actions, + actions); + } + + return (gchar **) g_array_free (actions, FALSE); +} + +static Group * +gtk_action_muxer_find_group (GtkActionMuxer *muxer, + const gchar *full_name, + const gchar **action_name) +{ + const gchar *dot; + gchar *prefix; + Group *group; + + dot = strchr (full_name, '.'); + + if (!dot) + return NULL; + + prefix = g_strndup (full_name, dot - full_name); + group = g_hash_table_lookup (muxer->groups, prefix); + g_free (prefix); + + if (action_name) + *action_name = dot + 1; + + return group; +} + +static void +gtk_action_muxer_action_enabled_changed (GtkActionMuxer *muxer, + const gchar *action_name, + gboolean enabled) +{ + Action *action; + GSList *node; + + action = g_hash_table_lookup (muxer->observed_actions, action_name); + for (node = action ? action->watchers : NULL; node; node = node->next) + gtk_action_observer_action_enabled_changed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name, enabled); + g_action_group_action_enabled_changed (G_ACTION_GROUP (muxer), action_name, enabled); +} + +static void +gtk_action_muxer_group_action_enabled_changed (GActionGroup *action_group, + const gchar *action_name, + gboolean enabled, + gpointer user_data) +{ + Group *group = user_data; + gchar *fullname; + + fullname = g_strconcat (group->prefix, ".", action_name, NULL); + gtk_action_muxer_action_enabled_changed (group->muxer, fullname, enabled); + + g_free (fullname); +} + +static void +gtk_action_muxer_parent_action_enabled_changed (GActionGroup *action_group, + const gchar *action_name, + gboolean enabled, + gpointer user_data) +{ + GtkActionMuxer *muxer = user_data; + + gtk_action_muxer_action_enabled_changed (muxer, action_name, enabled); +} + +static void +gtk_action_muxer_action_state_changed (GtkActionMuxer *muxer, + const gchar *action_name, + GVariant *state) +{ + Action *action; + GSList *node; + + action = g_hash_table_lookup (muxer->observed_actions, action_name); + for (node = action ? action->watchers : NULL; node; node = node->next) + gtk_action_observer_action_state_changed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name, state); + g_action_group_action_state_changed (G_ACTION_GROUP (muxer), action_name, state); +} + +static void +gtk_action_muxer_group_action_state_changed (GActionGroup *action_group, + const gchar *action_name, + GVariant *state, + gpointer user_data) +{ + Group *group = user_data; + gchar *fullname; + + fullname = g_strconcat (group->prefix, ".", action_name, NULL); + gtk_action_muxer_action_state_changed (group->muxer, fullname, state); + + g_free (fullname); +} + +static void +gtk_action_muxer_parent_action_state_changed (GActionGroup *action_group, + const gchar *action_name, + GVariant *state, + gpointer user_data) +{ + GtkActionMuxer *muxer = user_data; + + gtk_action_muxer_action_state_changed (muxer, action_name, state); +} + +static void +gtk_action_muxer_action_added (GtkActionMuxer *muxer, + const gchar *action_name, + GActionGroup *original_group, + const gchar *orignal_action_name) +{ + const GVariantType *parameter_type; + gboolean enabled; + GVariant *state; + Action *action; + + action = g_hash_table_lookup (muxer->observed_actions, action_name); + + if (action && action->watchers && + g_action_group_query_action (original_group, orignal_action_name, + &enabled, ¶meter_type, NULL, NULL, &state)) + { + GSList *node; + + for (node = action->watchers; node; node = node->next) + gtk_action_observer_action_added (node->data, + GTK_ACTION_OBSERVABLE (muxer), + action_name, parameter_type, enabled, state); + + if (state) + g_variant_unref (state); + } + + g_action_group_action_added (G_ACTION_GROUP (muxer), action_name); +} + +static void +gtk_action_muxer_action_added_to_group (GActionGroup *action_group, + const gchar *action_name, + gpointer user_data) +{ + Group *group = user_data; + gchar *fullname; + + fullname = g_strconcat (group->prefix, ".", action_name, NULL); + gtk_action_muxer_action_added (group->muxer, fullname, action_group, action_name); + + g_free (fullname); +} + +static void +gtk_action_muxer_action_added_to_parent (GActionGroup *action_group, + const gchar *action_name, + gpointer user_data) +{ + GtkActionMuxer *muxer = user_data; + + gtk_action_muxer_action_added (muxer, action_name, action_group, action_name); +} + +static void +gtk_action_muxer_action_removed (GtkActionMuxer *muxer, + const gchar *action_name) +{ + Action *action; + GSList *node; + + action = g_hash_table_lookup (muxer->observed_actions, action_name); + for (node = action ? action->watchers : NULL; node; node = node->next) + gtk_action_observer_action_removed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name); + g_action_group_action_removed (G_ACTION_GROUP (muxer), action_name); +} + +static void +gtk_action_muxer_action_removed_from_group (GActionGroup *action_group, + const gchar *action_name, + gpointer user_data) +{ + Group *group = user_data; + gchar *fullname; + + fullname = g_strconcat (group->prefix, ".", action_name, NULL); + gtk_action_muxer_action_removed (group->muxer, fullname); + + g_free (fullname); +} + +static void +gtk_action_muxer_action_removed_from_parent (GActionGroup *action_group, + const gchar *action_name, + gpointer user_data) +{ + GtkActionMuxer *muxer = user_data; + + gtk_action_muxer_action_removed (muxer, action_name); +} + +static gboolean +gtk_action_muxer_query_action (GActionGroup *action_group, + const gchar *action_name, + gboolean *enabled, + const GVariantType **parameter_type, + const GVariantType **state_type, + GVariant **state_hint, + GVariant **state) +{ + GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group); + Group *group; + const gchar *unprefixed_name; + + group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name); + + if (group) + return g_action_group_query_action (group->group, unprefixed_name, enabled, + parameter_type, state_type, state_hint, state); + + if (muxer->parent) + return g_action_group_query_action (G_ACTION_GROUP (muxer->parent), action_name, + enabled, parameter_type, + state_type, state_hint, state); + + return FALSE; +} + +static void +gtk_action_muxer_activate_action (GActionGroup *action_group, + const gchar *action_name, + GVariant *parameter) +{ + GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group); + Group *group; + const gchar *unprefixed_name; + + group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name); + + if (group) + g_action_group_activate_action (group->group, unprefixed_name, parameter); + else if (muxer->parent) + g_action_group_activate_action (G_ACTION_GROUP (muxer->parent), action_name, parameter); +} + +static void +gtk_action_muxer_change_action_state (GActionGroup *action_group, + const gchar *action_name, + GVariant *state) +{ + GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group); + Group *group; + const gchar *unprefixed_name; + + group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name); + + if (group) + g_action_group_change_action_state (group->group, unprefixed_name, state); + else if (muxer->parent) + g_action_group_change_action_state (G_ACTION_GROUP (muxer->parent), action_name, state); +} + +static void +gtk_action_muxer_unregister_internal (Action *action, + gpointer observer) +{ + GtkActionMuxer *muxer = action->muxer; + GSList **ptr; + + for (ptr = &action->watchers; *ptr; ptr = &(*ptr)->next) + if ((*ptr)->data == observer) + { + *ptr = g_slist_remove (*ptr, observer); + + if (action->watchers == NULL) + g_hash_table_remove (muxer->observed_actions, action->fullname); + + break; + } +} + +static void +gtk_action_muxer_weak_notify (gpointer data, + GObject *where_the_object_was) +{ + Action *action = data; + + gtk_action_muxer_unregister_internal (action, where_the_object_was); +} + +static void +gtk_action_muxer_register_observer (GtkActionObservable *observable, + const gchar *name, + GtkActionObserver *observer) +{ + GtkActionMuxer *muxer = GTK_ACTION_MUXER (observable); + Action *action; + + action = g_hash_table_lookup (muxer->observed_actions, name); + + if (action == NULL) + { + action = g_slice_new (Action); + action->muxer = muxer; + action->fullname = g_strdup (name); + action->watchers = NULL; + + g_hash_table_insert (muxer->observed_actions, action->fullname, action); + } + + action->watchers = g_slist_prepend (action->watchers, observer); + g_object_weak_ref (G_OBJECT (observer), gtk_action_muxer_weak_notify, action); +} + +static void +gtk_action_muxer_unregister_observer (GtkActionObservable *observable, + const gchar *name, + GtkActionObserver *observer) +{ + GtkActionMuxer *muxer = GTK_ACTION_MUXER (observable); + Action *action; + + action = g_hash_table_lookup (muxer->observed_actions, name); + g_object_weak_unref (G_OBJECT (observer), gtk_action_muxer_weak_notify, action); + gtk_action_muxer_unregister_internal (action, observer); +} + +static void +gtk_action_muxer_free_group (gpointer data) +{ + Group *group = data; + gint i; + + /* 'for loop' or 'four loop'? */ + for (i = 0; i < 4; i++) + g_signal_handler_disconnect (group->group, group->handler_ids[i]); + + g_object_unref (group->group); + g_free (group->prefix); + + g_slice_free (Group, group); +} + +static void +gtk_action_muxer_free_action (gpointer data) +{ + Action *action = data; + GSList *it; + + for (it = action->watchers; it; it = it->next) + g_object_weak_unref (G_OBJECT (it->data), gtk_action_muxer_weak_notify, action); + + g_slist_free (action->watchers); + g_free (action->fullname); + + g_slice_free (Action, action); +} + +static void +gtk_action_muxer_finalize (GObject *object) +{ + GtkActionMuxer *muxer = GTK_ACTION_MUXER (object); + + g_assert_cmpint (g_hash_table_size (muxer->observed_actions), ==, 0); + g_hash_table_unref (muxer->observed_actions); + g_hash_table_unref (muxer->groups); + + G_OBJECT_CLASS (gtk_action_muxer_parent_class) + ->finalize (object); +} + +static void +gtk_action_muxer_dispose (GObject *object) +{ + GtkActionMuxer *muxer = GTK_ACTION_MUXER (object); + + if (muxer->parent) + { + g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_added_to_parent, muxer); + g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_removed_from_parent, muxer); + g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_enabled_changed, muxer); + g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_state_changed, muxer); + + g_clear_object (&muxer->parent); + } + + g_hash_table_remove_all (muxer->observed_actions); + + G_OBJECT_CLASS (gtk_action_muxer_parent_class) + ->dispose (object); +} + +static void +gtk_action_muxer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GtkActionMuxer *muxer = GTK_ACTION_MUXER (object); + + switch (property_id) + { + case PROP_PARENT: + g_value_set_object (value, gtk_action_muxer_get_parent (muxer)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +gtk_action_muxer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkActionMuxer *muxer = GTK_ACTION_MUXER (object); + + switch (property_id) + { + case PROP_PARENT: + gtk_action_muxer_set_parent (muxer, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +gtk_action_muxer_init (GtkActionMuxer *muxer) +{ + muxer->observed_actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gtk_action_muxer_free_action); + muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gtk_action_muxer_free_group); +} + +static void +gtk_action_muxer_observable_iface_init (GtkActionObservableInterface *iface) +{ + iface->register_observer = gtk_action_muxer_register_observer; + iface->unregister_observer = gtk_action_muxer_unregister_observer; +} + +static void +gtk_action_muxer_group_iface_init (GActionGroupInterface *iface) +{ + iface->list_actions = gtk_action_muxer_list_actions; + iface->query_action = gtk_action_muxer_query_action; + iface->activate_action = gtk_action_muxer_activate_action; + iface->change_action_state = gtk_action_muxer_change_action_state; +} + +static void +gtk_action_muxer_class_init (GObjectClass *class) +{ + class->get_property = gtk_action_muxer_get_property; + class->set_property = gtk_action_muxer_set_property; + class->finalize = gtk_action_muxer_finalize; + class->dispose = gtk_action_muxer_dispose; + + properties[PROP_PARENT] = g_param_spec_object ("parent", "Parent", + "The parent muxer", + GTK_TYPE_ACTION_MUXER, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (class, NUM_PROPERTIES, properties); +} + +/** + * gtk_action_muxer_insert: + * @muxer: a #GtkActionMuxer + * @prefix: the prefix string for the action group + * @action_group: a #GActionGroup + * + * Adds the actions in @action_group to the list of actions provided by + * @muxer. @prefix is prefixed to each action name, such that for each + * action x in @action_group, there is an equivalent + * action @prefix.x in @muxer. + * + * For example, if @prefix is "app" and @action_group + * contains an action called "quit", then @muxer will + * now contain an action called "app.quit". + * + * If any #GtkActionObservers are registered for actions in the group, + * "action_added" notifications will be emitted, as appropriate. + * + * @prefix must not contain a dot ('.'). + */ +void +gtk_action_muxer_insert (GtkActionMuxer *muxer, + const gchar *prefix, + GActionGroup *action_group) +{ + gchar **actions; + Group *group; + gint i; + + /* TODO: diff instead of ripout and replace */ + gtk_action_muxer_remove (muxer, prefix); + + group = g_slice_new (Group); + group->muxer = muxer; + group->group = g_object_ref (action_group); + group->prefix = g_strdup (prefix); + + g_hash_table_insert (muxer->groups, group->prefix, group); + + actions = g_action_group_list_actions (group->group); + for (i = 0; actions[i]; i++) + gtk_action_muxer_action_added_to_group (group->group, actions[i], group); + g_strfreev (actions); + + group->handler_ids[0] = g_signal_connect (group->group, "action-added", + G_CALLBACK (gtk_action_muxer_action_added_to_group), group); + group->handler_ids[1] = g_signal_connect (group->group, "action-removed", + G_CALLBACK (gtk_action_muxer_action_removed_from_group), group); + group->handler_ids[2] = g_signal_connect (group->group, "action-enabled-changed", + G_CALLBACK (gtk_action_muxer_group_action_enabled_changed), group); + group->handler_ids[3] = g_signal_connect (group->group, "action-state-changed", + G_CALLBACK (gtk_action_muxer_group_action_state_changed), group); +} + +/** + * gtk_action_muxer_remove: + * @muxer: a #GtkActionMuxer + * @prefix: the prefix of the action group to remove + * + * Removes a #GActionGroup from the #GtkActionMuxer. + * + * If any #GtkActionObservers are registered for actions in the group, + * "action_removed" notifications will be emitted, as appropriate. + */ +void +gtk_action_muxer_remove (GtkActionMuxer *muxer, + const gchar *prefix) +{ + Group *group; + + group = g_hash_table_lookup (muxer->groups, prefix); + + if (group != NULL) + { + gchar **actions; + gint i; + + g_hash_table_steal (muxer->groups, prefix); + + actions = g_action_group_list_actions (group->group); + for (i = 0; actions[i]; i++) + gtk_action_muxer_action_removed_from_group (group->group, actions[i], group); + g_strfreev (actions); + + gtk_action_muxer_free_group (group); + } +} + +/** + * gtk_action_muxer_new: + * + * Creates a new #GtkActionMuxer. + */ +GtkActionMuxer * +gtk_action_muxer_new (void) +{ + return g_object_new (GTK_TYPE_ACTION_MUXER, NULL); +} + +/** + * gtk_action_muxer_get_parent: + * @muxer: a #GtkActionMuxer + * + * Returns: (transfer none): the parent of @muxer, or NULL. + */ +GtkActionMuxer * +gtk_action_muxer_get_parent (GtkActionMuxer *muxer) +{ + g_return_val_if_fail (GTK_IS_ACTION_MUXER (muxer), NULL); + + return muxer->parent; +} + +/** + * gtk_action_muxer_set_parent: + * @muxer: a #GtkActionMuxer + * @parent: (allow-none): the new parent #GtkActionMuxer + * + * Sets the parent of @muxer to @parent. + */ +void +gtk_action_muxer_set_parent (GtkActionMuxer *muxer, + GtkActionMuxer *parent) +{ + g_return_if_fail (GTK_IS_ACTION_MUXER (muxer)); + g_return_if_fail (parent == NULL || GTK_IS_ACTION_MUXER (parent)); + + if (muxer->parent == parent) + return; + + if (muxer->parent != NULL) + { + gchar **actions; + gchar **it; + + actions = g_action_group_list_actions (G_ACTION_GROUP (muxer->parent)); + for (it = actions; *it; it++) + gtk_action_muxer_action_removed (muxer, *it); + g_strfreev (actions); + + g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_added_to_parent, muxer); + g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_removed_from_parent, muxer); + g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_enabled_changed, muxer); + g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_state_changed, muxer); + + g_object_unref (muxer->parent); + } + + muxer->parent = parent; + + if (muxer->parent != NULL) + { + gchar **actions; + gchar **it; + + g_object_ref (muxer->parent); + + actions = g_action_group_list_actions (G_ACTION_GROUP (muxer->parent)); + for (it = actions; *it; it++) + gtk_action_muxer_action_added (muxer, *it, G_ACTION_GROUP (muxer->parent), *it); + g_strfreev (actions); + + g_signal_connect (muxer->parent, "action-added", + G_CALLBACK (gtk_action_muxer_action_added_to_parent), muxer); + g_signal_connect (muxer->parent, "action-removed", + G_CALLBACK (gtk_action_muxer_action_removed_from_parent), muxer); + g_signal_connect (muxer->parent, "action-enabled-changed", + G_CALLBACK (gtk_action_muxer_parent_action_enabled_changed), muxer); + g_signal_connect (muxer->parent, "action-state-changed", + G_CALLBACK (gtk_action_muxer_parent_action_state_changed), muxer); + } + + g_object_notify_by_pspec (G_OBJECT (muxer), properties[PROP_PARENT]); +} diff --git a/src/gtkactionmuxer.h b/src/gtkactionmuxer.h new file mode 100644 index 000000000..401483072 --- /dev/null +++ b/src/gtkactionmuxer.h @@ -0,0 +1,52 @@ +/* + * Copyright © 2011 Canonical Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Ryan Lortie + */ + +#ifndef __GTK_ACTION_MUXER_H__ +#define __GTK_ACTION_MUXER_H__ + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_ACTION_MUXER (gtk_action_muxer_get_type ()) +#define GTK_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + GTK_TYPE_ACTION_MUXER, GtkActionMuxer)) +#define GTK_IS_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + GTK_TYPE_ACTION_MUXER)) + +typedef struct _GtkActionMuxer GtkActionMuxer; + +GType gtk_action_muxer_get_type (void); +GtkActionMuxer * gtk_action_muxer_new (void); + +void gtk_action_muxer_insert (GtkActionMuxer *muxer, + const gchar *prefix, + GActionGroup *action_group); + +void gtk_action_muxer_remove (GtkActionMuxer *muxer, + const gchar *prefix); + +GtkActionMuxer * gtk_action_muxer_get_parent (GtkActionMuxer *muxer); + +void gtk_action_muxer_set_parent (GtkActionMuxer *muxer, + GtkActionMuxer *parent); + +G_END_DECLS + +#endif /* __GTK_ACTION_MUXER_H__ */ diff --git a/src/gtkactionobservable.c b/src/gtkactionobservable.c new file mode 100644 index 000000000..ab90df2e5 --- /dev/null +++ b/src/gtkactionobservable.c @@ -0,0 +1,78 @@ +/* + * Copyright © 2011 Canonical Limited + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Ryan Lortie + */ + +#include "config.h" + +#include "gtkactionobservable.h" + +G_DEFINE_INTERFACE (GtkActionObservable, gtk_action_observable, G_TYPE_OBJECT) + +/* + * SECTION:gtkactionobserable + * @short_description: an interface implemented by objects that report + * changes to actions + */ + +void +gtk_action_observable_default_init (GtkActionObservableInterface *iface) +{ +} + +/** + * gtk_action_observable_register_observer: + * @observable: a #GtkActionObservable + * @action_name: the name of the action + * @observer: the #GtkActionObserver to which the events will be reported + * + * Registers @observer as being interested in changes to @action_name on + * @observable. + */ +void +gtk_action_observable_register_observer (GtkActionObservable *observable, + const gchar *action_name, + GtkActionObserver *observer) +{ + g_return_if_fail (GTK_IS_ACTION_OBSERVABLE (observable)); + + GTK_ACTION_OBSERVABLE_GET_IFACE (observable) + ->register_observer (observable, action_name, observer); +} + +/** + * gtk_action_observable_unregister_observer: + * @observable: a #GtkActionObservable + * @action_name: the name of the action + * @observer: the #GtkActionObserver to which the events will be reported + * + * Removes the registration of @observer as being interested in changes + * to @action_name on @observable. + * + * If the observer was registered multiple times, it must be + * unregistered an equal number of times. + */ +void +gtk_action_observable_unregister_observer (GtkActionObservable *observable, + const gchar *action_name, + GtkActionObserver *observer) +{ + g_return_if_fail (GTK_IS_ACTION_OBSERVABLE (observable)); + + GTK_ACTION_OBSERVABLE_GET_IFACE (observable) + ->unregister_observer (observable, action_name, observer); +} diff --git a/src/gtkactionobservable.h b/src/gtkactionobservable.h new file mode 100644 index 000000000..aa1514b3c --- /dev/null +++ b/src/gtkactionobservable.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2011 Canonical Limited + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Ryan Lortie + */ + +#ifndef __GTK_ACTION_OBSERVABLE_H__ +#define __GTK_ACTION_OBSERVABLE_H__ + +#include "gtkactionobserver.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_ACTION_OBSERVABLE (gtk_action_observable_get_type ()) +#define GTK_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + GTK_TYPE_ACTION_OBSERVABLE, GtkActionObservable)) +#define GTK_IS_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + GTK_TYPE_ACTION_OBSERVABLE)) +#define GTK_ACTION_OBSERVABLE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ + GTK_TYPE_ACTION_OBSERVABLE, \ + GtkActionObservableInterface)) + +typedef struct _GtkActionObservableInterface GtkActionObservableInterface; + +struct _GtkActionObservableInterface +{ + GTypeInterface g_iface; + + void (* register_observer) (GtkActionObservable *observable, + const gchar *action_name, + GtkActionObserver *observer); + void (* unregister_observer) (GtkActionObservable *observable, + const gchar *action_name, + GtkActionObserver *observer); +}; + +GType gtk_action_observable_get_type (void); +void gtk_action_observable_register_observer (GtkActionObservable *observable, + const gchar *action_name, + GtkActionObserver *observer); +void gtk_action_observable_unregister_observer (GtkActionObservable *observable, + const gchar *action_name, + GtkActionObserver *observer); + +G_END_DECLS + +#endif /* __GTK_ACTION_OBSERVABLE_H__ */ diff --git a/src/gactionobserver.c b/src/gtkactionobserver.c similarity index 60% rename from src/gactionobserver.c rename to src/gtkactionobserver.c index 0586f0773..cf70b20a3 100644 --- a/src/gactionobserver.c +++ b/src/gtkactionobserver.c @@ -1,7 +1,7 @@ /* * Copyright © 2011 Canonical Limited * - * This program is free software: you can redistribute it and/or modify + * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 of the * licence or (at your option) any later version. @@ -12,25 +12,23 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. + * License along with this library. If not, see . * * Authors: Ryan Lortie */ #include "config.h" -#include "gactionobserver.h" +#include "gtkactionobserver.h" -G_DEFINE_INTERFACE (GActionObserver, g_action_observer, G_TYPE_OBJECT) +G_DEFINE_INTERFACE (GtkActionObserver, gtk_action_observer, G_TYPE_OBJECT) /** - * SECTION:gactionobserver + * SECTION:gtkactionobserver * @short_description: an interface implemented by objects that are * interested in monitoring actions for changes * - * GActionObserver is a simple interface allowing objects that wish to + * GtkActionObserver is a simple interface allowing objects that wish to * be notified of changes to actions to be notified of those changes. * * It is also possible to monitor changes to action groups using @@ -52,13 +50,13 @@ G_DEFINE_INTERFACE (GActionObserver, g_action_observer, G_TYPE_OBJECT) */ void -g_action_observer_default_init (GActionObserverInterface *class) +gtk_action_observer_default_init (GtkActionObserverInterface *class) { } -/* - * g_action_observer_action_added: - * @observer: a #GActionObserver +/** + * gtk_action_observer_action_added: + * @observer: a #GtkActionObserver * @observable: the source of the event * @action_name: the name of the action * @enabled: %TRUE if the action is now enabled @@ -74,22 +72,22 @@ g_action_observer_default_init (GActionObserverInterface *class) * observer has explicitly registered itself to receive events. */ void -g_action_observer_action_added (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name, - const GVariantType *parameter_type, - gboolean enabled, - GVariant *state) +gtk_action_observer_action_added (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + const GVariantType *parameter_type, + gboolean enabled, + GVariant *state) { - g_return_if_fail (G_IS_ACTION_OBSERVER (observer)); + g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer)); - G_ACTION_OBSERVER_GET_IFACE (observer) + GTK_ACTION_OBSERVER_GET_IFACE (observer) ->action_added (observer, observable, action_name, parameter_type, enabled, state); } -/* - * g_action_observer_action_enabled_changed: - * @observer: a #GActionObserver +/** + * gtk_action_observer_action_enabled_changed: + * @observer: a #GtkActionObserver * @observable: the source of the event * @action_name: the name of the action * @enabled: %TRUE if the action is now enabled @@ -101,45 +99,45 @@ g_action_observer_action_added (GActionObserver *observer, * observer has explicitly registered itself to receive events. */ void -g_action_observer_action_enabled_changed (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name, - gboolean enabled) +gtk_action_observer_action_enabled_changed (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + gboolean enabled) { - g_return_if_fail (G_IS_ACTION_OBSERVER (observer)); + g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer)); - G_ACTION_OBSERVER_GET_IFACE (observer) + GTK_ACTION_OBSERVER_GET_IFACE (observer) ->action_enabled_changed (observer, observable, action_name, enabled); } -/* - * g_action_observer_action_state_changed: - * @observer: a #GActionObserver +/** + * gtk_action_observer_action_state_changed: + * @observer: a #GtkActionObserver * @observable: the source of the event * @action_name: the name of the action * @state: the new state of the action * * This function is called when an action that the observer is - * registered to receive events for changes its state. + * registered to receive events for changes to its state. * * This function should only be called by objects with which the * observer has explicitly registered itself to receive events. */ void -g_action_observer_action_state_changed (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name, - GVariant *state) +gtk_action_observer_action_state_changed (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + GVariant *state) { - g_return_if_fail (G_IS_ACTION_OBSERVER (observer)); + g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer)); - G_ACTION_OBSERVER_GET_IFACE (observer) + GTK_ACTION_OBSERVER_GET_IFACE (observer) ->action_state_changed (observer, observable, action_name, state); } -/* - * g_action_observer_action_removed: - * @observer: a #GActionObserver +/** + * gtk_action_observer_action_removed: + * @observer: a #GtkActionObserver * @observable: the source of the event * @action_name: the name of the action * @@ -150,12 +148,12 @@ g_action_observer_action_state_changed (GActionObserver *observer, * observer has explicitly registered itself to receive events. */ void -g_action_observer_action_removed (GActionObserver *observer, - GActionObservable *observable, - const gchar *action_name) +gtk_action_observer_action_removed (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name) { - g_return_if_fail (G_IS_ACTION_OBSERVER (observer)); + g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer)); - G_ACTION_OBSERVER_GET_IFACE (observer) + GTK_ACTION_OBSERVER_GET_IFACE (observer) ->action_removed (observer, observable, action_name); } diff --git a/src/gtkactionobserver.h b/src/gtkactionobserver.h new file mode 100644 index 000000000..83629a7e4 --- /dev/null +++ b/src/gtkactionobserver.h @@ -0,0 +1,83 @@ +/* + * Copyright © 2011 Canonical Limited + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Ryan Lortie + */ + +#ifndef __GTK_ACTION_OBSERVER_H__ +#define __GTK_ACTION_OBSERVER_H__ + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_ACTION_OBSERVER (gtk_action_observer_get_type ()) +#define GTK_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + GTK_TYPE_ACTION_OBSERVER, GtkActionObserver)) +#define GTK_IS_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + GTK_TYPE_ACTION_OBSERVER)) +#define GTK_ACTION_OBSERVER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ + GTK_TYPE_ACTION_OBSERVER, GtkActionObserverInterface)) + +typedef struct _GtkActionObserverInterface GtkActionObserverInterface; +typedef struct _GtkActionObservable GtkActionObservable; +typedef struct _GtkActionObserver GtkActionObserver; + +struct _GtkActionObserverInterface +{ + GTypeInterface g_iface; + + void (* action_added) (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + const GVariantType *parameter_type, + gboolean enabled, + GVariant *state); + void (* action_enabled_changed) (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + gboolean enabled); + void (* action_state_changed) (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + GVariant *state); + void (* action_removed) (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name); +}; + +GType gtk_action_observer_get_type (void); +void gtk_action_observer_action_added (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + const GVariantType *parameter_type, + gboolean enabled, + GVariant *state); +void gtk_action_observer_action_enabled_changed (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + gboolean enabled); +void gtk_action_observer_action_state_changed (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + GVariant *state); +void gtk_action_observer_action_removed (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name); + +G_END_DECLS + +#endif /* __GTK_ACTION_OBSERVER_H__ */ diff --git a/src/shell-app.c b/src/shell-app.c index 9980d3989..6afa56a34 100644 --- a/src/shell-app.c +++ b/src/shell-app.c @@ -15,7 +15,7 @@ #include "shell-app-system-private.h" #include "shell-window-tracker-private.h" #include "st.h" -#include "gactionmuxer.h" +#include "gtkactionmuxer.h" typedef enum { MATCH_NONE, @@ -44,7 +44,7 @@ typedef struct { /* See GApplication documentation */ GDBusMenuModel *remote_menu; - GActionMuxer *muxer; + GtkActionMuxer *muxer; char *unique_bus_name; GDBusConnection *session; } ShellAppRunningState; @@ -565,9 +565,9 @@ shell_app_update_window_actions (ShellApp *app, MetaWindow *window) } if (!app->running_state->muxer) - app->running_state->muxer = g_action_muxer_new (); + app->running_state->muxer = gtk_action_muxer_new (); - g_action_muxer_insert (app->running_state->muxer, "win", actions); + gtk_action_muxer_insert (app->running_state->muxer, "win", actions); g_object_notify (G_OBJECT (app), "action-group"); } } @@ -1303,7 +1303,7 @@ create_running_state (ShellApp *app) app->running_state->session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); g_assert (app->running_state->session != NULL); - app->running_state->muxer = g_action_muxer_new (); + app->running_state->muxer = gtk_action_muxer_new (); } void @@ -1342,7 +1342,7 @@ shell_app_update_app_menu (ShellApp *app, g_clear_object (&app->running_state->remote_menu); app->running_state->remote_menu = g_dbus_menu_model_get (app->running_state->session, unique_bus_name, app_menu_object_path); actions = g_dbus_action_group_get (app->running_state->session, unique_bus_name, application_object_path); - g_action_muxer_insert (app->running_state->muxer, "app", G_ACTION_GROUP (actions)); + gtk_action_muxer_insert (app->running_state->muxer, "app", G_ACTION_GROUP (actions)); g_object_unref (actions); } }