/* dzl-child-property-action.c
*
* Copyright (C) 2017 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define G_LOG_DOMAIN "dzl-child-property-action"
#include "config.h"
#include "dzl-child-property-action.h"
#include "../util/dzl-util-private.h"
struct _DzlChildPropertyAction
{
GObject parent_instance;
GtkContainer *container;
GtkWidget *child;
const gchar *child_property_name;
const gchar *name;
};
enum {
PROP_0,
PROP_CHILD,
PROP_CHILD_PROPERTY_NAME,
PROP_CONTAINER,
N_PROPS,
PROP_ENABLED,
PROP_NAME,
PROP_PARAMETER_TYPE,
PROP_STATE,
PROP_STATE_TYPE,
};
static void action_iface_init (GActionInterface *iface);
G_DEFINE_TYPE_WITH_CODE (DzlChildPropertyAction, dzl_child_property_action, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION, action_iface_init))
static GParamSpec *properties [N_PROPS];
static const gchar *
dzl_child_property_action_get_name (GAction *action)
{
return DZL_CHILD_PROPERTY_ACTION (action)->name;
}
static const GVariantType *
dzl_child_property_action_get_state_type (GAction *action)
{
DzlChildPropertyAction *self = DZL_CHILD_PROPERTY_ACTION (action);
if (self->container != NULL &&
self->child != NULL &&
self->child_property_name != NULL)
{
GParamSpec *pspec;
pspec = gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (self->container),
self->child_property_name);
if (pspec != NULL)
{
if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
return G_VARIANT_TYPE ("b");
else if (G_IS_PARAM_SPEC_INT (pspec))
return G_VARIANT_TYPE ("i");
else if (G_IS_PARAM_SPEC_UINT (pspec))
return G_VARIANT_TYPE ("u");
else if (G_IS_PARAM_SPEC_STRING (pspec))
return G_VARIANT_TYPE ("s");
else if (G_IS_PARAM_SPEC_DOUBLE (pspec))
return G_VARIANT_TYPE ("d");
else if (G_IS_PARAM_SPEC_FLOAT (pspec))
return G_VARIANT_TYPE ("d");
}
}
g_warning ("Failed to discover state type for child property %s",
self->child_property_name);
return NULL;
}
static const GVariantType *
dzl_child_property_action_get_parameter_type (GAction *action)
{
const GVariantType *state_type = g_action_get_state_type (action);
if (g_variant_type_equal (state_type, G_VARIANT_TYPE ("b")))
return NULL;
return state_type;
}
static GVariant *
dzl_child_property_action_get_state_hint (GAction *action)
{
return NULL;
}
static gboolean
dzl_child_property_action_get_enabled (GAction *action)
{
return TRUE;
}
static GVariant *
dzl_child_property_action_get_state (GAction *action)
{
DzlChildPropertyAction *self = DZL_CHILD_PROPERTY_ACTION (action);
g_assert (DZL_IS_CHILD_PROPERTY_ACTION (self));
if (self->container != NULL &&
self->child != NULL &&
self->child_property_name != NULL)
{
GParamSpec *pspec;
pspec = gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (self->container),
self->child_property_name);
if (pspec != NULL)
{
g_auto(GValue) value = G_VALUE_INIT;
GVariant *ret = NULL;
g_value_init (&value, pspec->value_type);
gtk_container_child_get_property (self->container,
self->child,
self->child_property_name,
&value);
if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
ret = g_variant_new_boolean (g_value_get_boolean (&value));
else if (G_IS_PARAM_SPEC_INT (pspec))
ret = g_variant_new_int32 (g_value_get_int (&value));
else if (G_IS_PARAM_SPEC_UINT (pspec))
ret = g_variant_new_uint32 (g_value_get_uint (&value));
else if (G_IS_PARAM_SPEC_STRING (pspec))
ret = g_variant_new_string (g_value_get_string (&value));
else if (G_IS_PARAM_SPEC_DOUBLE (pspec))
ret = g_variant_new_double (g_value_get_double (&value));
else if (G_IS_PARAM_SPEC_FLOAT (pspec))
ret = g_variant_new_double (g_value_get_double (&value));
if (ret)
return g_variant_ref_sink (ret);
}
}
g_warning ("Failed to determine default state");
return NULL;
}
static void
dzl_child_property_action_change_state (GAction *action,
GVariant *state)
{
DzlChildPropertyAction *self = DZL_CHILD_PROPERTY_ACTION (action);
if (self->container != NULL &&
self->child != NULL &&
self->child_property_name != NULL)
{
GParamSpec *pspec;
pspec = gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (self->container),
self->child_property_name);
if (pspec != NULL)
{
g_auto(GValue) value = G_VALUE_INIT;
g_value_init (&value, pspec->value_type);
if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
{
if (!g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
{
g_warning ("Expected 'b', got %s", g_variant_get_type_string (state));
return;
}
g_value_set_boolean (&value, g_variant_get_boolean (state));
}
else if (G_IS_PARAM_SPEC_INT (pspec))
{
if (!g_variant_is_of_type (state, G_VARIANT_TYPE_INT32))
{
g_warning ("Expected 'i', got %s", g_variant_get_type_string (state));
return;
}
g_value_set_int (&value, g_variant_get_int32 (state));
}
else if (G_IS_PARAM_SPEC_UINT (pspec))
{
if (!g_variant_is_of_type (state, G_VARIANT_TYPE_UINT32))
{
g_warning ("Expected 'u', got %s", g_variant_get_type_string (state));
return;
}
g_value_set_uint (&value, g_variant_get_uint32 (state));
}
else if (G_IS_PARAM_SPEC_STRING (pspec))
{
if (!g_variant_is_of_type (state, G_VARIANT_TYPE_STRING))
{
g_warning ("Expected 's', got %s", g_variant_get_type_string (state));
return;
}
g_value_set_string (&value, g_variant_get_string (state, NULL));
}
else if (G_IS_PARAM_SPEC_DOUBLE (pspec) || G_IS_PARAM_SPEC_FLOAT (pspec))
{
if (!g_variant_is_of_type (state, G_VARIANT_TYPE_STRING))
{
g_warning ("Expected 'd', got %s", g_variant_get_type_string (state));
return;
}
if (G_IS_PARAM_SPEC_DOUBLE (pspec))
g_value_set_double (&value, g_variant_get_double (state));
else
g_value_set_float (&value, g_variant_get_double (state));
}
else
{
g_warning ("I don't know how to handle %s property types.",
g_type_name (pspec->value_type));
return;
}
gtk_container_child_set_property (self->container,
self->child,
self->child_property_name,
&value);
g_object_notify (G_OBJECT (self), "state");
return;
}
}
g_warning ("Attempt to change state on incapable child property action");
}
static void
dzl_child_property_action_activate (GAction *action,
GVariant *parameter)
{
DzlChildPropertyAction *self = (DzlChildPropertyAction *)action;
g_assert (DZL_IS_CHILD_PROPERTY_ACTION (self));
if (self->container != NULL &&
self->child != NULL &&
self->child_property_name != NULL)
{
GParamSpec *pspec;
pspec = gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (self->container),
self->child_property_name);
if (pspec != NULL)
{
g_auto(GValue) value = G_VALUE_INIT;
if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
{
g_value_init (&value, G_TYPE_BOOLEAN);
if (parameter != NULL)
g_value_set_boolean (&value, g_variant_get_boolean (parameter));
else
{
g_auto(GValue) previous = G_VALUE_INIT;
g_value_init (&previous, G_TYPE_BOOLEAN);
gtk_container_child_get_property (self->container,
self->child,
self->child_property_name,
&previous);
g_value_set_boolean (&value, !g_value_get_boolean (&previous));
}
}
else if (G_IS_PARAM_SPEC_INT (pspec) && parameter != NULL)
{
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, g_variant_get_int32 (parameter));
}
else if (G_IS_PARAM_SPEC_UINT (pspec) && parameter != NULL)
{
g_value_init (&value, G_TYPE_UINT);
g_value_set_uint (&value, g_variant_get_uint32 (parameter));
}
else if (G_IS_PARAM_SPEC_STRING (pspec) && parameter != NULL)
{
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, g_variant_get_string (parameter, NULL));
}
else if (G_IS_PARAM_SPEC_DOUBLE (pspec) || G_IS_PARAM_SPEC_FLOAT (pspec))
{
if (parameter != NULL)
{
g_value_init (&value, G_TYPE_DOUBLE);
g_value_set_double (&value, g_variant_get_double (parameter));
}
}
else
{
g_warning ("Failed to transform state type");
return;
}
gtk_container_child_set_property (self->container, self->child, pspec->name, &value);
return;
}
}
g_warning ("I don't know how to activate %s", self->name);
}
static void
action_iface_init (GActionInterface *iface)
{
iface->get_name = dzl_child_property_action_get_name;
iface->get_parameter_type = dzl_child_property_action_get_parameter_type;
iface->get_state_type = dzl_child_property_action_get_state_type;
iface->get_state_hint = dzl_child_property_action_get_state_hint;
iface->get_enabled = dzl_child_property_action_get_enabled;
iface->get_state = dzl_child_property_action_get_state;
iface->change_state = dzl_child_property_action_change_state;
iface->activate = dzl_child_property_action_activate;
}
static void
child_notify_cb (DzlChildPropertyAction *self,
GParamSpec *pspec,
GtkWidget *child)
{
g_assert (DZL_IS_CHILD_PROPERTY_ACTION (self));
g_assert (pspec != NULL);
g_assert (GTK_IS_WIDGET (child));
g_object_notify (G_OBJECT (self), "state");
}
static void
dzl_child_property_action_dispose (GObject *object)
{
DzlChildPropertyAction *self = (DzlChildPropertyAction *)object;
dzl_clear_weak_pointer (&self->container);
dzl_clear_weak_pointer (&self->child);
G_OBJECT_CLASS (dzl_child_property_action_parent_class)->dispose (object);
}
static void
dzl_child_property_action_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
DzlChildPropertyAction *self = DZL_CHILD_PROPERTY_ACTION (object);
switch (prop_id)
{
case PROP_CONTAINER:
g_value_set_object (value, self->container);
break;
case PROP_CHILD:
g_value_set_object (value, self->child);
break;
case PROP_CHILD_PROPERTY_NAME:
g_value_set_static_string (value, self->child_property_name);
break;
case PROP_ENABLED:
g_value_set_boolean (value, dzl_child_property_action_get_enabled (G_ACTION (self)));
break;
case PROP_PARAMETER_TYPE:
g_value_set_boxed (value, dzl_child_property_action_get_parameter_type (G_ACTION (self)));
break;
case PROP_STATE:
g_value_take_variant (value, dzl_child_property_action_get_state (G_ACTION (self)));
break;
case PROP_STATE_TYPE:
g_value_set_boxed (value, dzl_child_property_action_get_state_type (G_ACTION (self)));
break;
case PROP_NAME:
g_value_set_static_string (value, self->name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
dzl_child_property_action_class_init (DzlChildPropertyActionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = dzl_child_property_action_dispose;
object_class->get_property = dzl_child_property_action_get_property;
g_object_class_override_property (object_class, PROP_ENABLED, "enabled");
g_object_class_override_property (object_class, PROP_NAME, "name");
g_object_class_override_property (object_class, PROP_PARAMETER_TYPE, "parameter-type");
g_object_class_override_property (object_class, PROP_STATE, "state");
g_object_class_override_property (object_class, PROP_STATE_TYPE, "state-type");
properties [PROP_CHILD] =
g_param_spec_object ("child",
"Child",
"The child widget",
GTK_TYPE_WIDGET,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_CHILD_PROPERTY_NAME] =
g_param_spec_string ("child-property-name",
"Child Property Name",
"The name of the child property",
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_CONTAINER] =
g_param_spec_object ("container",
"Container",
"The container widget",
GTK_TYPE_CONTAINER,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
dzl_child_property_action_init (DzlChildPropertyAction *self)
{
}
/**
* dzl_child_property_action_new:
* @name: the name of the action
* @container: the container of the widget
* @child: the widget for the child property
* @child_property_name: the name of the child property
*
* This creates a new #GAction that will change when the underlying child
* property of @container changes for @child.
*
* Returns: (transfer full): A new #DzlChildPropertyAction.
*/
GAction *
dzl_child_property_action_new (const gchar *name,
GtkContainer *container,
GtkWidget *child,
const gchar *child_property_name)
{
g_autoptr(DzlChildPropertyAction) self = NULL;
g_autofree gchar *signal_detail = NULL;
g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
g_return_val_if_fail (child_property_name != NULL, NULL);
self = g_object_new (DZL_TYPE_CHILD_PROPERTY_ACTION, NULL);
self->name = g_intern_string (name);
self->child_property_name = g_intern_string (child_property_name);
dzl_set_weak_pointer (&self->container, container);
dzl_set_weak_pointer (&self->child, child);
signal_detail = g_strdup_printf ("child-notify::%s", child_property_name);
g_signal_connect_object (child,
signal_detail,
G_CALLBACK (child_notify_cb),
self,
G_CONNECT_SWAPPED);
return G_ACTION (g_steal_pointer (&self));
}