medit/moo/mooutils/mooaction.c

767 lines
25 KiB
C

/*
* mooaction.c
*
* Copyright (C) 2004-2007 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* See COPYING file that comes with this distribution.
*/
#include "mooutils/mooaction-private.h"
#include "mooutils/mooactionbase-private.h"
#include "mooutils/mooutils-gobject.h"
#include "mooutils/mooactiongroup.h"
#include <string.h>
static void _moo_action_set_closure (MooAction *action,
MooClosure *closure);
void
_moo_action_ring_the_bells_it_has_tooltip (GtkAction *action)
{
char *tooltip;
g_object_get (action, "tooltip", &tooltip, NULL);
g_object_set (action, "tooltip", tooltip, NULL);
g_free (tooltip);
}
gpointer
_moo_action_get_window (gpointer action)
{
GtkActionGroup *group;
MooActionCollection *collection;
g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
group = _moo_action_get_group (action);
g_return_val_if_fail (MOO_IS_ACTION_GROUP (group), NULL);
collection = _moo_action_group_get_collection (MOO_ACTION_GROUP (group));
return _moo_action_collection_get_window (collection);
}
#define DEFINE_ACTION_TYPE(TypeName, type_name, TYPE_PARENT) \
\
static void type_name##_init (TypeName *self); \
static void type_name##_class_init (TypeName##Class *klass); \
static void type_name##_set_property (GObject *object, \
guint property_id, \
const GValue *value, \
GParamSpec *pspec); \
static void type_name##_get_property (GObject *object, \
guint property_id, \
GValue *value, \
GParamSpec *pspec); \
static gpointer type_name##_parent_class = NULL; \
\
static void \
type_name##_class_intern_init (gpointer klass) \
{ \
GObjectClass *object_class = G_OBJECT_CLASS (klass); \
\
object_class->set_property = type_name##_set_property; \
object_class->get_property = type_name##_get_property; \
_moo_action_base_init_class (object_class); \
\
type_name##_parent_class = g_type_class_peek_parent (klass); \
type_name##_class_init ((TypeName##Class*) klass); \
} \
\
GType \
type_name##_get_type (void) \
{ \
static GType type; \
\
if (G_UNLIKELY (!type)) \
{ \
static const GTypeInfo info = { \
sizeof (TypeName##Class), \
(GBaseInitFunc) NULL, \
(GBaseFinalizeFunc) NULL, \
(GClassInitFunc) type_name##_class_intern_init, \
(GClassFinalizeFunc) NULL, \
NULL, /* class_data */ \
sizeof (TypeName), \
0, /* n_preallocs */ \
(GInstanceInitFunc) type_name##_init, \
NULL /* value_table */ \
}; \
\
static const GInterfaceInfo iface_info; \
\
type = g_type_register_static (TYPE_PARENT, \
#TypeName, \
&info, 0); \
g_type_add_interface_static (type, \
MOO_TYPE_ACTION_BASE, \
&iface_info); \
} \
\
return type; \
}
DEFINE_ACTION_TYPE (MooAction, moo_action, GTK_TYPE_ACTION)
DEFINE_ACTION_TYPE (MooToggleAction, moo_toggle_action, GTK_TYPE_TOGGLE_ACTION)
DEFINE_ACTION_TYPE (MooRadioAction, moo_radio_action, GTK_TYPE_RADIO_ACTION)
static void
connect_proxy (GtkAction *action,
GtkWidget *widget)
{
GtkActionClass *parent_class;
if (MOO_IS_ACTION (action))
parent_class = moo_action_parent_class;
else if (MOO_IS_TOGGLE_ACTION (action))
parent_class = moo_toggle_action_parent_class;
else
parent_class = moo_radio_action_parent_class;
parent_class->connect_proxy (action, widget);
g_signal_emit_by_name (action, "connect-proxy", widget);
_moo_action_base_connect_proxy (action, widget);
}
static void
disconnect_proxy (GtkAction *action,
GtkWidget *widget)
{
GtkActionClass *parent;
if (MOO_IS_ACTION (action))
parent = moo_action_parent_class;
else if (MOO_IS_TOGGLE_ACTION (action))
parent = moo_toggle_action_parent_class;
else
parent = moo_radio_action_parent_class;
g_signal_emit_by_name (action, "disconnect-proxy", widget);
parent->disconnect_proxy (action, widget);
}
/*****************************************************************************/
/* MooAction
*/
struct _MooActionPrivate {
MooClosure *closure;
};
enum {
MOO_ACTION_BASE_PROPS(ACTION),
ACTION_PROP_CLOSURE,
ACTION_PROP_CLOSURE_OBJECT,
ACTION_PROP_CLOSURE_SIGNAL,
ACTION_PROP_CLOSURE_CALLBACK,
ACTION_PROP_CLOSURE_PROXY_FUNC
};
static void
moo_action_init (MooAction *action)
{
action->priv = g_new0 (MooActionPrivate, 1);
_moo_action_base_init_instance (action);
}
static void
moo_action_dispose (GObject *object)
{
MooAction *action = MOO_ACTION (object);
if (action->priv)
{
if (action->priv->closure)
{
moo_closure_unref (action->priv->closure);
action->priv->closure = NULL;
}
g_free (action->priv);
action->priv = NULL;
}
G_OBJECT_CLASS (moo_action_parent_class)->dispose (object);
}
static void
moo_action_activate (GtkAction *gtkaction)
{
MooAction *action = MOO_ACTION (gtkaction);
if (GTK_ACTION_CLASS (moo_action_parent_class)->activate)
GTK_ACTION_CLASS (moo_action_parent_class)->activate (gtkaction);
if (action->priv->closure)
moo_closure_invoke (action->priv->closure);
}
static GObject *
moo_action_constructor (GType type,
guint n_props,
GObjectConstructParam *props)
{
guint i;
GObject *object;
MooClosure *closure = NULL;
gpointer closure_object = NULL;
const char *closure_signal = NULL;
GCallback closure_callback = NULL;
GCallback closure_proxy_func = NULL;
for (i = 0; i < n_props; ++i)
{
const char *name = props[i].pspec->name;
GValue *value = props[i].value;
if (!strcmp (name, "closure-object"))
closure_object = g_value_get_object (value);
else if (!strcmp (name, "closure-signal"))
closure_signal = g_value_get_string (value);
else if (!strcmp (name, "closure-callback"))
closure_callback = g_value_get_pointer (value);
else if (!strcmp (name, "closure-proxy-func"))
closure_proxy_func = g_value_get_pointer (value);
}
if (closure_callback || closure_signal)
{
if (closure_object)
{
closure = _moo_closure_new_simple (closure_object, closure_signal,
closure_callback, closure_proxy_func);
moo_closure_ref_sink (closure);
}
else
g_critical ("%s: closure data missing", G_STRLOC);
}
object = G_OBJECT_CLASS(moo_action_parent_class)->constructor (type, n_props, props);
if (closure)
{
_moo_action_set_closure (MOO_ACTION (object), closure);
moo_closure_unref (closure);
}
return object;
}
static void
moo_action_class_init (MooActionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkActionClass *action_class = GTK_ACTION_CLASS (klass);
object_class->dispose = moo_action_dispose;
object_class->constructor = moo_action_constructor;
action_class->activate = moo_action_activate;
action_class->connect_proxy = connect_proxy;
action_class->disconnect_proxy = disconnect_proxy;
g_object_class_install_property (object_class, ACTION_PROP_CLOSURE,
g_param_spec_boxed ("closure", "closure", "closure",
MOO_TYPE_CLOSURE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class, ACTION_PROP_CLOSURE_OBJECT,
g_param_spec_object ("closure-object", "closure-object", "closure-object",
G_TYPE_OBJECT, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, ACTION_PROP_CLOSURE_SIGNAL,
g_param_spec_string ("closure-signal", "closure-signal", "closure-signal",
NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, ACTION_PROP_CLOSURE_CALLBACK,
g_param_spec_pointer ("closure-callback", "closure-callback", "closure-callback",
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, ACTION_PROP_CLOSURE_PROXY_FUNC,
g_param_spec_pointer ("closure-proxy-func", "closure-proxy-func", "closure-proxy-func",
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
}
static void
moo_action_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
MooAction *action = MOO_ACTION (object);
switch (property_id)
{
case ACTION_PROP_CLOSURE:
_moo_action_set_closure (action, g_value_get_boxed (value));
break;
case ACTION_PROP_CLOSURE_OBJECT:
case ACTION_PROP_CLOSURE_SIGNAL:
case ACTION_PROP_CLOSURE_CALLBACK:
case ACTION_PROP_CLOSURE_PROXY_FUNC:
/* these are handled in the constructor */
break;
MOO_ACTION_BASE_SET_PROPERTY (ACTION);
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
moo_action_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
MooAction *action = MOO_ACTION (object);
switch (property_id)
{
case ACTION_PROP_CLOSURE:
g_value_set_boxed (value, action->priv->closure);
break;
MOO_ACTION_BASE_GET_PROPERTY (ACTION);
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
_moo_action_set_closure (MooAction *action,
MooClosure *closure)
{
g_return_if_fail (MOO_IS_ACTION (action));
if (closure == action->priv->closure)
return;
if (action->priv->closure)
moo_closure_unref (action->priv->closure);
if (closure)
moo_closure_ref_sink (closure);
action->priv->closure = closure;
g_object_notify (G_OBJECT (action), "closure");
}
/*****************************************************************************/
/* MooToggleAction
*/
typedef void (*MooToggleActionCallback) (gpointer data,
gboolean active);
struct _MooToggleActionPrivate {
MooToggleActionCallback callback;
MooObjectPtr *ptr;
gpointer data;
};
enum {
MOO_ACTION_BASE_PROPS(TOGGLE_ACTION),
TOGGLE_ACTION_PROP_TOGGLED_CALLBACK,
TOGGLE_ACTION_PROP_TOGGLED_OBJECT,
TOGGLE_ACTION_PROP_TOGGLED_DATA
};
static void
moo_toggle_action_init (MooToggleAction *action)
{
action->priv = g_new0 (MooToggleActionPrivate, 1);
_moo_action_base_init_instance (action);
}
static void
moo_toggle_action_dispose (GObject *object)
{
MooToggleAction *action = MOO_TOGGLE_ACTION (object);
if (action->priv->ptr)
{
_moo_object_ptr_free (action->priv->ptr);
action->priv->ptr = NULL;
}
G_OBJECT_CLASS (moo_toggle_action_parent_class)->dispose (object);
}
static void
moo_toggle_action_toggled (GtkToggleAction *gtkaction)
{
MooToggleAction *action = MOO_TOGGLE_ACTION (gtkaction);
if (GTK_TOGGLE_ACTION_CLASS (moo_toggle_action_parent_class)->toggled)
GTK_TOGGLE_ACTION_CLASS (moo_toggle_action_parent_class)->toggled (gtkaction);
if (action->priv->callback)
{
if (MOO_OBJECT_PTR_GET (action->priv->ptr))
{
GObject *obj = MOO_OBJECT_PTR_GET (action->priv->ptr);
g_object_ref (obj);
action->priv->callback (obj, gtk_toggle_action_get_active (gtkaction));
g_object_unref (obj);
}
else
{
action->priv->callback (action->priv->data, gtk_toggle_action_get_active (gtkaction));
}
}
}
static void
_moo_toggle_action_set_callback (MooToggleAction *action,
MooToggleActionCallback callback,
gpointer data,
gboolean object)
{
g_return_if_fail (MOO_IS_TOGGLE_ACTION (action));
g_return_if_fail (!object || G_IS_OBJECT (data));
action->priv->callback = callback;
if (action->priv->ptr)
_moo_object_ptr_free (action->priv->ptr);
action->priv->ptr = NULL;
action->priv->data = NULL;
if (callback)
{
if (object)
action->priv->ptr = _moo_object_ptr_new (data, NULL, NULL);
else
action->priv->data = data;
}
}
static GObject *
moo_toggle_action_constructor (GType type,
guint n_props,
GObjectConstructParam *props)
{
guint i;
GObject *object;
MooToggleAction *action;
MooToggleActionCallback toggled_callback = NULL;
gpointer toggled_data = NULL;
gpointer toggled_object = NULL;
for (i = 0; i < n_props; ++i)
{
const char *name = props[i].pspec->name;
GValue *value = props[i].value;
if (!strcmp (name, "toggled-callback"))
toggled_callback = g_value_get_pointer (value);
else if (!strcmp (name, "toggled-data"))
toggled_data = g_value_get_pointer (value);
else if (!strcmp (name, "toggled-object"))
toggled_object = g_value_get_object (value);
}
object = G_OBJECT_CLASS(moo_toggle_action_parent_class)->constructor (type, n_props, props);
action = MOO_TOGGLE_ACTION (object);
if (toggled_callback)
{
if (toggled_object)
_moo_toggle_action_set_callback (action, toggled_callback, toggled_object, TRUE);
else
_moo_toggle_action_set_callback (action, toggled_callback, toggled_data, FALSE);
}
return object;
}
static void
moo_toggle_action_class_init (MooToggleActionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkActionClass *action_class = GTK_ACTION_CLASS (klass);
GtkToggleActionClass *toggle_action_class = GTK_TOGGLE_ACTION_CLASS (klass);
object_class->dispose = moo_toggle_action_dispose;
object_class->constructor = moo_toggle_action_constructor;
toggle_action_class->toggled = moo_toggle_action_toggled;
action_class->connect_proxy = connect_proxy;
action_class->disconnect_proxy = disconnect_proxy;
g_object_class_install_property (object_class, TOGGLE_ACTION_PROP_TOGGLED_CALLBACK,
g_param_spec_pointer ("toggled-callback", "toggled-callback", "toggled-callback",
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, TOGGLE_ACTION_PROP_TOGGLED_OBJECT,
g_param_spec_object ("toggled-object", "toggled-object", "toggled-object",
G_TYPE_OBJECT, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, TOGGLE_ACTION_PROP_TOGGLED_DATA,
g_param_spec_pointer ("toggled-data", "toggled-data", "toggled-data",
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
}
static void
moo_toggle_action_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
case TOGGLE_ACTION_PROP_TOGGLED_CALLBACK:
case TOGGLE_ACTION_PROP_TOGGLED_OBJECT:
case TOGGLE_ACTION_PROP_TOGGLED_DATA:
/* these are handled in the constructor */
break;
MOO_ACTION_BASE_SET_PROPERTY (TOGGLE_ACTION);
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
moo_toggle_action_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
MOO_ACTION_BASE_GET_PROPERTY (TOGGLE_ACTION);
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
/*****************************************************************************/
/* MooRadioAction
*/
enum {
MOO_ACTION_BASE_PROPS(RADIO_ACTION)
};
static void
moo_radio_action_init (MooRadioAction *action)
{
_moo_action_base_init_instance (action);
}
static void
moo_radio_action_class_init (MooRadioActionClass *klass)
{
GtkActionClass *action_class = GTK_ACTION_CLASS (klass);
action_class->connect_proxy = connect_proxy;
action_class->disconnect_proxy = disconnect_proxy;
}
static void
moo_radio_action_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
MOO_ACTION_BASE_SET_PROPERTY (RADIO_ACTION);
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
moo_radio_action_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
MOO_ACTION_BASE_GET_PROPERTY (RADIO_ACTION);
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
/**************************************************************************/
/* _moo_sync_toggle_action
*/
typedef struct {
MooObjectWatch parent;
GParamSpec *pspec;
gboolean invert;
} ToggleWatch;
static void action_toggled (ToggleWatch *watch);
static void prop_changed (ToggleWatch *watch);
static void toggle_watch_destroy (MooObjectWatch *watch);
static MooObjectWatchClass ToggleWatchClass = {NULL, NULL, toggle_watch_destroy};
static ToggleWatch *
toggle_watch_new (GObject *master,
const char *prop,
GtkAction *action,
gboolean invert)
{
ToggleWatch *watch;
GObjectClass *klass;
GParamSpec *pspec;
char *signal;
g_return_val_if_fail (G_IS_OBJECT (master), NULL);
g_return_val_if_fail (GTK_IS_TOGGLE_ACTION (action), NULL);
g_return_val_if_fail (prop != NULL, NULL);
klass = g_type_class_peek (G_OBJECT_TYPE (master));
pspec = g_object_class_find_property (klass, prop);
if (!pspec)
{
g_warning ("%s: no property '%s' in class '%s'",
G_STRLOC, prop, g_type_name (G_OBJECT_TYPE (master)));
return NULL;
}
watch = _moo_object_watch_new (ToggleWatch, &ToggleWatchClass,
master, action, NULL, NULL);
watch->pspec = pspec;
watch->invert = invert;
signal = g_strdup_printf ("notify::%s", prop);
g_signal_connect_swapped (master, signal,
G_CALLBACK (prop_changed),
watch);
g_signal_connect_swapped (action, "toggled",
G_CALLBACK (action_toggled),
watch);
g_free (signal);
return watch;
}
static void
toggle_watch_destroy (MooObjectWatch *watch)
{
if (MOO_OBJECT_PTR_GET (watch->source))
{
g_signal_handlers_disconnect_by_func (MOO_OBJECT_PTR_GET (watch->source),
(gpointer) prop_changed,
watch);
}
if (MOO_OBJECT_PTR_GET (watch->target))
{
g_assert (GTK_IS_TOGGLE_ACTION (MOO_OBJECT_PTR_GET (watch->target)));
g_signal_handlers_disconnect_by_func (MOO_OBJECT_PTR_GET (watch->target),
(gpointer) action_toggled,
watch);
}
}
void
_moo_sync_toggle_action (GtkAction *action,
gpointer master,
const char *prop,
gboolean invert)
{
ToggleWatch *watch;
g_return_if_fail (GTK_IS_TOGGLE_ACTION (action));
g_return_if_fail (G_IS_OBJECT (master));
g_return_if_fail (prop != NULL);
watch = toggle_watch_new (master, prop, action, invert);
g_return_if_fail (watch != NULL);
prop_changed (watch);
}
static void
prop_changed (ToggleWatch *watch)
{
gboolean value, active, equal;
gpointer action;
g_object_get (MOO_OBJECT_PTR_GET (watch->parent.source),
watch->pspec->name, &value, NULL);
action = MOO_OBJECT_PTR_GET (watch->parent.target);
g_assert (GTK_IS_TOGGLE_ACTION (action));
active = gtk_toggle_action_get_active (action);
if (!watch->invert)
equal = !value == !active;
else
equal = !value != !active;
if (!equal)
gtk_toggle_action_set_active (action, watch->invert ? !value : value);
}
static void
action_toggled (ToggleWatch *watch)
{
gboolean value, active, equal;
gpointer action;
g_object_get (MOO_OBJECT_PTR_GET (watch->parent.source),
watch->pspec->name, &value, NULL);
action = MOO_OBJECT_PTR_GET (watch->parent.target);
g_assert (GTK_IS_TOGGLE_ACTION (action));
active = gtk_toggle_action_get_active (action);
if (!watch->invert)
equal = !value == !active;
else
equal = !value != !active;
if (!equal)
g_object_set (MOO_OBJECT_PTR_GET (watch->parent.source),
watch->pspec->name,
watch->invert ? !active : active, NULL);
}