diff options
Diffstat (limited to 'e-util/e-popup-action.c')
-rw-r--r-- | e-util/e-popup-action.c | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/e-util/e-popup-action.c b/e-util/e-popup-action.c new file mode 100644 index 0000000000..27c90f67c3 --- /dev/null +++ b/e-util/e-popup-action.c @@ -0,0 +1,408 @@ +/* + * e-popup-action.c + * + * 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 License, or (at your option) version 3. + * + * This program 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-popup-action.h" + +#include <glib/gi18n.h> + +#define E_POPUP_ACTION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_POPUP_ACTION, EPopupActionPrivate)) + +enum { + PROP_0, + PROP_RELATED_ACTION, + PROP_USE_ACTION_APPEARANCE +}; + +struct _EPopupActionPrivate { + GtkAction *related_action; + gboolean use_action_appearance; + gulong activate_handler_id; + gulong notify_handler_id; +}; + +/* Forward Declarations */ +static void e_popup_action_activatable_init (GtkActivatableIface *interface); + +G_DEFINE_TYPE_WITH_CODE ( + EPopupAction, + e_popup_action, + GTK_TYPE_ACTION, + G_IMPLEMENT_INTERFACE ( + GTK_TYPE_ACTIVATABLE, + e_popup_action_activatable_init)) + +static void +popup_action_notify_cb (GtkAction *action, + GParamSpec *pspec, + GtkActivatable *activatable) +{ + GtkActivatableIface *iface; + + iface = GTK_ACTIVATABLE_GET_IFACE (activatable); + g_return_if_fail (iface->update != NULL); + + iface->update (activatable, action, pspec->name); +} + +static GtkAction * +popup_action_get_related_action (EPopupAction *popup_action) +{ + return popup_action->priv->related_action; +} + +static void +popup_action_set_related_action (EPopupAction *popup_action, + GtkAction *related_action) +{ + GtkActivatable *activatable; + + /* Do not call gtk_activatable_do_set_related_action() because + * it assumes the activatable object is a widget and tries to add + * it to the related actions's proxy list. Instead we'll just do + * the relevant steps manually. */ + + activatable = GTK_ACTIVATABLE (popup_action); + + if (related_action == popup_action->priv->related_action) + return; + + if (related_action != NULL) + g_object_ref (related_action); + + if (popup_action->priv->related_action != NULL) { + g_signal_handler_disconnect ( + popup_action, + popup_action->priv->activate_handler_id); + g_signal_handler_disconnect ( + popup_action->priv->related_action, + popup_action->priv->notify_handler_id); + popup_action->priv->activate_handler_id = 0; + popup_action->priv->notify_handler_id = 0; + g_object_unref (popup_action->priv->related_action); + } + + popup_action->priv->related_action = related_action; + + if (related_action != NULL) { + popup_action->priv->activate_handler_id = + g_signal_connect_swapped ( + popup_action, "activate", + G_CALLBACK (gtk_action_activate), + related_action); + popup_action->priv->notify_handler_id = + g_signal_connect ( + related_action, "notify", + G_CALLBACK (popup_action_notify_cb), + popup_action); + gtk_activatable_sync_action_properties ( + activatable, related_action); + } else + gtk_action_set_visible (GTK_ACTION (popup_action), FALSE); + + g_object_notify (G_OBJECT (popup_action), "related-action"); +} + +static gboolean +popup_action_get_use_action_appearance (EPopupAction *popup_action) +{ + return popup_action->priv->use_action_appearance; +} + +static void +popup_action_set_use_action_appearance (EPopupAction *popup_action, + gboolean use_action_appearance) +{ + GtkActivatable *activatable; + GtkAction *related_action; + + if (popup_action->priv->use_action_appearance == use_action_appearance) + return; + + popup_action->priv->use_action_appearance = use_action_appearance; + + g_object_notify (G_OBJECT (popup_action), "use-action-appearance"); + + activatable = GTK_ACTIVATABLE (popup_action); + related_action = popup_action_get_related_action (popup_action); + gtk_activatable_sync_action_properties (activatable, related_action); +} + +static void +popup_action_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_RELATED_ACTION: + popup_action_set_related_action ( + E_POPUP_ACTION (object), + g_value_get_object (value)); + return; + + case PROP_USE_ACTION_APPEARANCE: + popup_action_set_use_action_appearance ( + E_POPUP_ACTION (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +popup_action_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_RELATED_ACTION: + g_value_set_object ( + value, + popup_action_get_related_action ( + E_POPUP_ACTION (object))); + return; + + case PROP_USE_ACTION_APPEARANCE: + g_value_set_boolean ( + value, + popup_action_get_use_action_appearance ( + E_POPUP_ACTION (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +popup_action_dispose (GObject *object) +{ + EPopupActionPrivate *priv; + + priv = E_POPUP_ACTION_GET_PRIVATE (object); + + if (priv->related_action != NULL) { + g_signal_handler_disconnect ( + object, + priv->activate_handler_id); + g_signal_handler_disconnect ( + priv->related_action, + priv->notify_handler_id); + g_object_unref (priv->related_action); + priv->related_action = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_popup_action_parent_class)->dispose (object); +} + +static void +popup_action_update (GtkActivatable *activatable, + GtkAction *action, + const gchar *property_name) +{ + GObjectClass *class; + GParamSpec *pspec; + GValue *value; + + /* Ignore "action-group" changes" */ + if (strcmp (property_name, "action-group") == 0) + return; + + /* Ignore "visible" changes. */ + if (strcmp (property_name, "visible") == 0) + return; + + value = g_slice_new0 (GValue); + class = G_OBJECT_GET_CLASS (action); + pspec = g_object_class_find_property (class, property_name); + g_value_init (value, pspec->value_type); + + g_object_get_property (G_OBJECT (action), property_name, value); + + if (strcmp (property_name, "sensitive") == 0) + property_name = "visible"; + else if (!gtk_activatable_get_use_action_appearance (activatable)) + goto exit; + + g_object_set_property (G_OBJECT (activatable), property_name, value); + +exit: + g_value_unset (value); + g_slice_free (GValue, value); +} + +static void +popup_action_sync_action_properties (GtkActivatable *activatable, + GtkAction *action) +{ + if (action == NULL) + return; + + /* XXX GTK+ 2.18 is still missing accessor functions for + * "hide-if-empty" and "visible-overflown" properties. + * These are rarely used so we'll skip them for now. */ + + /* A popup action is never shown as insensitive. */ + gtk_action_set_sensitive (GTK_ACTION (activatable), TRUE); + + gtk_action_set_visible ( + GTK_ACTION (activatable), + gtk_action_get_sensitive (action)); + + gtk_action_set_visible_horizontal ( + GTK_ACTION (activatable), + gtk_action_get_visible_horizontal (action)); + + gtk_action_set_visible_vertical ( + GTK_ACTION (activatable), + gtk_action_get_visible_vertical (action)); + + gtk_action_set_is_important ( + GTK_ACTION (activatable), + gtk_action_get_is_important (action)); + + if (!gtk_activatable_get_use_action_appearance (activatable)) + return; + + gtk_action_set_label ( + GTK_ACTION (activatable), + gtk_action_get_label (action)); + + gtk_action_set_short_label ( + GTK_ACTION (activatable), + gtk_action_get_short_label (action)); + + gtk_action_set_tooltip ( + GTK_ACTION (activatable), + gtk_action_get_tooltip (action)); + + gtk_action_set_stock_id ( + GTK_ACTION (activatable), + gtk_action_get_stock_id (action)); + + gtk_action_set_gicon ( + GTK_ACTION (activatable), + gtk_action_get_gicon (action)); + + gtk_action_set_icon_name ( + GTK_ACTION (activatable), + gtk_action_get_icon_name (action)); +} + +static void +e_popup_action_class_init (EPopupActionClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EPopupActionPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = popup_action_set_property; + object_class->get_property = popup_action_get_property; + object_class->dispose = popup_action_dispose; + + g_object_class_override_property ( + object_class, + PROP_RELATED_ACTION, + "related-action"); + + g_object_class_override_property ( + object_class, + PROP_USE_ACTION_APPEARANCE, + "use-action-appearance"); +} + +static void +e_popup_action_init (EPopupAction *popup_action) +{ + popup_action->priv = E_POPUP_ACTION_GET_PRIVATE (popup_action); + popup_action->priv->use_action_appearance = TRUE; + + /* Remain invisible until we have a related action. */ + gtk_action_set_visible (GTK_ACTION (popup_action), FALSE); +} + +static void +e_popup_action_activatable_init (GtkActivatableIface *interface) +{ + interface->update = popup_action_update; + interface->sync_action_properties = popup_action_sync_action_properties; +} + +EPopupAction * +e_popup_action_new (const gchar *name) +{ + g_return_val_if_fail (name != NULL, NULL); + + return g_object_new (E_TYPE_POPUP_ACTION, "name", name, NULL); +} + +void +e_action_group_add_popup_actions (GtkActionGroup *action_group, + const EPopupActionEntry *entries, + guint n_entries) +{ + guint ii; + + g_return_if_fail (GTK_IS_ACTION_GROUP (action_group)); + + for (ii = 0; ii < n_entries; ii++) { + EPopupAction *popup_action; + GtkAction *related_action; + const gchar *label; + + label = gtk_action_group_translate_string ( + action_group, entries[ii].label); + + related_action = gtk_action_group_get_action ( + action_group, entries[ii].related); + + if (related_action == NULL) { + g_warning ( + "Related action '%s' not found in " + "action group '%s'", entries[ii].related, + gtk_action_group_get_name (action_group)); + continue; + } + + popup_action = e_popup_action_new (entries[ii].name); + + gtk_activatable_set_related_action ( + GTK_ACTIVATABLE (popup_action), related_action); + + if (label != NULL && *label != '\0') + gtk_action_set_label ( + GTK_ACTION (popup_action), label); + + gtk_action_group_add_action ( + action_group, GTK_ACTION (popup_action)); + + g_object_unref (popup_action); + } +} |