diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 0e25918ab..d6c8361bc 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -168,6 +168,7 @@ + diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 73890aa78..f2f97bf0d 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2689,7 +2689,6 @@ g_application_get_type gactiongroup GActionGroup GActionGroup -GActionGroupClass g_action_group_has_action @@ -2719,9 +2718,7 @@ g_action_group_get_type GActionGroupPrivate G_TYPE_ACTION_GROUP G_IS_ACTION_GROUP -G_ACTION_GROUP_CLASS -G_ACTION_GROUP_GET_CLASS -G_IS_ACTION_GROUP_CLASS +G_ACTION_GROUP_GET_INTERFACE G_ACTION_GROUP @@ -2729,11 +2726,6 @@ G_ACTION_GROUP gaction GAction GAction -GActionClass - - -g_action_new -g_action_new_stateful g_action_get_name @@ -2743,7 +2735,6 @@ g_action_get_state_hint g_action_get_enabled -g_action_set_enabled g_action_get_state g_action_set_state @@ -2751,16 +2742,37 @@ g_action_set_state g_action_activate -GActionPrivate g_action_get_type G_TYPE_ACTION G_IS_ACTION -G_ACTION_CLASS -G_ACTION_GET_CLASS -G_IS_ACTION_CLASS +G_ACTION_GET_INTERFACE G_ACTION +
+gsimpleaction +GSimpleAction +GSimpleAction +GSimpleActionClass + + +g_simple_action_new +g_simple_action_new_stateful + + +g_simple_action_set_enabled + + +GSimpleActionPrivate +g_simple_action_get_type +G_TYPE_SIMPLE_ACTION +G_IS_SIMPLE_ACTION +G_SIMPLE_ACTION_CLASS +G_SIMPLE_ACTION_GET_CLASS +G_IS_SIMPLE_ACTION_CLASS +G_SIMPLE_ACTION +
+
gsimpleactiongroup GSimpleActionGroup diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types index 1b0a4da4b..7ebd6c489 100644 --- a/docs/reference/gio/gio.types +++ b/docs/reference/gio/gio.types @@ -1,4 +1,5 @@ g_action_get_type +g_simple_action_get_type g_action_group_get_type g_simple_action_group_get_type g_app_info_create_flags_get_type diff --git a/gio/Makefile.am b/gio/Makefile.am index 6962369d1..b7479e351 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -132,12 +132,14 @@ application_headers = \ gactiongroup.h \ gsimpleactiongroup.h \ gaction.h \ + gsimpleaction.h \ gapplication.h application_sources = \ gactiongroup.c \ gsimpleactiongroup.c \ gaction.c \ + gsimpleaction.c \ gapplication.c diff --git a/gio/gaction.c b/gio/gaction.c index 3b3b64259..f8b8a2c0c 100644 --- a/gio/gaction.c +++ b/gio/gaction.c @@ -23,7 +23,7 @@ #include "gaction.h" #include "glibintl.h" -G_DEFINE_TYPE (GAction, g_action, G_TYPE_OBJECT) +G_DEFINE_INTERFACE (GAction, g_action, G_TYPE_OBJECT) /** * SECTION:gaction @@ -46,220 +46,24 @@ G_DEFINE_TYPE (GAction, g_action, G_TYPE_OBJECT) * The state may have a hint associated with it, specifying its valid * range. * - * #GAction is intended to be used both as a simple action class and as - * a base class for more complicated action types. The base class - * itself supports activation and state. Not supported are state hints - * and filtering requests to set the state based on the requested value. - * You should subclass if you require either of these abilities. + * #GAction is merely the interface to the concept of an action, as + * described above. Various implementations of actions exist, including + * #GSimpleAction and #GtkAction. * - * In all cases, the base class is responsible for storing the name of - * the action, the parameter type, the enabled state, the optional state - * type and the state and emitting the appropriate signals when these - * change. The base class is also responsbile for filtering calls to - * g_action_activate() and g_action_set_state() for type safety and for - * the state being enabled. + * In all cases, the implementing class is responsible for storing the + * name of the action, the parameter type, the enabled state, the + * optional state type and the state and emitting the appropriate + * signals when these change. The implementor responsible for filtering + * calls to g_action_activate() and g_action_set_state() for type safety + * and for the state being enabled. * * Probably the only useful thing to do with a #GAction is to put it * inside of a #GSimpleActionGroup. **/ -struct _GActionPrivate -{ - gchar *name; - GVariantType *parameter_type; - guint enabled : 1; - guint state_set : 1; - GVariant *state; -}; - -enum -{ - PROP_NONE, - PROP_NAME, - PROP_PARAMETER_TYPE, - PROP_ENABLED, - PROP_STATE_TYPE, - PROP_STATE -}; - -enum -{ - SIGNAL_ACTIVATE, - NR_SIGNALS -}; - -static guint g_action_signals[NR_SIGNALS]; - -static void -g_action_real_set_state (GAction *action, - GVariant *value) -{ - if (action->priv->state == value) - return; - - if (!action->priv->state || !g_variant_equal (action->priv->state, value)) - { - if (action->priv->state) - g_variant_unref (action->priv->state); - - action->priv->state = g_variant_ref (value); - - g_object_notify (G_OBJECT (action), "state"); - } -} - -static GVariant * -g_action_real_get_state_hint (GAction *action) -{ - return NULL; -} - -static void -g_action_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GAction *action = G_ACTION (object); - - switch (prop_id) - { - case PROP_NAME: - g_assert (action->priv->name == NULL); - action->priv->name = g_value_dup_string (value); - break; - - case PROP_PARAMETER_TYPE: - g_assert (action->priv->parameter_type == NULL); - action->priv->parameter_type = g_value_dup_boxed (value); - break; - - case PROP_ENABLED: - g_action_set_enabled (action, g_value_get_boolean (value)); - break; - - case PROP_STATE: - /* PROP_STATE is marked as G_PARAM_CONSTRUCT so we always get a - * call during object construction, even if it is NULL. We treat - * that first call differently, for a number of reasons. - * - * First, we don't want the value to be rejected by the - * possibly-overridden .set_state() function. Second, we don't - * want to be tripped by the assertions in g_action_set_state() - * that would enforce the catch22 that we only provide a value of - * the same type as the existing value (when there is not yet an - * existing value). - */ - if (action->priv->state_set) - g_action_set_state (action, g_value_get_variant (value)); - - else /* this is the special case */ - { - /* only do it the first time. */ - action->priv->state_set = TRUE; - - /* blindly set it. */ - action->priv->state = g_value_dup_variant (value); - } - break; - - default: - g_assert_not_reached (); - } -} - -static void -g_action_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GAction *action = G_ACTION (object); - - switch (prop_id) - { - case PROP_NAME: - g_value_set_string (value, g_action_get_name (action)); - break; - - case PROP_PARAMETER_TYPE: - g_value_set_boxed (value, g_action_get_parameter_type (action)); - break; - - case PROP_ENABLED: - g_value_set_boolean (value, g_action_get_enabled (action)); - break; - - case PROP_STATE_TYPE: - g_value_set_boxed (value, g_action_get_state_type (action)); - break; - - case PROP_STATE: - g_value_take_variant (value, g_action_get_state (action)); - break; - - default: - g_assert_not_reached (); - } -} - -static void -g_action_finalize (GObject *object) -{ - GAction *action = G_ACTION (object); - - g_free (action->priv->name); - if (action->priv->parameter_type) - g_variant_type_free (action->priv->parameter_type); - if (action->priv->state) - g_variant_unref (action->priv->state); - - G_OBJECT_CLASS (g_action_parent_class) - ->finalize (object); -} - void -g_action_init (GAction *action) +g_action_default_init (GActionInterface *iface) { - action->priv = G_TYPE_INSTANCE_GET_PRIVATE (action, - G_TYPE_ACTION, - GActionPrivate); -} - -void -g_action_class_init (GActionClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - - class->get_state_hint = g_action_real_get_state_hint; - class->set_state = g_action_real_set_state; - - object_class->get_property = g_action_get_property; - object_class->set_property = g_action_set_property; - object_class->finalize = g_action_finalize; - - /** - * GAction::activate: - * @action: the #GAction - * @parameter: (allow-none): the parameter to the activation - * - * Indicates that the action was just activated. - * - * @parameter will always be of the expected type. In the event that - * an incorrect type was given, no signal will be emitted. - * - * Since: 2.26 - */ - g_action_signals[SIGNAL_ACTIVATE] = - g_signal_new (I_("activate"), - G_TYPE_ACTION, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GActionClass, activate), - NULL, NULL, - g_cclosure_marshal_VOID__VARIANT, - G_TYPE_NONE, 1, - G_TYPE_VARIANT); - /** * GAction:name: * @@ -268,14 +72,13 @@ g_action_class_init (GActionClass *class) * * Since: 2.26 **/ - g_object_class_install_property (object_class, PROP_NAME, - g_param_spec_string ("name", - P_("Action Name"), - P_("The name used to invoke the action"), - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); + g_object_interface_install_property (iface, + g_param_spec_string ("name", + P_("Action Name"), + P_("The name used to invoke the action"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); /** * GAction:parameter-type: @@ -285,14 +88,13 @@ g_action_class_init (GActionClass *class) * * Since: 2.26 **/ - g_object_class_install_property (object_class, PROP_PARAMETER_TYPE, - g_param_spec_boxed ("parameter-type", - P_("Parameter Type"), - P_("The type of GVariant passed to activate()"), - G_TYPE_VARIANT_TYPE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); + g_object_interface_install_property (iface, + g_param_spec_boxed ("parameter-type", + P_("Parameter Type"), + P_("The type of GVariant passed to activate()"), + G_TYPE_VARIANT_TYPE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); /** * GAction:enabled: @@ -304,14 +106,13 @@ g_action_class_init (GActionClass *class) * * Since: 2.26 **/ - g_object_class_install_property (object_class, PROP_ENABLED, - g_param_spec_boolean ("enabled", - P_("Enabled"), - P_("If the action can be activated"), - TRUE, - G_PARAM_CONSTRUCT | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + g_object_interface_install_property (iface, + g_param_spec_boolean ("enabled", + P_("Enabled"), + P_("If the action can be activated"), + TRUE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); /** * GAction:state-type: @@ -321,13 +122,13 @@ g_action_class_init (GActionClass *class) * * Since: 2.26 **/ - g_object_class_install_property (object_class, PROP_STATE_TYPE, - g_param_spec_boxed ("state-type", - P_("State Type"), - P_("The type of the state kept by the action"), - G_TYPE_VARIANT_TYPE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + g_object_interface_install_property (iface, + g_param_spec_boxed ("state-type", + P_("State Type"), + P_("The type of the state kept by the action"), + G_TYPE_VARIANT_TYPE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); /** * GAction:state: @@ -336,17 +137,15 @@ g_action_class_init (GActionClass *class) * * Since: 2.26 **/ - g_object_class_install_property (object_class, PROP_STATE, - g_param_spec_variant ("state", - P_("State"), - P_("The state the action is in"), - G_VARIANT_TYPE_ANY, - NULL, - G_PARAM_CONSTRUCT | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_type_class_add_private (class, sizeof (GActionPrivate)); + g_object_interface_install_property (iface, + g_param_spec_variant ("state", + P_("State"), + P_("The state the action is in"), + G_VARIANT_TYPE_ANY, + NULL, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); } /** @@ -381,9 +180,8 @@ g_action_set_state (GAction *action, g_variant_ref_sink (value); - if (action->priv->enabled) - G_ACTION_GET_CLASS (action) - ->set_state (action, value); + G_ACTION_GET_IFACE (action) + ->set_state (action, value); g_variant_unref (value); } @@ -398,8 +196,8 @@ g_action_set_state (GAction *action, * action is stateful then the type of the return value is the type * given by g_action_get_state_type(). * - * The return value should be released with g_variant_unref() when it is - * no longer required. + * The return value (if non-%NULL) should be freed with + * g_variant_unref() when it is no longer required. * * Returns: (allow-none): the current state of the action * @@ -410,7 +208,8 @@ g_action_get_state (GAction *action) { g_return_val_if_fail (G_IS_ACTION (action), NULL); - return action->priv->state ? g_variant_ref (action->priv->state) : NULL; + return G_ACTION_GET_IFACE (action) + ->get_state (action); } /** @@ -428,7 +227,8 @@ g_action_get_name (GAction *action) { g_return_val_if_fail (G_IS_ACTION (action), NULL); - return action->priv->name; + return G_ACTION_GET_IFACE (action) + ->get_name (action); } /** @@ -453,7 +253,8 @@ g_action_get_parameter_type (GAction *action) { g_return_val_if_fail (G_IS_ACTION (action), NULL); - return action->priv->parameter_type; + return G_ACTION_GET_IFACE (action) + ->get_parameter_type (action); } /** @@ -482,10 +283,8 @@ g_action_get_state_type (GAction *action) { g_return_val_if_fail (G_IS_ACTION (action), NULL); - if (action->priv->state != NULL) - return g_variant_get_type (action->priv->state); - else - return NULL; + return G_ACTION_GET_IFACE (action) + ->get_state_type (action); } /** @@ -520,7 +319,8 @@ g_action_get_state_hint (GAction *action) { g_return_val_if_fail (G_IS_ACTION (action), NULL); - return G_ACTION_GET_CLASS (action)->get_state_hint (action); + return G_ACTION_GET_IFACE (action) + ->get_state_hint (action); } /** @@ -541,34 +341,8 @@ g_action_get_enabled (GAction *action) { g_return_val_if_fail (G_IS_ACTION (action), FALSE); - return action->priv->enabled; -} - -/** - * g_action_set_enabled: - * @action: a #GAction - * @enabled: whether the action is enabled - * - * Sets the action as enabled or not. - * - * An action must be enabled in order to be activated or in order to - * have its state changed from outside callers. - * - * Since: 2.26 - **/ -void -g_action_set_enabled (GAction *action, - gboolean enabled) -{ - g_return_if_fail (G_IS_ACTION (action)); - - enabled = !!enabled; - - if (action->priv->enabled != enabled) - { - action->priv->enabled = enabled; - g_object_notify (G_OBJECT (action), "enabled"); - } + return G_ACTION_GET_IFACE (action) + ->get_enabled (action); } /** @@ -590,70 +364,12 @@ g_action_activate (GAction *action, { g_return_if_fail (G_IS_ACTION (action)); - g_return_if_fail (action->priv->parameter_type == NULL ? - parameter == NULL : - (parameter != NULL && - g_variant_is_of_type (parameter, - action->priv->parameter_type))); - if (parameter != NULL) g_variant_ref_sink (parameter); - if (action->priv->enabled) - g_signal_emit (action, g_action_signals[SIGNAL_ACTIVATE], 0, parameter); + G_ACTION_GET_IFACE (action) + ->activate (action, parameter); if (parameter != NULL) g_variant_unref (parameter); } - -/** - * g_action_new: - * @name: the name of the action - * @parameter_type: (allow-none): the type of parameter to the activate function - * - * Creates a new action. - * - * The created action is stateless. See g_action_new_stateful(). - * - * Returns: a new #GAction - * - * Since: 2.26 - **/ -GAction * -g_action_new (const gchar *name, - const GVariantType *parameter_type) -{ - return g_object_new (G_TYPE_ACTION, - "name", name, - "parameter-type", parameter_type, - NULL); -} - -/** - * g_action_new_stateful: - * @name: the name of the action - * @parameter_type: (allow-none): the type of the parameter to the activate function - * @state: the initial state of the action - * - * Creates a new stateful action. - * - * @state is the initial state of the action. All future state values - * must have the same #GVariantType as the initial state. - * - * If the @state GVariant is floating, it is consumed. - * - * Returns: a new #GAction - * - * Since: 2.26 - **/ -GAction * -g_action_new_stateful (const gchar *name, - const GVariantType *parameter_type, - GVariant *state) -{ - return g_object_new (G_TYPE_ACTION, - "name", name, - "parameter-type", parameter_type, - "state", state, - NULL); -} diff --git a/gio/gaction.h b/gio/gaction.h index 68d4b65f1..5bf745624 100644 --- a/gio/gaction.h +++ b/gio/gaction.h @@ -33,82 +33,53 @@ G_BEGIN_DECLS #define G_TYPE_ACTION (g_action_get_type ()) #define G_ACTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ G_TYPE_ACTION, GAction)) -#define G_ACTION_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ - G_TYPE_ACTION, GActionClass)) #define G_IS_ACTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_ACTION)) -#define G_IS_ACTION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_ACTION)) -#define G_ACTION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ - G_TYPE_ACTION, GActionClass)) +#define G_ACTION_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ + G_TYPE_ACTION, GActionInterface)) -typedef struct _GActionPrivate GActionPrivate; -typedef struct _GActionClass GActionClass; +typedef struct _GActionInterface GActionInterface; /** - * GAction: - * - * The GAction structure contains private - * data and should only be accessed using the provided API - * - * Since: 2.26 - */ -struct _GAction -{ - /*< private >*/ - GObject parent_instance; - - GActionPrivate *priv; -}; - -/** - * GActionClass: + * GActionInterface: + * @get_name: the virtual function pointer for g_action_get_name() + * @get_parameter_type: the virtual function pointer for g_action_get_parameter_type() + * @get_state_type: the virtual function pointer for g_action_get_state_type() * @get_state_hint: the virtual function pointer for g_action_get_state_hint() - * @set_state: the virtual function pointer for g_action_set_state(). The implementation should check the value - * for validity and then chain up to the handler in the base class in order to actually update the - * state. - * @activate: the class closure for the activate signal + * @get_enabled: the virtual function pointer for g_action_get_enabled() + * @get_state: the virtual function pointer for g_action_get_state() + * @set_state: the virtual function pointer for g_action_set_state() + * @activate: the virtual function pointer for g_action_activate(). Note that #GAction does not have an + * 'activate' signal but that implementations of it may have one. * * Since: 2.26 */ -struct _GActionClass +struct _GActionInterface { - GObjectClass parent_class; + GTypeInterface g_iface; - /*< public >*/ /* virtual functions */ + const gchar * (* get_name) (GAction *action); + const GVariantType * (* get_parameter_type) (GAction *action); + const GVariantType * (* get_state_type) (GAction *action); GVariant * (* get_state_hint) (GAction *action); + + gboolean (* get_enabled) (GAction *action); + GVariant * (* get_state) (GAction *action); void (* set_state) (GAction *action, GVariant *state); - /*< private >*/ - gpointer vfunc_padding[6]; - - /*< public >*/ - /* signals */ void (* activate) (GAction *action, GVariant *parameter); - - /*< private >*/ - gpointer signal_padding[6]; }; GType g_action_get_type (void) G_GNUC_CONST; -GAction * g_action_new (const gchar *name, - const GVariantType *parameter_type); - -GAction * g_action_new_stateful (const gchar *name, - const GVariantType *parameter_type, - GVariant *state); - const gchar * g_action_get_name (GAction *action); const GVariantType * g_action_get_parameter_type (GAction *action); const GVariantType * g_action_get_state_type (GAction *action); GVariant * g_action_get_state_hint (GAction *action); gboolean g_action_get_enabled (GAction *action); -void g_action_set_enabled (GAction *action, - gboolean enabled); - GVariant * g_action_get_state (GAction *action); void g_action_set_state (GAction *action, GVariant *value); diff --git a/gio/gactiongroup.c b/gio/gactiongroup.c index e1624756d..d60ba9027 100644 --- a/gio/gactiongroup.c +++ b/gio/gactiongroup.c @@ -338,7 +338,7 @@ g_action_group_get_enabled (GActionGroup *action_group, * The return value (if non-%NULL) should be freed with * g_variant_unref() when it is no longer required. * - * Return value: (allow-none) (transfer-none): the current state of the action + * Return value: (allow-none): the current state of the action * * Since: 2.26 **/ diff --git a/gio/gio.h b/gio/gio.h index ad16d9070..db1f13b0e 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include diff --git a/gio/giotypes.h b/gio/giotypes.h index 65788e36f..ecd1dc0c0 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -49,6 +49,7 @@ typedef struct _GZlibDecompressor GZlibDecompressor; typedef struct _GSimpleActionGroup GSimpleActionGroup; typedef struct _GActionGroup GActionGroup; +typedef struct _GSimpleAction GSimpleAction; typedef struct _GAction GAction; typedef struct _GSettingsBackend GSettingsBackend; typedef struct _GSettings GSettings; diff --git a/gio/gsimpleaction.c b/gio/gsimpleaction.c new file mode 100644 index 000000000..fcfedbe2a --- /dev/null +++ b/gio/gsimpleaction.c @@ -0,0 +1,498 @@ +/* + * Copyright © 2010 Codethink Limited + * + * 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 licence or (at + * your option) any later version. + * + * This library 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 this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Ryan Lortie + */ + +#include "config.h" + +#include "gsimpleaction.h" + +#include "gaction.h" +#include "glibintl.h" + +static void g_simple_action_iface_init (GActionInterface *iface); +G_DEFINE_TYPE_WITH_CODE (GSimpleAction, g_simple_action, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ACTION, g_simple_action_iface_init)) + +/** + * SECTION:gsimpleaction + * @title: GSimpleAction + * @short_description: a simple #GSimpleAction + * + * A #GSimpleAction is the obvious simple implementation of the #GSimpleAction + * interface. This is the easiest way to create an action for purposes of + * adding it to a #GSimpleActionGroup. + * + * See also #GtkAction. + **/ + +struct _GSimpleActionPrivate +{ + gchar *name; + GVariantType *parameter_type; + guint enabled : 1; + guint state_set : 1; + GVariant *state; +}; + +enum +{ + PROP_NONE, + PROP_NAME, + PROP_PARAMETER_TYPE, + PROP_ENABLED, + PROP_STATE_TYPE, + PROP_STATE +}; + +enum +{ + SIGNAL_ACTIVATE, + NR_SIGNALS +}; + +static guint g_simple_action_signals[NR_SIGNALS]; + +static const gchar * +g_simple_action_get_name (GAction *action) +{ + GSimpleAction *simple = G_SIMPLE_ACTION (action); + + return simple->priv->name; +} + +const GVariantType * +g_simple_action_get_parameter_type (GAction *action) +{ + GSimpleAction *simple = G_SIMPLE_ACTION (action); + + return simple->priv->parameter_type; +} + +static const GVariantType * +g_simple_action_get_state_type (GAction *action) +{ + GSimpleAction *simple = G_SIMPLE_ACTION (action); + + if (simple->priv->state != NULL) + return g_variant_get_type (simple->priv->state); + else + return NULL; +} + +static GVariant * +g_simple_action_get_state_hint (GAction *action) +{ + return NULL; +} + +static gboolean +g_simple_action_get_enabled (GAction *action) +{ + GSimpleAction *simple = G_SIMPLE_ACTION (action); + + return simple->priv->enabled; +} + +static void +g_simple_action_set_state (GAction *action, + GVariant *value) +{ + GSimpleAction *simple = G_SIMPLE_ACTION (action); + + g_return_if_fail (value != NULL); + + { + const GVariantType *state_type; + + state_type = simple->priv->state ? + g_variant_get_type (simple->priv->state) : NULL; + g_return_if_fail (state_type != NULL); + g_return_if_fail (g_variant_is_of_type (value, state_type)); + } + + g_variant_ref_sink (value); + + if (!g_variant_equal (simple->priv->state, value)) + { + if (simple->priv->state) + g_variant_unref (simple->priv->state); + + simple->priv->state = g_variant_ref (value); + + g_object_notify (G_OBJECT (simple), "state"); + } + + g_variant_unref (value); +} + +static GVariant * +g_simple_action_get_state (GAction *action) +{ + GSimpleAction *simple = G_SIMPLE_ACTION (action); + + return simple->priv->state ? g_variant_ref (simple->priv->state) : NULL; +} + +static void +g_simple_action_activate (GAction *action, + GVariant *parameter) +{ + GSimpleAction *simple = G_SIMPLE_ACTION (action); + + g_return_if_fail (simple->priv->parameter_type == NULL ? + parameter == NULL : + (parameter != NULL && + g_variant_is_of_type (parameter, + simple->priv->parameter_type))); + + if (parameter != NULL) + g_variant_ref_sink (parameter); + + if (simple->priv->enabled) + g_signal_emit (simple, g_simple_action_signals[SIGNAL_ACTIVATE], 0, parameter); + + if (parameter != NULL) + g_variant_unref (parameter); +} + +static void +g_simple_action_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSimpleAction *simple = G_SIMPLE_ACTION (object); + + switch (prop_id) + { + case PROP_NAME: + g_assert (simple->priv->name == NULL); + simple->priv->name = g_value_dup_string (value); + break; + + case PROP_PARAMETER_TYPE: + g_assert (simple->priv->parameter_type == NULL); + simple->priv->parameter_type = g_value_dup_boxed (value); + break; + + case PROP_ENABLED: + g_simple_action_set_enabled (simple, g_value_get_boolean (value)); + break; + + case PROP_STATE: + /* PROP_STATE is marked as G_PARAM_CONSTRUCT so we always get a + * call during object construction, even if it is NULL. We treat + * that first call differently, for a number of reasons. + * + * First, we don't want the value to be rejected by the + * possibly-overridden .set_state() function. Second, we don't + * want to be tripped by the assertions in g_simple_action_set_state() + * that would enforce the catch22 that we only provide a value of + * the same type as the existing value (when there is not yet an + * existing value). + */ + if (simple->priv->state_set) + g_simple_action_set_state (G_ACTION (simple), + g_value_get_variant (value)); + + else /* this is the special case */ + { + /* only do it the first time. */ + simple->priv->state_set = TRUE; + + /* blindly set it. */ + simple->priv->state = g_value_dup_variant (value); + } + break; + + default: + g_assert_not_reached (); + } +} + +static void +g_simple_action_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GAction *action = G_ACTION (object); + + switch (prop_id) + { + case PROP_NAME: + g_value_set_string (value, g_simple_action_get_name (action)); + break; + + case PROP_PARAMETER_TYPE: + g_value_set_boxed (value, g_simple_action_get_parameter_type (action)); + break; + + case PROP_ENABLED: + g_value_set_boolean (value, g_simple_action_get_enabled (action)); + break; + + case PROP_STATE_TYPE: + g_value_set_boxed (value, g_simple_action_get_state_type (action)); + break; + + case PROP_STATE: + g_value_take_variant (value, g_simple_action_get_state (action)); + break; + + default: + g_assert_not_reached (); + } +} + +static void +g_simple_action_finalize (GObject *object) +{ + GSimpleAction *simple = G_SIMPLE_ACTION (object); + + g_free (simple->priv->name); + if (simple->priv->parameter_type) + g_variant_type_free (simple->priv->parameter_type); + if (simple->priv->state) + g_variant_unref (simple->priv->state); + + G_OBJECT_CLASS (g_simple_action_parent_class) + ->finalize (object); +} + +void +g_simple_action_init (GSimpleAction *simple) +{ + simple->priv = G_TYPE_INSTANCE_GET_PRIVATE (simple, + G_TYPE_SIMPLE_ACTION, + GSimpleActionPrivate); +} + +void +g_simple_action_iface_init (GActionInterface *iface) +{ + iface->get_name = g_simple_action_get_name; + iface->get_parameter_type = g_simple_action_get_parameter_type; + iface->get_state_type = g_simple_action_get_state_type; + iface->get_state_hint = g_simple_action_get_state_hint; + iface->get_enabled = g_simple_action_get_enabled; + iface->get_state = g_simple_action_get_state; + iface->set_state = g_simple_action_set_state; + iface->activate = g_simple_action_activate; +} + +void +g_simple_action_class_init (GSimpleActionClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + + object_class->get_property = g_simple_action_get_property; + object_class->set_property = g_simple_action_set_property; + object_class->finalize = g_simple_action_finalize; + + /** + * GSimpleAction::activate: + * @simple: the #GSimpleAction + * @parameter: (allow-none): the parameter to the activation + * + * Indicates that the action was just activated. + * + * @parameter will always be of the expected type. In the event that + * an incorrect type was given, no signal will be emitted. + * + * Since: 2.26 + */ + g_simple_action_signals[SIGNAL_ACTIVATE] = + g_signal_new (I_("activate"), + G_TYPE_SIMPLE_ACTION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSimpleActionClass, activate), + NULL, NULL, + g_cclosure_marshal_VOID__VARIANT, + G_TYPE_NONE, 1, + G_TYPE_VARIANT); + + /** + * GSimpleAction:name: + * + * The name of the action. This is mostly meaningful for identifying + * the action once it has been added to a #GSimpleActionGroup. + * + * Since: 2.26 + **/ + g_object_class_install_property (object_class, PROP_NAME, + g_param_spec_string ("name", + P_("Action Name"), + P_("The name used to invoke the action"), + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GSimpleAction:parameter-type: + * + * The type of the parameter that must be given when activating the + * action. + * + * Since: 2.26 + **/ + g_object_class_install_property (object_class, PROP_PARAMETER_TYPE, + g_param_spec_boxed ("parameter-type", + P_("Parameter Type"), + P_("The type of GVariant passed to activate()"), + G_TYPE_VARIANT_TYPE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GSimpleAction:enabled: + * + * If @action is currently enabled. + * + * If the action is disabled then calls to g_simple_action_activate() and + * g_simple_action_set_state() have no effect. + * + * Since: 2.26 + **/ + g_object_class_install_property (object_class, PROP_ENABLED, + g_param_spec_boolean ("enabled", + P_("Enabled"), + P_("If the action can be activated"), + TRUE, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * GSimpleAction:state-type: + * + * The #GVariantType of the state that the action has, or %NULL if the + * action is stateless. + * + * Since: 2.26 + **/ + g_object_class_install_property (object_class, PROP_STATE_TYPE, + g_param_spec_boxed ("state-type", + P_("State Type"), + P_("The type of the state kept by the action"), + G_TYPE_VARIANT_TYPE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * GSimpleAction:state: + * + * The state of the action, or %NULL if the action is stateless. + * + * Since: 2.26 + **/ + g_object_class_install_property (object_class, PROP_STATE, + g_param_spec_variant ("state", + P_("State"), + P_("The state the action is in"), + G_VARIANT_TYPE_ANY, + NULL, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_type_class_add_private (class, sizeof (GSimpleActionPrivate)); +} + +/** + * g_simple_action_set_enabled: + * @simple: a #GSimpleAction + * @enabled: whether the action is enabled + * + * Sets the action as enabled or not. + * + * An action must be enabled in order to be activated or in order to + * have its state changed from outside callers. + * + * Since: 2.26 + **/ +void +g_simple_action_set_enabled (GSimpleAction *simple, + gboolean enabled) +{ + g_return_if_fail (G_IS_SIMPLE_ACTION (simple)); + + enabled = !!enabled; + + if (simple->priv->enabled != enabled) + { + simple->priv->enabled = enabled; + g_object_notify (G_OBJECT (simple), "enabled"); + } +} +/** + * g_simple_action_new: + * @name: the name of the action + * @parameter_type: (allow-none): the type of parameter to the activate function + * + * Creates a new action. + * + * The created action is stateless. See g_simple_action_new_stateful(). + * + * Returns: a new #GSimpleAction + * + * Since: 2.26 + **/ +GSimpleAction * +g_simple_action_new (const gchar *name, + const GVariantType *parameter_type) +{ + return g_object_new (G_TYPE_SIMPLE_ACTION, + "name", name, + "parameter-type", parameter_type, + NULL); +} + +/** + * g_simple_action_new_stateful: + * @name: the name of the action + * @parameter_type: (allow-none): the type of the parameter to the activate function + * @state: the initial state of the action + * + * Creates a new stateful action. + * + * @state is the initial state of the action. All future state values + * must have the same #GVariantType as the initial state. + * + * If the @state GVariant is floating, it is consumed. + * + * Returns: a new #GSimpleAction + * + * Since: 2.26 + **/ +GSimpleAction * +g_simple_action_new_stateful (const gchar *name, + const GVariantType *parameter_type, + GVariant *state) +{ + return g_object_new (G_TYPE_SIMPLE_ACTION, + "name", name, + "parameter-type", parameter_type, + "state", state, + NULL); +} diff --git a/gio/gsimpleaction.h b/gio/gsimpleaction.h new file mode 100644 index 000000000..a4ddeee78 --- /dev/null +++ b/gio/gsimpleaction.h @@ -0,0 +1,95 @@ +/* + * Copyright © 2010 Codethink Limited + * + * 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 licence or (at + * your option) any later version. + * + * This library 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 this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Ryan Lortie + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_SIMPLE_ACTION_H__ +#define __G_SIMPLE_ACTION_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_SIMPLE_ACTION (g_simple_action_get_type ()) +#define G_SIMPLE_ACTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_SIMPLE_ACTION, GSimpleAction)) +#define G_SIMPLE_ACTION_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_SIMPLE_ACTION, GSimpleActionClass)) +#define G_IS_SIMPLE_ACTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_SIMPLE_ACTION)) +#define G_IS_SIMPLE_ACTION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_SIMPLE_ACTION)) +#define G_SIMPLE_ACTION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_SIMPLE_ACTION, GSimpleActionClass)) + +typedef struct _GSimpleActionPrivate GSimpleActionPrivate; +typedef struct _GSimpleActionClass GSimpleActionClass; + +/** + * GSimpleAction: + * + * The GSimpleAction structure contains private + * data and should only be accessed using the provided API + * + * Since: 2.26 + */ +struct _GSimpleAction +{ + /*< private >*/ + GObject parent_instance; + + GSimpleActionPrivate *priv; +}; + +/** + * GSimpleActionClass: + * @activate: the class closure for the activate signal + * + * Since: 2.26 + */ +struct _GSimpleActionClass +{ + GObjectClass parent_class; + + /* signals */ + void (* activate) (GSimpleAction *simple, + GVariant *parameter); + /*< private >*/ + gpointer padding[6]; +}; + +GType g_simple_action_get_type (void) G_GNUC_CONST; + +GSimpleAction * g_simple_action_new (const gchar *name, + const GVariantType *parameter_type); + +GSimpleAction * g_simple_action_new_stateful (const gchar *name, + const GVariantType *parameter_type, + GVariant *state); + +void g_simple_action_set_enabled (GSimpleAction *simple, + gboolean enabled); + +G_END_DECLS + +#endif /* __G_SIMPLE_ACTION_H__ */ diff --git a/gio/tests/actions.c b/gio/tests/actions.c index 9da72bcfc..436b0e13e 100644 --- a/gio/tests/actions.c +++ b/gio/tests/actions.c @@ -25,19 +25,19 @@ static void test_basic (void) { Activation a = { 0, }; - GAction *action; - const gchar *name; + GSimpleAction *action; + gchar *name; GVariantType *parameter_type; gboolean enabled; GVariantType *state_type; GVariant *state; - action = g_action_new ("foo", NULL); - g_assert (g_action_get_enabled (action)); - g_assert (g_action_get_parameter_type (action) == NULL); - g_assert (g_action_get_state_type (action) == NULL); - g_assert (g_action_get_state_hint (action) == NULL); - g_assert (g_action_get_state (action) == NULL); + action = g_simple_action_new ("foo", NULL); + g_assert (g_action_get_enabled (G_ACTION (action))); + g_assert (g_action_get_parameter_type (G_ACTION (action)) == NULL); + g_assert (g_action_get_state_type (G_ACTION (action)) == NULL); + g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL); + g_assert (g_action_get_state (G_ACTION (action)) == NULL); g_object_get (action, "name", &name, "parameter-type", ¶meter_type, @@ -54,17 +54,17 @@ test_basic (void) g_signal_connect (action, "activate", G_CALLBACK (activate), &a); g_assert (!a.did_run); - g_action_activate (action, NULL); + g_action_activate (G_ACTION (action), NULL); g_assert (a.did_run); a.did_run = FALSE; - g_action_set_enabled (action, FALSE); - g_action_activate (action, NULL); + g_simple_action_set_enabled (action, FALSE); + g_action_activate (G_ACTION (action), NULL); g_assert (!a.did_run); if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) { - g_action_activate (action, g_variant_new_string ("xxx")); + g_action_activate (G_ACTION (action), g_variant_new_string ("xxx")); exit (0); } g_test_trap_assert_failed (); @@ -72,16 +72,16 @@ test_basic (void) g_object_unref (action); g_assert (!a.did_run); - action = g_action_new ("foo", G_VARIANT_TYPE_STRING); - g_assert (g_action_get_enabled (action)); - g_assert (g_variant_type_equal (g_action_get_parameter_type (action), G_VARIANT_TYPE_STRING)); - g_assert (g_action_get_state_type (action) == NULL); - g_assert (g_action_get_state_hint (action) == NULL); - g_assert (g_action_get_state (action) == NULL); + action = g_simple_action_new ("foo", G_VARIANT_TYPE_STRING); + g_assert (g_action_get_enabled (G_ACTION (action))); + g_assert (g_variant_type_equal (g_action_get_parameter_type (G_ACTION (action)), G_VARIANT_TYPE_STRING)); + g_assert (g_action_get_state_type (G_ACTION (action)) == NULL); + g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL); + g_assert (g_action_get_state (G_ACTION (action)) == NULL); g_signal_connect (action, "activate", G_CALLBACK (activate), &a); g_assert (!a.did_run); - g_action_activate (action, g_variant_new_string ("Hello world")); + g_action_activate (G_ACTION (action), g_variant_new_string ("Hello world")); g_assert (a.did_run); g_assert_cmpstr (g_variant_get_string (a.params, NULL), ==, "Hello world"); g_variant_unref (a.params); @@ -89,7 +89,7 @@ test_basic (void) if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) { - g_action_activate (action, NULL); + g_action_activate (G_ACTION (action), NULL); exit (0); } @@ -149,28 +149,29 @@ test_simple_group (void) { GSimpleActionGroup *group; Activation a = { 0, }; + GSimpleAction *simple; GAction *action; gchar **actions; GVariant *state; - action = g_action_new ("foo", NULL); - g_signal_connect (action, "activate", G_CALLBACK (activate), &a); + simple = g_simple_action_new ("foo", NULL); + g_signal_connect (simple, "activate", G_CALLBACK (activate), &a); g_assert (!a.did_run); - g_action_activate (action, NULL); + g_action_activate (G_ACTION (simple), NULL); g_assert (a.did_run); a.did_run = FALSE; group = g_simple_action_group_new (); - g_simple_action_group_insert (group, action); - g_object_unref (action); + g_simple_action_group_insert (group, G_ACTION (simple)); + g_object_unref (simple); g_assert (!a.did_run); g_action_group_activate (G_ACTION_GROUP (group), "foo", NULL); g_assert (a.did_run); - action = g_action_new_stateful ("bar", G_VARIANT_TYPE_STRING, g_variant_new_string ("hihi")); - g_simple_action_group_insert (group, action); - g_object_unref (action); + simple = g_simple_action_new_stateful ("bar", G_VARIANT_TYPE_STRING, g_variant_new_string ("hihi")); + g_simple_action_group_insert (group, G_ACTION (simple)); + g_object_unref (simple); g_assert (g_action_group_has_action (G_ACTION_GROUP (group), "foo")); g_assert (g_action_group_has_action (G_ACTION_GROUP (group), "bar")); @@ -199,7 +200,7 @@ test_simple_group (void) g_variant_unref (state); action = g_simple_action_group_lookup (group, "bar"); - g_action_set_enabled (action, FALSE); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); g_assert (!g_action_group_get_enabled (G_ACTION_GROUP (group), "bar")); g_simple_action_group_remove (group, "bar"); @@ -216,37 +217,37 @@ test_simple_group (void) static void test_stateful (void) { + GSimpleAction *action; GVariant *state; - GAction *action; - action = g_action_new_stateful ("foo", NULL, g_variant_new_string ("hihi")); - g_assert (g_action_get_enabled (action)); - g_assert (g_action_get_parameter_type (action) == NULL); - g_assert (g_action_get_state_hint (action) == NULL); - g_assert (g_variant_type_equal (g_action_get_state_type (action), + action = g_simple_action_new_stateful ("foo", NULL, g_variant_new_string ("hihi")); + g_assert (g_action_get_enabled (G_ACTION (action))); + g_assert (g_action_get_parameter_type (G_ACTION (action)) == NULL); + g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL); + g_assert (g_variant_type_equal (g_action_get_state_type (G_ACTION (action)), G_VARIANT_TYPE_STRING)); - state = g_action_get_state (action); + state = g_action_get_state (G_ACTION (action)); g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hihi"); g_variant_unref (state); if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) { - g_action_set_state (action, g_variant_new_int32 (123)); + g_action_set_state (G_ACTION (action), g_variant_new_int32 (123)); exit (0); } g_test_trap_assert_failed (); - g_action_set_state (action, g_variant_new_string ("hello")); - state = g_action_get_state (action); + g_action_set_state (G_ACTION (action), g_variant_new_string ("hello")); + state = g_action_get_state (G_ACTION (action)); g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hello"); g_variant_unref (state); g_object_unref (action); - action = g_action_new ("foo", NULL); + action = g_simple_action_new ("foo", NULL); if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) { - g_action_set_state (action, g_variant_new_int32 (123)); + g_action_set_state (G_ACTION (action), g_variant_new_int32 (123)); exit (0); } g_test_trap_assert_failed ();