diff options
author | Sebastien Bacher <seb128@ubuntu.com> | 2012-08-21 16:54:10 +0200 |
---|---|---|
committer | Sebastien Bacher <seb128@ubuntu.com> | 2012-08-21 16:54:10 +0200 |
commit | 9d8a4eeaf93b5f5537558e925a1c4f9b16e772b7 (patch) | |
tree | f801c95c81b320abcb8d3b9f6b6a2980001f65ec /src/gactionmuxer.c | |
parent | 06d8dda36decf33bc41e638ac8d59abed9abafbb (diff) | |
parent | e3c2920cf5d1f08466bc93a79a9642525eec838e (diff) | |
download | ayatana-indicator-messages-9d8a4eeaf93b5f5537558e925a1c4f9b16e772b7.tar.gz ayatana-indicator-messages-9d8a4eeaf93b5f5537558e925a1c4f9b16e772b7.tar.bz2 ayatana-indicator-messages-9d8a4eeaf93b5f5537558e925a1c4f9b16e772b7.zip |
Import upstream version 12.10.0
Diffstat (limited to 'src/gactionmuxer.c')
-rw-r--r-- | src/gactionmuxer.c | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/src/gactionmuxer.c b/src/gactionmuxer.c new file mode 100644 index 0000000..2b1d11a --- /dev/null +++ b/src/gactionmuxer.c @@ -0,0 +1,485 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Lars Uebernickel <lars.uebernickel@canonical.com> + * Ryan Lortie <desrt@desrt.ca> + */ + +#include "gactionmuxer.h" + +#include <string.h> + +/* + * SECTION:gactionmuxer + * @short_description: Aggregate several action groups + * + * #GActionMuxer is a #GActionGroup 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. + */ + +typedef GObjectClass GActionMuxerClass; + +struct _GActionMuxer +{ + GObject parent; + GActionGroup *global_actions; + GHashTable *groups; /* prefix -> subgroup */ + GHashTable *reverse; /* subgroup -> prefix */ +}; + + +static void g_action_muxer_group_init (GActionGroupInterface *iface); +static void g_action_muxer_dispose (GObject *object); +static void g_action_muxer_finalize (GObject *object); +static void g_action_muxer_disconnect_group (GActionMuxer *muxer, + GActionGroup *subgroup); +static gchar ** g_action_muxer_list_actions (GActionGroup *group); +static void g_action_muxer_activate_action (GActionGroup *group, + const gchar *action_name, + GVariant *parameter); +static void g_action_muxer_change_action_state (GActionGroup *group, + const gchar *action_name, + GVariant *value); +static gboolean g_action_muxer_query_action (GActionGroup *group, + const gchar *action_name, + gboolean *enabled, + const GVariantType **parameter_type, + const GVariantType **state_type, + GVariant **state_hint, + GVariant **state); +static void g_action_muxer_action_added (GActionGroup *group, + gchar *action_name, + gpointer user_data); +static void g_action_muxer_action_removed (GActionGroup *group, + gchar *action_name, + gpointer user_data); +static void g_action_muxer_action_state_changed (GActionGroup *group, + gchar *action_name, + GVariant *value, + gpointer user_data); +static void g_action_muxer_action_enabled_changed (GActionGroup *group, + gchar *action_name, + gboolean enabled, + gpointer user_data); + +G_DEFINE_TYPE_WITH_CODE (GActionMuxer, g_action_muxer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_action_muxer_group_init)); + + +static void +g_action_muxer_class_init (GObjectClass *klass) +{ + klass->dispose = g_action_muxer_dispose; + klass->finalize = g_action_muxer_finalize; +} + +static void +g_action_muxer_init (GActionMuxer *muxer) +{ + muxer->global_actions = NULL; + muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + muxer->reverse = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +g_action_muxer_group_init (GActionGroupInterface *iface) +{ + iface->list_actions = g_action_muxer_list_actions; + iface->activate_action = g_action_muxer_activate_action; + iface->change_action_state = g_action_muxer_change_action_state; + iface->query_action = g_action_muxer_query_action; +} + +static void +g_action_muxer_dispose (GObject *object) +{ + GActionMuxer *muxer = G_ACTION_MUXER (object); + GHashTableIter it; + GActionGroup *subgroup; + + if (muxer->global_actions) + { + g_action_muxer_disconnect_group (muxer, muxer->global_actions); + g_clear_object (&muxer->global_actions); + } + + g_hash_table_iter_init (&it, muxer->groups); + while (g_hash_table_iter_next (&it, NULL, (gpointer *) &subgroup)) + g_action_muxer_disconnect_group (muxer, subgroup); + + g_hash_table_remove_all (muxer->groups); + g_hash_table_remove_all (muxer->reverse); +} + +static void +g_action_muxer_finalize (GObject *object) +{ + GActionMuxer *muxer = G_ACTION_MUXER (object); + + g_hash_table_unref (muxer->groups); + g_hash_table_unref (muxer->reverse); + + G_OBJECT_CLASS (g_action_muxer_parent_class)->finalize (object); +} + +static GActionGroup * +g_action_muxer_lookup_group (GActionMuxer *muxer, + const gchar *full_name, + const gchar **action_name) +{ + const gchar *sep; + GActionGroup *group; + + sep = strchr (full_name, '.'); + + if (sep) + { + gchar *prefix; + prefix = g_strndup (full_name, sep - full_name); + group = g_hash_table_lookup (muxer->groups, prefix); + g_free (prefix); + if (action_name) + *action_name = sep + 1; + } + else + { + group = muxer->global_actions; + if (action_name) + *action_name = full_name; + } + + return group; +} + +static gchar * +g_action_muxer_lookup_full_name (GActionMuxer *muxer, + GActionGroup *subgroup, + const gchar *action_name) +{ + gpointer prefix; + + if (subgroup == muxer->global_actions) + return g_strdup (action_name); + + if (g_hash_table_lookup_extended (muxer->reverse, subgroup, NULL, &prefix)) + return g_strdup_printf ("%s.%s", (gchar *) prefix, action_name); + + return NULL; +} + +static void +g_action_muxer_disconnect_group (GActionMuxer *muxer, + GActionGroup *subgroup) +{ + gchar **actions; + gchar **action; + + actions = g_action_group_list_actions (subgroup); + for (action = actions; *action; action++) + g_action_muxer_action_removed (subgroup, *action, muxer); + g_strfreev (actions); + + g_signal_handlers_disconnect_by_func (subgroup, g_action_muxer_action_added, muxer); + g_signal_handlers_disconnect_by_func (subgroup, g_action_muxer_action_removed, muxer); + g_signal_handlers_disconnect_by_func (subgroup, g_action_muxer_action_enabled_changed, muxer); + g_signal_handlers_disconnect_by_func (subgroup, g_action_muxer_action_state_changed, muxer); +} + +static gchar ** +g_action_muxer_list_actions (GActionGroup *group) +{ + GActionMuxer *muxer = G_ACTION_MUXER (group); + GHashTableIter it; + GArray *all_actions; + gchar *prefix; + GActionGroup *subgroup; + gchar **actions; + gchar **a; + + all_actions = g_array_sized_new (TRUE, FALSE, sizeof (gchar *), 8); + + if (muxer->global_actions) + { + actions = g_action_group_list_actions (muxer->global_actions); + for (a = actions; *a; a++) + { + gchar *name = g_strdup (*a); + g_array_append_val (all_actions, name); + } + g_strfreev (actions); + } + + g_hash_table_iter_init (&it, muxer->groups); + while (g_hash_table_iter_next (&it, (gpointer *) &prefix, (gpointer *) &subgroup)) + { + actions = g_action_group_list_actions (subgroup); + for (a = actions; *a; a++) + { + gchar *full_name = g_strdup_printf ("%s.%s", prefix, *a); + g_array_append_val (all_actions, full_name); + } + g_strfreev (actions); + } + + return (gchar **) g_array_free (all_actions, FALSE); +} + +static void +g_action_muxer_activate_action (GActionGroup *group, + const gchar *action_name, + GVariant *parameter) +{ + GActionMuxer *muxer = G_ACTION_MUXER (group); + GActionGroup *subgroup; + const gchar *action; + + g_return_if_fail (action_name != NULL); + + subgroup = g_action_muxer_lookup_group (muxer, action_name, &action); + + if (subgroup) + g_action_group_activate_action (subgroup, action, parameter); +} + +static void +g_action_muxer_change_action_state (GActionGroup *group, + const gchar *action_name, + GVariant *value) +{ + GActionMuxer *muxer = G_ACTION_MUXER (group); + GActionGroup *subgroup; + const gchar *action; + + g_return_if_fail (action_name != NULL); + + subgroup = g_action_muxer_lookup_group (muxer, action_name, &action); + + if (subgroup) + g_action_group_change_action_state (subgroup, action, value); +} + +static gboolean +g_action_muxer_query_action (GActionGroup *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 (group); + GActionGroup *subgroup; + const gchar *action; + + g_return_val_if_fail (action_name != NULL, FALSE); + + subgroup = g_action_muxer_lookup_group (muxer, action_name, &action); + + if (!subgroup) + return FALSE; + + return g_action_group_query_action (subgroup, action, enabled, parameter_type, + state_type, state_hint, state); +} + +static void +g_action_muxer_action_added (GActionGroup *group, + gchar *action_name, + gpointer user_data) +{ + GActionMuxer *muxer = user_data; + gchar *full_name; + + full_name = g_action_muxer_lookup_full_name (muxer, group, action_name); + + if (full_name) + { + g_action_group_action_added (G_ACTION_GROUP (muxer), full_name); + g_free (full_name); + } +} + +static void +g_action_muxer_action_removed (GActionGroup *group, + gchar *action_name, + gpointer user_data) +{ + GActionMuxer *muxer = user_data; + gchar *full_name; + + full_name = g_action_muxer_lookup_full_name (muxer, group, action_name); + + if (full_name) + { + g_action_group_action_removed (G_ACTION_GROUP (muxer), full_name); + g_free (full_name); + } +} + +static void +g_action_muxer_action_state_changed (GActionGroup *group, + gchar *action_name, + GVariant *value, + gpointer user_data) +{ + GActionMuxer *muxer = user_data; + gchar *full_name; + + full_name = g_action_muxer_lookup_full_name (muxer, group, action_name); + + if (full_name) + { + g_action_group_action_state_changed (G_ACTION_GROUP (muxer), full_name, value); + g_free (full_name); + } +} + +static void +g_action_muxer_action_enabled_changed (GActionGroup *group, + gchar *action_name, + gboolean enabled, + gpointer user_data) +{ + GActionMuxer *muxer = user_data; + gchar *full_name; + + full_name = g_action_muxer_lookup_full_name (muxer, group, action_name); + + if (full_name) + { + g_action_group_action_enabled_changed (G_ACTION_GROUP (muxer), full_name, enabled); + g_free (full_name); + } +} + +/* + * g_action_muxer_new: + * + * Creates a new #GActionMuxer. + */ +GActionMuxer * +g_action_muxer_new (void) +{ + return g_object_new (G_TYPE_ACTION_MUXER, NULL); +} + +/* + * g_action_muxer_insert: + * @muxer: a #GActionMuxer + * @prefix: (allow-none): the prefix string for the action group, or NULL + * @group: (allow-none): a #GActionGroup, or NULL + * + * Adds the actions in @group to the list of actions provided by @muxer. + * @prefix is prefixed to each action name, such that for each action + * <varname>x</varname> in @group, there is an equivalent action + * @prefix<literal>.</literal><varname>x</varname> in @muxer. + * + * For example, if @prefix is "<literal>app</literal>" and @group contains an + * action called "<literal>quit</literal>", then @muxer will now contain an + * action called "<literal>app.quit</literal>". + * + * If @prefix is <literal>NULL</literal>, the actions in @group will be added + * to @muxer without prefix. + * + * If @group is <literal>NULL</literal>, this function has the same effect as + * calling g_action_muxer_remove() with @prefix. + * + * There may only be one group per prefix (including the + * <literal>NULL</literal>-prefix). If a group has been added with @prefix in + * a previous call to this function, it will be removed. + * + * @prefix must not contain a dot ('.'). + */ +void +g_action_muxer_insert (GActionMuxer *muxer, + const gchar *prefix, + GActionGroup *group) +{ + gchar *prefix_copy; + gchar **actions; + gchar **action; + + g_return_if_fail (G_IS_ACTION_MUXER (muxer)); + g_return_if_fail (G_IS_ACTION_GROUP (group)); + + g_action_muxer_remove (muxer, prefix); + + if (prefix) + { + prefix_copy = g_strdup (prefix); + g_hash_table_insert (muxer->groups, prefix_copy, g_object_ref (group)); + g_hash_table_insert (muxer->reverse, group, prefix_copy); + } + else + muxer->global_actions = g_object_ref (group); + + actions = g_action_group_list_actions (group); + for (action = actions; *action; action++) + g_action_muxer_action_added (group, *action, muxer); + g_strfreev (actions); + + g_signal_connect (group, "action-added", G_CALLBACK (g_action_muxer_action_added), muxer); + g_signal_connect (group, "action-removed", G_CALLBACK (g_action_muxer_action_removed), muxer); + g_signal_connect (group, "action-enabled-changed", G_CALLBACK (g_action_muxer_action_enabled_changed), muxer); + g_signal_connect (group, "action-state-changed", G_CALLBACK (g_action_muxer_action_state_changed), muxer); +} + +/* + * g_action_muxer_remove: + * @muxer: a #GActionMuxer + * @prefix: (allow-none): the prefix of the action group to remove, or NULL + * + * Removes a #GActionGroup from the #GActionMuxer. + */ +void +g_action_muxer_remove (GActionMuxer *muxer, + const gchar *prefix) +{ + GActionGroup *subgroup; + + g_return_if_fail (G_IS_ACTION_MUXER (muxer)); + + subgroup = prefix ? g_hash_table_lookup (muxer->groups, prefix) : muxer->global_actions; + if (!subgroup) + return; + + g_action_muxer_disconnect_group (muxer, subgroup); + + if (prefix) + { + g_hash_table_remove (muxer->groups, prefix); + g_hash_table_remove (muxer->reverse, subgroup); + } + else + g_clear_object (&muxer->global_actions); +} + |