/* * Copyright © 2010 Codethink Limited * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library 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.1 of the License, 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, see . * * Authors: Ryan Lortie */ #include "config.h" #include "gsimpleaction.h" #include "gaction.h" #include "glibintl.h" /** * GSimpleAction: * * A `GSimpleAction` is the obvious simple implementation of the * [iface@Gio.Action] interface. This is the easiest way to create an action for * purposes of adding it to a [class@Gio.SimpleActionGroup]. */ struct _GSimpleAction { GObject parent_instance; gchar *name; GVariantType *parameter_type; gboolean enabled; GVariant *state; GVariant *state_hint; gboolean state_set_already; }; typedef GObjectClass GSimpleActionClass; 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)) enum { PROP_NONE, PROP_NAME, PROP_PARAMETER_TYPE, PROP_ENABLED, PROP_STATE_TYPE, PROP_STATE }; enum { SIGNAL_CHANGE_STATE, 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->name; } static const GVariantType * g_simple_action_get_parameter_type (GAction *action) { GSimpleAction *simple = G_SIMPLE_ACTION (action); return simple->parameter_type; } static const GVariantType * g_simple_action_get_state_type (GAction *action) { GSimpleAction *simple = G_SIMPLE_ACTION (action); if (simple->state != NULL) return g_variant_get_type (simple->state); else return NULL; } static GVariant * g_simple_action_get_state_hint (GAction *action) { GSimpleAction *simple = G_SIMPLE_ACTION (action); if (simple->state_hint != NULL) return g_variant_ref (simple->state_hint); else return NULL; } static gboolean g_simple_action_get_enabled (GAction *action) { GSimpleAction *simple = G_SIMPLE_ACTION (action); return simple->enabled; } static void g_simple_action_change_state (GAction *action, GVariant *value) { GSimpleAction *simple = G_SIMPLE_ACTION (action); /* If the user connected a signal handler then they are responsible * for handling state changes. */ if (g_signal_has_handler_pending (action, g_simple_action_signals[SIGNAL_CHANGE_STATE], 0, TRUE)) g_signal_emit (action, g_simple_action_signals[SIGNAL_CHANGE_STATE], 0, value); /* If not, then the default behaviour is to just set the state. */ else g_simple_action_set_state (simple, value); } /** * g_simple_action_set_state: * @simple: a #GSimpleAction * @value: the new #GVariant for the state * * Sets the state of the action. * * This directly updates the 'state' property to the given value. * * This should only be called by the implementor of the action. Users * of the action should not attempt to directly modify the 'state' * property. Instead, they should call g_action_change_state() to * request the change. * * If the @value GVariant is floating, it is consumed. * * Since: 2.30 **/ void g_simple_action_set_state (GSimpleAction *simple, GVariant *value) { g_return_if_fail (G_IS_SIMPLE_ACTION (simple)); g_return_if_fail (value != NULL); { const GVariantType *state_type; state_type = simple->state ? g_variant_get_type (simple->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 (!simple->state || !g_variant_equal (simple->state, value)) { if (simple->state) g_variant_unref (simple->state); simple->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->state ? g_variant_ref (simple->state) : NULL; } static void g_simple_action_activate (GAction *action, GVariant *parameter) { GSimpleAction *simple = G_SIMPLE_ACTION (action); g_return_if_fail (simple->parameter_type == NULL ? parameter == NULL : (parameter != NULL && g_variant_is_of_type (parameter, simple->parameter_type))); if (parameter != NULL) g_variant_ref_sink (parameter); if (simple->enabled) { /* If the user connected a signal handler then they are responsible * for handling activation. */ if (g_signal_has_handler_pending (action, g_simple_action_signals[SIGNAL_ACTIVATE], 0, TRUE)) g_signal_emit (action, g_simple_action_signals[SIGNAL_ACTIVATE], 0, parameter); /* If not, do some reasonable defaults for stateful actions. */ else if (simple->state) { /* If we have no parameter and this is a boolean action, toggle. */ if (parameter == NULL && g_variant_is_of_type (simple->state, G_VARIANT_TYPE_BOOLEAN)) { gboolean was_enabled = g_variant_get_boolean (simple->state); g_simple_action_change_state (action, g_variant_new_boolean (!was_enabled)); } /* else, if the parameter and state type are the same, do a change-state */ else if (g_variant_is_of_type (simple->state, g_variant_get_type (parameter))) g_simple_action_change_state (action, 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 *action = G_SIMPLE_ACTION (object); switch (prop_id) { case PROP_NAME: action->name = g_strdup (g_value_get_string (value)); break; case PROP_PARAMETER_TYPE: action->parameter_type = g_value_dup_boxed (value); break; case PROP_ENABLED: action->enabled = g_value_get_boolean (value); break; case PROP_STATE: /* The first time we see this (during construct) we should just * take the state as it was handed to us. * * After that, we should make sure we go through the same checks * as the C API. */ if (!action->state_set_already) { action->state = g_value_dup_variant (value); action->state_set_already = TRUE; } else g_simple_action_set_state (action, g_value_get_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->name); if (simple->parameter_type) g_variant_type_free (simple->parameter_type); if (simple->state) g_variant_unref (simple->state); if (simple->state_hint) g_variant_unref (simple->state_hint); G_OBJECT_CLASS (g_simple_action_parent_class) ->finalize (object); } void g_simple_action_init (GSimpleAction *simple) { simple->enabled = TRUE; } 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->change_state = g_simple_action_change_state; iface->activate = g_simple_action_activate; } void g_simple_action_class_init (GSimpleActionClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); object_class->set_property = g_simple_action_set_property; object_class->get_property = g_simple_action_get_property; object_class->finalize = g_simple_action_finalize; /** * GSimpleAction::activate: * @simple: the #GSimpleAction * @parameter: (nullable): the parameter to the activation, or %NULL if it has * no parameter * * Indicates that the action was just activated. * * @parameter will always be of the expected type, i.e. the parameter type * specified when the action was created. If an incorrect type is given when * activating the action, this signal is not emitted. * * Since GLib 2.40, if no handler is connected to this signal then the * default behaviour for boolean-stated actions with a %NULL parameter * type is to toggle them via the #GSimpleAction::change-state signal. * For stateful actions where the state type is equal to the parameter * type, the default is to forward them directly to * #GSimpleAction::change-state. This should allow almost all users * of #GSimpleAction to connect only one handler or the other. * * Since: 2.28 */ g_simple_action_signals[SIGNAL_ACTIVATE] = g_signal_new (I_("activate"), G_TYPE_SIMPLE_ACTION, G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_VARIANT); /** * GSimpleAction::change-state: * @simple: the #GSimpleAction * @value: (nullable): the requested value for the state * * Indicates that the action just received a request to change its * state. * * @value will always be of the correct state type, i.e. the type of the * initial state passed to g_simple_action_new_stateful(). If an incorrect * type is given when requesting to change the state, this signal is not * emitted. * * If no handler is connected to this signal then the default * behaviour is to call g_simple_action_set_state() to set the state * to the requested value. If you connect a signal handler then no * default action is taken. If the state should change then you must * call g_simple_action_set_state() from the handler. * * An example of a 'change-state' handler: * |[ * static void * change_volume_state (GSimpleAction *action, * GVariant *value, * gpointer user_data) * { * gint requested; * * requested = g_variant_get_int32 (value); * * // Volume only goes from 0 to 10 * if (0 <= requested && requested <= 10) * g_simple_action_set_state (action, value); * } * ]| * * The handler need not set the state to the requested value. * It could set it to any value at all, or take some other action. * * Since: 2.30 */ g_simple_action_signals[SIGNAL_CHANGE_STATE] = g_signal_new (I_("change-state"), G_TYPE_SIMPLE_ACTION, G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT, 0, NULL, NULL, NULL, 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.28 **/ g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", NULL, NULL, 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.28 **/ g_object_class_install_property (object_class, PROP_PARAMETER_TYPE, g_param_spec_boxed ("parameter-type", NULL, NULL, 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_action_activate() and * g_action_change_state() have no effect. * * Since: 2.28 **/ g_object_class_install_property (object_class, PROP_ENABLED, g_param_spec_boolean ("enabled", NULL, NULL, TRUE, 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.28 **/ g_object_class_install_property (object_class, PROP_STATE_TYPE, g_param_spec_boxed ("state-type", NULL, NULL, 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.28 **/ g_object_class_install_property (object_class, PROP_STATE, g_param_spec_variant ("state", NULL, NULL, G_VARIANT_TYPE_ANY, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); } /** * 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. * * This should only be called by the implementor of the action. Users * of the action should not attempt to modify its enabled flag. * * Since: 2.28 **/ void g_simple_action_set_enabled (GSimpleAction *simple, gboolean enabled) { g_return_if_fail (G_IS_SIMPLE_ACTION (simple)); enabled = !!enabled; if (simple->enabled != enabled) { simple->enabled = enabled; g_object_notify (G_OBJECT (simple), "enabled"); } } /** * g_simple_action_set_state_hint: * @simple: a #GSimpleAction * @state_hint: (nullable): a #GVariant representing the state hint * * Sets the state hint for the action. * * See g_action_get_state_hint() for more information about * action state hints. * * Since: 2.44 **/ void g_simple_action_set_state_hint (GSimpleAction *simple, GVariant *state_hint) { g_return_if_fail (G_IS_SIMPLE_ACTION (simple)); if (simple->state_hint != NULL) { g_variant_unref (simple->state_hint); simple->state_hint = NULL; } if (state_hint != NULL) simple->state_hint = g_variant_ref (state_hint); } /** * g_simple_action_new: * @name: the name of the action * @parameter_type: (nullable): the type of parameter that will be passed to * handlers for the #GSimpleAction::activate signal, or %NULL for no parameter * * Creates a new action. * * The created action is stateless. See g_simple_action_new_stateful() to create * an action that has state. * * Returns: a new #GSimpleAction * * Since: 2.28 **/ GSimpleAction * g_simple_action_new (const gchar *name, const GVariantType *parameter_type) { g_return_val_if_fail (name != NULL, NULL); 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: (nullable): the type of the parameter that will be passed to * handlers for the #GSimpleAction::activate signal, or %NULL for no parameter * @state: the initial state of the action * * Creates a new stateful 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.28 **/ 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); }