aboutsummaryrefslogtreecommitdiff
path: root/src/idoactionhelper.c
diff options
context:
space:
mode:
authorCharles Kerr <charles.kerr@canonical.com>2013-05-31 19:51:15 +0000
committerTarmac <Unknown>2013-05-31 19:51:15 +0000
commite5d21874d8c6a0d0adfab5e5294b3d0d02445f11 (patch)
tree87d34939e9de15291122684b5b81105fc111fb14 /src/idoactionhelper.c
parent27f3661cfb2266ee87a552738e5c03774dc9ae68 (diff)
parent18f2ca9e20c5ea1ac895f7892ae9d652153333ad (diff)
downloadayatana-ido-e5d21874d8c6a0d0adfab5e5294b3d0d02445f11.tar.gz
ayatana-ido-e5d21874d8c6a0d0adfab5e5294b3d0d02445f11.tar.bz2
ayatana-ido-e5d21874d8c6a0d0adfab5e5294b3d0d02445f11.zip
Put a framework in place to move custom menu items from the indicator packages into ido. It depends on a vendor-patch to gtk (see bug #1183505), because gtk+ does not yet have an API for inserting arbitrary menu items into menus created with gtk_menu_new_from_model().
IdoMenuItemFactory implements the GtkMenuItemFactory interface, which is used by (the patched) gtk+ to create menu items that have an "x-canonical-type" attribute. IdoActionHelper contains some common functionality that all menu items need (watching the action group for action additions and removals, as well es enabled and state changes). It can be used to glue a menu item widget to an action in IdoMenuItemFactory. This changeset also adds the first widget: IdoUserMenuItem (merged from lp:~charlesk/ido/add-idousermenuitem). Please follow this example when adding additional widgets. Approved by PS Jenkins bot, Ted Gould.
Diffstat (limited to 'src/idoactionhelper.c')
-rw-r--r--src/idoactionhelper.c403
1 files changed, 403 insertions, 0 deletions
diff --git a/src/idoactionhelper.c b/src/idoactionhelper.c
new file mode 100644
index 0000000..fdd9479
--- /dev/null
+++ b/src/idoactionhelper.c
@@ -0,0 +1,403 @@
+/*
+* 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 <http://www.gnu.org/licenses/>.
+*
+* Authors:
+* Lars Uebernickel <lars.uebernickel@canonical.com>
+*/
+
+#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;
+
+ /**
+ * IdoActionHelper::action-state-changed:
+ * @helper: the #IdoActionHelper watching the action
+ * @state: the new state of the action
+ *
+ * Emitted when the widget must be updated from the action's state,
+ * which happens every time the action appears in the group and when
+ * the action changes its state.
+ */
+ 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);
+
+ /**
+ * IdoActionHelper:widget:
+ *
+ * The widget that is associated with this action helper. The action
+ * helper updates the widget's "sensitive" property to reflect whether
+ * the action #IdoActionHelper:action-name exists in
+ * #IdoActionHelper:action-group.
+ */
+ properties[PROP_WIDGET] = g_param_spec_object ("widget", "", "",
+ GTK_TYPE_WIDGET,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ /**
+ * IdoActionHelper:action-group:
+ *
+ * The action group that eventually contains the action that
+ * #IdoActionHelper:widget should be bound to.
+ */
+ 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);
+
+ /**
+ * IdoActionHelper:action-name:
+ *
+ * The name of the action in #IdoActionHelper:action-group that
+ * should be bound to #IdoActionHelper:widget
+ */
+ properties[PROP_ACTION_NAME] = g_param_spec_string ("action-name", "", "", NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ /**
+ * IdoActionHelper:action-target:
+ *
+ * The target of #IdoActionHelper:widget. ido_action_helper_activate()
+ * passes the target as parameter when activating the action.
+ *
+ * The handler of #IdoActionHelper:action-state-changed is responsible
+ * for comparing this target with the action's state and updating the
+ * #IdoActionHelper:widget appropriately.
+ */
+ 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);
+}