From 12a5f15581d2c8ba5e6b4ec4baa19bd436e4fd8d Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 22 May 2013 23:19:50 -0400 Subject: Add IdoActionHelper This class contains some functionality that all menu items that are bound to an action need. It listens for action additions and removals, changes in enabled state, and state changes. It is _not_ intended to be used by specific menu items, but rather as the glue in IdoMenuItemFactory, to bind a widget to an action. An alternative way to reach the same goal is to have an IdoMenuItem base class which contains the common functionality. This would have required touching all existing menu items (slider, user menu item, media player items) and would have added more complexity to them. It is similar in spirit to GtkActionHelper (a private class in gtk), but very different in API and implementation. --- src/Makefile.am | 6 +- src/idoactionhelper.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/idoactionhelper.h | 44 ++++++ 3 files changed, 412 insertions(+), 2 deletions(-) create mode 100644 src/idoactionhelper.c create mode 100644 src/idoactionhelper.h diff --git a/src/Makefile.am b/src/Makefile.am index d6012f1..a2a82da 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,8 @@ sources_h = \ idoscalemenuitem.h \ idoswitchmenuitem.h \ idotimeline.h \ - libido.h + libido.h \ + idoactionhelper.h EXTRA_DIST = \ ido.list \ @@ -68,7 +69,8 @@ libido_0_1_la_SOURCES = \ idoscalemenuitem.c \ idoswitchmenuitem.c \ idotimeline.c \ - idomenuitemfactory.c + idomenuitemfactory.c \ + idoactionhelper.c libido3_0_1_la_SOURCES = $(libido_0_1_la_SOURCES) diff --git a/src/idoactionhelper.c b/src/idoactionhelper.c new file mode 100644 index 0000000..b68423a --- /dev/null +++ b/src/idoactionhelper.c @@ -0,0 +1,364 @@ +/* +* Copyright 2013 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 . +* +* Authors: +* Lars Uebernickel +*/ + +#include "idoactionhelper.h" + +typedef GObjectClass IdoActionHelperClass; + +struct _IdoActionHelper +{ + GObject parent; + + GtkWidget *widget; + GActionGroup *actions; + gchar *action_name; + GVariant *action_target; +}; + +G_DEFINE_TYPE (IdoActionHelper, ido_action_helper, G_TYPE_OBJECT) + +enum +{ + PROP_0, + PROP_WIDGET, + PROP_ACTION_GROUP, + PROP_ACTION_NAME, + PROP_ACTION_TARGET, + NUM_PROPERTIES +}; + +enum +{ + ACTION_STATE_CHANGED, + NUM_SIGNALS +}; + +static GParamSpec *properties[NUM_PROPERTIES]; +static guint signals[NUM_SIGNALS]; + +static void +ido_action_helper_action_added (GActionGroup *actions, + const gchar *action_name, + gpointer user_data) +{ + IdoActionHelper *helper = user_data; + gboolean enabled; + GVariant *state; + + if (!g_str_equal (action_name, helper->action_name)) + return; + + if (g_action_group_query_action (actions, action_name, + &enabled, NULL, NULL, NULL, &state)) + { + gtk_widget_set_sensitive (helper->widget, enabled); + + if (state) + { + g_signal_emit (helper, signals[ACTION_STATE_CHANGED], 0, state); + g_variant_unref (state); + } + } + else + { + gtk_widget_set_sensitive (helper->widget, FALSE); + } +} + +static void +ido_action_helper_action_removed (GActionGroup *action_group, + gchar *action_name, + gpointer user_data) +{ + IdoActionHelper *helper = user_data; + + if (g_str_equal (action_name, helper->action_name)) + gtk_widget_set_sensitive (helper->widget, FALSE); +} + +static void +ido_action_helper_action_enabled_changed (GActionGroup *action_group, + gchar *action_name, + gboolean enabled, + gpointer user_data) +{ + IdoActionHelper *helper = user_data; + + if (g_str_equal (action_name, helper->action_name)) + gtk_widget_set_sensitive (helper->widget, enabled); +} + +static void +ido_action_helper_action_state_changed (GActionGroup *action_group, + gchar *action_name, + GVariant *value, + gpointer user_data) +{ + IdoActionHelper *helper = user_data; + + if (g_str_equal (action_name, helper->action_name)) + g_signal_emit (helper, signals[ACTION_STATE_CHANGED], 0, value); +} + +static gboolean +call_action_added (gpointer user_data) +{ + IdoActionHelper *helper = user_data; + + ido_action_helper_action_added (helper->actions, helper->action_name, helper); + + return G_SOURCE_REMOVE; +} + +static void +ido_action_helper_constructed (GObject *object) +{ + IdoActionHelper *helper = IDO_ACTION_HELPER (object); + + g_signal_connect (helper->actions, "action-added", + G_CALLBACK (ido_action_helper_action_added), helper); + g_signal_connect (helper->actions, "action-removed", + G_CALLBACK (ido_action_helper_action_removed), helper); + g_signal_connect (helper->actions, "action-enabled-changed", + G_CALLBACK (ido_action_helper_action_enabled_changed), helper); + g_signal_connect (helper->actions, "action-state-changed", + G_CALLBACK (ido_action_helper_action_state_changed), helper); + + if (g_action_group_has_action (helper->actions, helper->action_name)) + { + /* call action_added in an idle, so that we don't fire the + * state-changed signal during construction (nobody could have + * connected by then). + */ + g_idle_add (call_action_added, helper); + } + + G_OBJECT_CLASS (ido_action_helper_parent_class)->constructed (object); +} + +static void +ido_action_helper_get_property (GObject *object, + guint id, + GValue *value, + GParamSpec *pspec) +{ + IdoActionHelper *helper = IDO_ACTION_HELPER (object); + + switch (id) + { + case PROP_WIDGET: + g_value_set_object (value, helper->widget); + break; + + case PROP_ACTION_GROUP: + g_value_set_object (value, helper->actions); + break; + + case PROP_ACTION_NAME: + g_value_set_string (value, helper->action_name); + break; + + case PROP_ACTION_TARGET: + g_value_set_variant (value, helper->action_target); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); + } +} + +static void +ido_action_helper_set_property (GObject *object, + guint id, + const GValue *value, + GParamSpec *pspec) +{ + IdoActionHelper *helper = IDO_ACTION_HELPER (object); + + switch (id) + { + case PROP_WIDGET: /* construct-only */ + helper->widget = g_value_dup_object (value); + break; + + case PROP_ACTION_GROUP: /* construct-only */ + helper->actions = g_value_dup_object (value); + break; + + case PROP_ACTION_NAME: /* construct-only */ + helper->action_name = g_value_dup_string (value); + break; + + case PROP_ACTION_TARGET: /* construct-only */ + helper->action_target = g_value_dup_variant (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); + } +} + +static void +ido_action_helper_finalize (GObject *object) +{ + IdoActionHelper *helper = IDO_ACTION_HELPER (object); + + g_object_unref (helper->widget); + + g_signal_handlers_disconnect_by_data (helper->actions, helper); + g_object_unref (helper->actions); + + g_free (helper->action_name); + + if (helper->action_target) + g_variant_unref (helper->action_target); + + G_OBJECT_CLASS (ido_action_helper_parent_class)->finalize (object); +} + +static void +ido_action_helper_class_init (IdoActionHelperClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->constructed = ido_action_helper_constructed; + object_class->get_property = ido_action_helper_get_property; + object_class->set_property = ido_action_helper_set_property; + object_class->finalize = ido_action_helper_finalize; + + signals[ACTION_STATE_CHANGED] = g_signal_new ("action-state-changed", + IDO_TYPE_ACTION_HELPER, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VARIANT, + G_TYPE_NONE, 1, G_TYPE_VARIANT); + + properties[PROP_WIDGET] = g_param_spec_object ("widget", "", "", + GTK_TYPE_WIDGET, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ACTION_GROUP] = g_param_spec_object ("action-group", "", "", + G_TYPE_ACTION_GROUP, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ACTION_NAME] = g_param_spec_string ("action-name", "", "", NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ACTION_TARGET] = g_param_spec_variant ("action-target", "", "", + G_VARIANT_TYPE_ANY, NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); +} + +static void +ido_action_helper_init (IdoActionHelper *helper) +{ +} + +/** + * ido_action_helper_new: + * @widget: a #GtkWidget + * @action_group: a #GActionGroup + * @action_name: the name of an action in @action_group + * @target: the target of the action + * + * Creates a new #IdoActionHelper. This helper ties @widget to an action + * (and a target), and performs some common tasks: + * + * @widget will be set to insensitive whenever @action_group does not + * contain an action with the name @action_name, or the action with that + * name is disabled. + * + * Also, the helper emits the "action-state-changed" signal whenever the + * widget must be updated from the action's state. This includes once + * when the action was added, and every time the action changes its + * state. + * + * Returns: (transfer full): a new #IdoActionHelper + */ +IdoActionHelper * +ido_action_helper_new (GtkWidget *widget, + GActionGroup *action_group, + const gchar *action_name, + GVariant *target) +{ + g_return_val_if_fail (widget != NULL, NULL); + g_return_val_if_fail (action_group != NULL, NULL); + g_return_val_if_fail (action_name != NULL, NULL); + + return g_object_new (IDO_TYPE_ACTION_HELPER, + "widget", widget, + "action-group", action_group, + "action-name", action_name, + "action-target", target, + NULL); +} + +/** + * ido_action_helper_get_widget: + * @helper: an #IdoActionHelper + * + * Returns: (transfer none): the #GtkWidget associated with @helper + */ +GtkWidget * +ido_action_helper_get_widget (IdoActionHelper *helper) +{ + g_return_val_if_fail (IDO_IS_ACTION_HELPER (helper), NULL); + + return helper->widget; +} + +/** + * ido_action_helper_get_action_target: + * @helper: an #IdoActionHelper + * + * Returns: (transfer none): the action target that was set in + * ido_action_helper_new() as a #GVariant + */ +GVariant * +ido_action_helper_get_action_target (IdoActionHelper *helper) +{ + g_return_val_if_fail (IDO_IS_ACTION_HELPER (helper), NULL); + + return helper->action_target; +} + +/** + * ido_action_helper_activate: + * @helper: an #IdoActionHelper + * + * Activates the action that is associated with this helper. + */ +void +ido_action_helper_activate (IdoActionHelper *helper) +{ + g_return_if_fail (IDO_IS_ACTION_HELPER (helper)); + + if (helper->actions && helper->action_name) + g_action_group_activate_action (helper->actions, helper->action_name, helper->action_target); +} diff --git a/src/idoactionhelper.h b/src/idoactionhelper.h new file mode 100644 index 0000000..22cdcae --- /dev/null +++ b/src/idoactionhelper.h @@ -0,0 +1,44 @@ +/* +* Copyright 2013 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 . +* +* Authors: +* Lars Uebernickel +*/ + +#ifndef __IDO_ACTION_HELPER_H__ +#define __IDO_ACTION_HELPER_H__ + +#include + +#define IDO_TYPE_ACTION_HELPER (ido_action_helper_get_type ()) +#define IDO_ACTION_HELPER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), IDO_TYPE_ACTION_HELPER, IdoActionHelper)) +#define IDO_IS_ACTION_HELPER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), IDO_TYPE_ACTION_HELPER)) + +typedef struct _IdoActionHelper IdoActionHelper; + +GType ido_menu_item_get_type (void); + +IdoActionHelper * ido_action_helper_new (GtkWidget *widget, + GActionGroup *action_group, + const gchar *action_name, + GVariant *target); + +GtkWidget * ido_action_helper_get_widget (IdoActionHelper *helper); + +GVariant * ido_action_helper_get_action_target (IdoActionHelper *helper); + +void ido_action_helper_activate (IdoActionHelper *helper); + +#endif -- cgit v1.2.3