430 lines
11 KiB
C
430 lines
11 KiB
C
/*
|
|
* 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)
|
|
*
|
|
*/
|
|
|
|
#include "e-popup-action.h"
|
|
|
|
#include <glib/gi18n.h>
|
|
#include "e-util/e-binding.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;
|
|
};
|
|
|
|
static gpointer parent_class;
|
|
|
|
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;
|
|
|
|
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 (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
|
|
popup_action_class_init (EPopupActionClass *class)
|
|
{
|
|
GObjectClass *object_class;
|
|
|
|
parent_class = g_type_class_peek_parent (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
|
|
popup_action_iface_init (GtkActivatableIface *iface)
|
|
{
|
|
iface->update = popup_action_update;
|
|
iface->sync_action_properties = popup_action_sync_action_properties;
|
|
}
|
|
|
|
static void
|
|
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);
|
|
}
|
|
|
|
GType
|
|
e_popup_action_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
|
|
if (G_UNLIKELY (type == 0)) {
|
|
static const GTypeInfo type_info = {
|
|
sizeof (EPopupActionClass),
|
|
(GBaseInitFunc) NULL,
|
|
(GBaseFinalizeFunc) NULL,
|
|
(GClassInitFunc) popup_action_class_init,
|
|
(GClassFinalizeFunc) NULL,
|
|
NULL, /* class_data */
|
|
sizeof (EPopupAction),
|
|
0, /* n_preallocs */
|
|
(GInstanceInitFunc) popup_action_init,
|
|
NULL /* value_table */
|
|
};
|
|
|
|
static const GInterfaceInfo iface_info = {
|
|
(GInterfaceInitFunc) popup_action_iface_init,
|
|
(GInterfaceFinalizeFunc) NULL,
|
|
NULL /* interface_data */
|
|
};
|
|
|
|
type = g_type_register_static (
|
|
GTK_TYPE_ACTION, "EPopupAction", &type_info, 0);
|
|
|
|
g_type_add_interface_static (
|
|
type, GTK_TYPE_ACTIVATABLE, &iface_info);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|