/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2001 Ximian, Inc.
* Copyright (C) 2006 The GNOME Foundation.
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Authors:
* Chema Celorio <chema@celorio.com>
* Tristan Van Berkom <tvb@gnome.org>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/**
* SECTION:glade-property
* @Title: GladeProperty
* @Short_Description: An interface to properties on the #GladeWidget.
*
* Every object property of every #GladeWidget in every #GladeProject has
* a #GladeProperty to interface with, #GladeProperty provides a means
* to handle properties in the runtime environment.
*
* A #GladeProperty can be seen as an instance of a #GladePropertyClass,
* the #GladePropertyClass describes how a #GladeProperty will function.
*/
#include <stdio.h>
#include <stdlib.h> /* for atoi and atof */
#include <string.h>
#include <glib/gi18n-lib.h>
#include "glade.h"
#include "glade-widget.h"
#include "glade-property.h"
#include "glade-property-class.h"
#include "glade-parameter.h"
#include "glade-project.h"
#include "glade-widget-adaptor.h"
#include "glade-debug.h"
#include "glade-app.h"
#include "glade-editor.h"
#include "glade-marshallers.h"
enum
{
VALUE_CHANGED,
TOOLTIP_CHANGED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_ENABLED,
PROP_SENSITIVE,
PROP_I18N_TRANSLATABLE,
PROP_I18N_HAS_CONTEXT,
PROP_I18N_COMMENT
};
static guint glade_property_signals[LAST_SIGNAL] = { 0 };
static GObjectClass* parent_class = NULL;
/*******************************************************************************
GladeProperty class methods
*******************************************************************************/
static GladeProperty *
glade_property_dup_impl (GladeProperty *template_prop, GladeWidget *widget)
{
GladeProperty *property;
property = g_object_new (GLADE_TYPE_PROPERTY,
"enabled", template_prop->enabled,
"sensitive", template_prop->sensitive,
"i18n-translatable", template_prop->i18n_translatable,
"i18n-has-context", template_prop->i18n_has_context,
"i18n-comment", template_prop->i18n_comment,
NULL);
property->klass = template_prop->klass;
property->widget = widget;
property->value = g_new0 (GValue, 1);
property->insensitive_tooltip =
template_prop->insensitive_tooltip ?
g_strdup (template_prop->insensitive_tooltip) : NULL;
g_value_init (property->value, template_prop->value->g_type);
g_value_copy (template_prop->value, property->value);
return property;
}
static gboolean
glade_property_equals_value_impl (GladeProperty *property,
const GValue *value)
{
if (G_IS_PARAM_SPEC_STRING (property->klass->pspec))
{
const gchar *prop_str, *value_str;
/* in string specs; NULL and '\0' are
* treated as equivalent.
*/
prop_str = g_value_get_string (property->value);
value_str = g_value_get_string (value);
if (prop_str == NULL && value_str && value_str[0] == '\0')
return TRUE;
else if (value_str == NULL && prop_str && prop_str[0] == '\0')
return TRUE;
}
return !g_param_values_cmp (property->klass->pspec,
property->value, value);
}
static void
glade_property_update_prop_refs (GladeProperty *property,
const GValue *old_value,
const GValue *new_value)
{
GladeWidget *gold, *gnew;
GObject *old_object, *new_object;
GList *old_list, *new_list, *list, *removed, *added;
if (GLADE_IS_PARAM_SPEC_OBJECTS (property->klass->pspec))
{
/* Make our own copies incase we're walking an
* unstable list
*/
old_list = g_value_dup_boxed (old_value);
new_list = g_value_dup_boxed (new_value);
/* Diff up the GList */
removed = glade_util_removed_from_list (old_list, new_list);
added = glade_util_added_in_list (old_list, new_list);
/* Adjust the appropriate prop refs */
for (list = removed; list; list = list->next)
{
old_object = list->data;
gold = glade_widget_get_from_gobject (old_object);
glade_widget_remove_prop_ref (gold, property);
}
for (list = added; list; list = list->next)
{
new_object = list->data;
gnew = glade_widget_get_from_gobject (new_object);
glade_widget_add_prop_ref (gnew, property);
}
g_list_free (removed);
g_list_free (added);
g_list_free (old_list);
g_list_free (new_list);
}
else
{
if ((old_object = g_value_get_object (old_value)) != NULL)
{
gold = glade_widget_get_from_gobject (old_object);
glade_widget_remove_prop_ref (gold, property);
}
if ((new_object = g_value_get_object (new_value)) != NULL)
{
gnew = glade_widget_get_from_gobject (new_object);
glade_widget_add_prop_ref (gnew, property);
}
}
}
static gboolean
glade_property_verify (GladeProperty *property, const GValue *value)
{
if (property->klass->packing)
{
if (property->widget->parent)
return glade_widget_adaptor_child_verify_property (property->widget->parent->adaptor,
property->widget->parent->object,
property->widget->object,
property->klass->id,
value);
else
return FALSE;
}
else
{
return glade_widget_adaptor_verify_property (property->widget->adaptor,
property->widget->object,
property->klass->id,
value);
}
}
static void
glade_property_set_value_impl (GladeProperty *property, const GValue *value)
{
GladeProject *project = property->widget ?
glade_widget_get_project (property->widget) : NULL;
gboolean changed = FALSE;
GValue old_value = {0,};
#if 0
{
gchar *str = glade_property_class_make_string_from_gvalue
(property->klass, value);
g_print ("Setting property %s on %s to %s\n",
property->klass->id,
property->widget ? property->widget->name : "unknown", str);
g_free (str);
}
#endif
if (!g_value_type_compatible (G_VALUE_TYPE (property->value), G_VALUE_TYPE (value)))
{
g_warning ("Trying to assign an incompatible value to property %s\n",
property->klass->id);
return;
}
/* Check if the backend doesnt give us permission to
* set this value.
*/
if (glade_property_superuser () == FALSE && property->widget &&
project && glade_project_is_loading (project) == FALSE &&
glade_property_verify (property, value) == FALSE)
return;
/* save "changed" state.
*/
changed = g_param_values_cmp (property->klass->pspec,
property->value, value) != 0;
/* Add/Remove references from widget ref stacks here
* (before assigning the value)
*/
if (property->widget && changed && glade_property_class_is_object (property->klass))
glade_property_update_prop_refs (property, property->value, value);
/* Make a copy of the old value */
g_value_init (&old_value, G_VALUE_TYPE (property->value));
g_value_copy (property->value, &old_value);
/* Assign property first so that; if the object need be
* rebuilt, it will reflect the new value
*/
g_value_reset (property->value);
g_value_copy (value, property->value);
GLADE_PROPERTY_GET_KLASS (property)->sync (property);
if (changed && property->widget)
{
g_signal_emit (G_OBJECT (property),
glade_property_signals[VALUE_CHANGED],
0, &old_value, property->value);
}
g_value_unset (&old_value);
}
static void
glade_property_get_value_impl (GladeProperty *property, GValue *value)
{
g_value_init (value, property->klass->pspec->value_type);
g_value_copy (property->value, value);
}
static void
glade_property_get_default_impl (GladeProperty *property, GValue *value)
{
g_value_init (value, property->klass->pspec->value_type);
g_value_copy (property->klass->def, value);
}
static void
glade_property_sync_impl (GladeProperty *property)
{
/* Heh, here are the many reasons not to
* sync a property ;-)
*/
if (/* the class can be NULL during object,
* construction this is just a temporary state */
property->klass == NULL ||
/* optional properties that are disabled */
property->enabled == FALSE ||
/* explicit "never sync" flag */
property->klass->ignore ||
/* avoid recursion */
property->syncing ||
/* No widget owns this property yet */
property->widget == NULL)
return;
property->syncing = TRUE;
/* In the case of construct_only, the widget instance must be rebuilt
* to apply the property
*/
if (property->klass->construct_only)
glade_widget_rebuild (property->widget);
else if (property->klass->packing)
glade_widget_child_set_property (glade_widget_get_parent (property->widget),
property->widget,
property->klass->id,
property->value);
else
glade_widget_object_set_property (property->widget,
property->klass->id,
property->value);
property->syncing = FALSE;
}
static void
glade_property_load_impl (GladeProperty *property)
{
GObject *object;
GObjectClass *oclass;
if (property->widget == NULL ||
property->klass->packing ||
property->klass->type != GPC_NORMAL ||
!(property->klass->pspec->flags & G_PARAM_READABLE))
return;
object = glade_widget_get_object (property->widget);
oclass = G_OBJECT_GET_CLASS (object);
if (g_object_class_find_property (oclass, property->klass->id))
g_object_get_property (object, property->klass->id, property->value);
}
static gboolean
glade_property_write_impl (GladeProperty *property,
GladeInterface *interface,
GArray *props)
{
GladePropInfo info = { 0, };
GladeAtkActionInfo ainfo = { 0, };
GList *list;
gchar *name, *value, **split, *tmp;
gint i;
if (!property->klass->save || !property->enabled)
return FALSE;
g_assert (property->klass->orig_def);
g_assert (property->klass->def);
/* Skip properties that are default by original pspec default
* (excepting those that specified otherwise).
*/
if (!(property->klass->save_always || property->save_always) &&
glade_property_equals_value (property, property->klass->orig_def))
return FALSE;
/* we should change each '-' by '_' on the name of the property
* (<property name="...">) */
if (property->klass->type != GPC_NORMAL)
{
tmp = (gchar *)glade_property_class_atk_realname (property->klass->id);
name = g_strdup (tmp);
}
else
{
name = g_strdup (property->klass->id);
}
/* convert the value of this property to a string */
if (property->klass->type == GPC_ACCEL_PROPERTY ||
(value = glade_property_class_make_string_from_gvalue
(property->klass, property->value)) == NULL)
/* make sure we keep the empty string, also... upcomming
* funcs that may not like NULL.
*/
value = g_strdup ("");
else
{
/* Escape the string so that it will be parsed as it should. */
tmp = value;
value = g_markup_escape_text (value, -1);
g_free (tmp);
}
switch (property->klass->type)
{
case GPC_ATK_PROPERTY:
tmp = g_strdup_printf ("AtkObject::%s", name);
g_free (name);
name = tmp;
/* Dont break here ... */
case GPC_NORMAL:
info.name = glade_xml_alloc_propname(interface, name);
info.value = glade_xml_alloc_string(interface, value);
if (property->klass->translatable)
{
info.translatable = property->i18n_translatable;
info.has_context = property->i18n_has_context;
if (property->i18n_comment)
info.comment = glade_xml_alloc_string
(interface, property->i18n_comment);
}
g_array_append_val (props, info);
break;
case GPC_ATK_RELATION:
if ((split = g_strsplit (value, GPC_OBJECT_DELIMITER, 0)) != NULL)
{
for (i = 0; split[i] != NULL; i++)
{
GladeAtkRelationInfo rinfo = { 0, };
rinfo.type = glade_xml_alloc_string(interface, name);
rinfo.target = glade_xml_alloc_string(interface, split[i]);
g_array_append_val (props, rinfo);
}
g_strfreev (split);
}
break;
case GPC_ATK_ACTION:
ainfo.action_name = glade_xml_alloc_string(interface, name);
ainfo.description = glade_xml_alloc_string(interface, value);
g_array_append_val (props, ainfo);
break;
case GPC_ACCEL_PROPERTY:
for (list = g_value_get_boxed (property->value);
list; list = list->next)
{
GladeAccelInfo *accel = list->data;
GladeAccelInfo accel_info = { 0, };
accel_info.signal = glade_xml_alloc_string(interface, accel->signal);
accel_info.key = accel->key;
accel_info.modifiers = accel->modifiers;
g_array_append_val (props, accel_info);
}
break;
default:
break;
}
g_free (name);
g_free (value);
return TRUE;
}
static G_CONST_RETURN gchar *
glade_property_get_tooltip_impl (GladeProperty *property)
{
gchar *tooltip = NULL;
if (property->sensitive == FALSE)
tooltip = property->insensitive_tooltip;
else
tooltip = property->klass->tooltip;
return tooltip;
}
/*******************************************************************************
GObjectClass & Object Construction
*******************************************************************************/
static void
glade_property_set_real_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GladeProperty *property = GLADE_PROPERTY (object);
switch (prop_id)
{
case PROP_ENABLED:
glade_property_set_enabled (property, g_value_get_boolean (value));
break;
case PROP_SENSITIVE:
property->sensitive = g_value_get_boolean (value);
break;
case PROP_I18N_TRANSLATABLE:
glade_property_i18n_set_translatable (property, g_value_get_boolean (value));
break;
case PROP_I18N_HAS_CONTEXT:
glade_property_i18n_set_has_context (property, g_value_get_boolean (value));
break;
case PROP_I18N_COMMENT:
glade_property_i18n_set_comment (property, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
glade_property_get_real_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GladeProperty *property = GLADE_PROPERTY (object);
switch (prop_id)
{
case PROP_ENABLED:
g_value_set_boolean (value, glade_property_get_enabled (property));
break;
case PROP_SENSITIVE:
g_value_set_boolean (value, glade_property_get_sensitive (property));
break;
case PROP_I18N_TRANSLATABLE:
g_value_set_boolean (value, glade_property_i18n_get_translatable (property));
break;
case PROP_I18N_HAS_CONTEXT:
g_value_set_boolean (value, glade_property_i18n_get_has_context (property));
break;
case PROP_I18N_COMMENT:
g_value_set_string (value, glade_property_i18n_get_comment (property));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
glade_property_finalize (GObject *object)
{
GladeProperty *property = GLADE_PROPERTY (object);
if (property->value)
{
g_value_unset (property->value);
g_free (property->value);
}
if (property->i18n_comment)
g_free (property->i18n_comment);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
glade_property_init (GladeProperty *property)
{
property->enabled = TRUE;
property->sensitive = TRUE;
property->i18n_translatable = TRUE;
property->i18n_has_context = FALSE;
property->i18n_comment = NULL;
}
static void
glade_property_klass_init (GladePropertyKlass *prop_class)
{
GObjectClass *object_class;
g_return_if_fail (prop_class != NULL);
parent_class = g_type_class_peek_parent (prop_class);
object_class = G_OBJECT_CLASS (prop_class);
/* GObjectClass */
object_class->set_property = glade_property_set_real_property;
object_class->get_property = glade_property_get_real_property;
object_class->finalize = glade_property_finalize;
/* Class methods */
prop_class->dup = glade_property_dup_impl;
prop_class->equals_value = glade_property_equals_value_impl;
prop_class->set_value = glade_property_set_value_impl;
prop_class->get_value = glade_property_get_value_impl;
prop_class->get_default = glade_property_get_default_impl;
prop_class->sync = glade_property_sync_impl;
prop_class->load = glade_property_load_impl;
prop_class->write = glade_property_write_impl;
prop_class->get_tooltip = glade_property_get_tooltip_impl;
prop_class->value_changed = NULL;
prop_class->tooltip_changed = NULL;
/* Properties */
g_object_class_install_property
(object_class, PROP_ENABLED,
g_param_spec_boolean
("enabled", _("Enabled"),
_("If the property is optional, this is its enabled state"),
TRUE, G_PARAM_READWRITE));
g_object_class_install_property
(object_class, PROP_SENSITIVE,
g_param_spec_boolean
("sensitive", _("Sensitive"),
_("This gives backends control to set property sensitivity"),
TRUE, G_PARAM_READWRITE));
g_object_class_install_property
(object_class, PROP_I18N_COMMENT,
g_param_spec_string
("i18n-comment", _("Comment"),
_("Comment for translators"),
NULL, G_PARAM_READWRITE));
g_object_class_install_property
(object_class, PROP_I18N_TRANSLATABLE,
g_param_spec_boolean
("i18n-translatable", _("Translatable"),
_("Whether this property is translatable or not"),
TRUE, G_PARAM_READWRITE));
g_object_class_install_property
(object_class, PROP_I18N_HAS_CONTEXT,
g_param_spec_boolean
("i18n-has-context", _("Has Context"),
_("Whether or not the translatable string has a context prefix"),
FALSE, G_PARAM_READWRITE));
/* Signal */
glade_property_signals[VALUE_CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GladePropertyKlass,
value_changed),
NULL, NULL,
glade_marshal_VOID__POINTER_POINTER,
G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
glade_property_signals[TOOLTIP_CHANGED] =
g_signal_new ("tooltip-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GladePropertyKlass,
tooltip_changed),
NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
}
GType
glade_property_get_type (void)
{
static GType property_type = 0;
if (!property_type)
{
static const GTypeInfo property_info =
{
sizeof (GladePropertyKlass), /* Klass is our class */
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) glade_property_klass_init,
(GClassFinalizeFunc) NULL,
NULL, /* class_data */
sizeof (GladeProperty),
0, /* n_preallocs */
(GInstanceInitFunc) glade_property_init,
};
property_type =
g_type_register_static (G_TYPE_OBJECT,
"GladeProperty",
&property_info, 0);
}
return property_type;
}
/*******************************************************************************
GladeInterface Parsing code
*******************************************************************************/
static GValue *
glade_property_read_packing (GladeProperty *property,
GladePropertyClass *pclass,
GladeProject *project,
GladeChildInfo *info,
gboolean free_value)
{
GValue *gvalue = NULL;
gint i;
gchar *id;
for (i = 0; i < info->n_properties; ++i)
{
GladePropInfo *pinfo = info->properties + i;
id = glade_util_read_prop_name (pinfo->name);
if (!strcmp (id, pclass->id))
{
gvalue = glade_property_class_make_gvalue_from_string
(pclass, pinfo->value, project);
if (property)
{
glade_property_i18n_set_translatable
(property, pinfo->translatable);
glade_property_i18n_set_has_context
(property, pinfo->has_context);
glade_property_i18n_set_comment
(property, pinfo->comment);
property->enabled = TRUE;
GLADE_PROPERTY_GET_KLASS (property)->set_value
(property, gvalue);
}
if (free_value)
{
g_value_unset (gvalue);
g_free (gvalue);
}
g_free (id);
break;
}
g_free (id);
}
return gvalue;
}
static GValue *
glade_property_read_normal (GladeProperty *property,
GladePropertyClass *pclass,
GladeProject *project,
GladeWidgetInfo *info,
gboolean free_value)
{
GValue *gvalue = NULL;
gint i;
gchar *id;
for (i = 0; i < info->n_properties; ++i)
{
GladePropInfo *pinfo = info->properties + i;
id = glade_util_read_prop_name (pinfo->name);
if (!strcmp (id, pclass->id))
{
if (property && glade_property_class_is_object (pclass))
{
/* we must synchronize this directly after loading this project
* (i.e. lookup the actual objects after they've been parsed and
* are present).
*/
g_object_set_data_full (G_OBJECT (property),
"glade-loaded-object",
g_strdup (pinfo->value), g_free);
}
else
{
gvalue = glade_property_class_make_gvalue_from_string
(pclass, pinfo->value, project);
if (property)
GLADE_PROPERTY_GET_KLASS
(property)->set_value (property, gvalue);
if (free_value)
{
g_value_unset (gvalue);
g_free (gvalue);
}
}
if (property)
{
glade_property_i18n_set_translatable
(property, pinfo->translatable);
glade_property_i18n_set_has_context
(property, pinfo->has_context);
glade_property_i18n_set_comment
(property, pinfo->comment);
property->enabled = TRUE;
}
g_free (id);
break;
}
g_free (id);
}
return gvalue;
}
static GValue *
glade_property_read_atk_prop (GladeProperty *property,
GladePropertyClass *pclass,
GladeProject *project,
GladeWidgetInfo *info,
gboolean free_value)
{
GValue *gvalue = NULL;
gint i;
gchar *id;
for (i = 0; i < info->n_atk_props; ++i)
{
GladePropInfo *pinfo = info->atk_props + i;
id = glade_util_read_prop_name (pinfo->name);
if (!strcmp (id, pclass->id))
{
gvalue = glade_property_class_make_gvalue_from_string
(pclass, pinfo->value, project);
if (property)
{
glade_property_i18n_set_translatable
(property, pinfo->translatable);
glade_property_i18n_set_has_context
(property, pinfo->has_context);
glade_property_i18n_set_comment
(property, pinfo->comment);
property->enabled = TRUE;
GLADE_PROPERTY_GET_KLASS (property)->set_value
(property, gvalue);
}
if (free_value)
{
g_value_unset (gvalue);
g_free (gvalue);
}
g_free (id);
break;
}
g_free (id);
}
return gvalue;
}
static GValue *
glade_property_read_atk_relation (GladeProperty *property,
GladePropertyClass *pclass,
GladeProject *project,
GladeWidgetInfo *info)
{
const gchar *class_id;
gchar *id, *string = NULL, *tmp;
gint i;
for (i = 0; i < info->n_relations; ++i)
{
GladeAtkRelationInfo *rinfo = info->relations + i;
id = glade_util_read_prop_name (rinfo->type);
class_id = glade_property_class_atk_realname (pclass->id);
if (!strcmp (id, class_id))
{
if (string == NULL)
string = g_strdup (rinfo->target);
else
{
tmp = g_strdup_printf ("%s%s%s", string,
GPC_OBJECT_DELIMITER, rinfo->target);
string = (g_free (string), tmp);
}
}
g_free (id);
}
/* we must synchronize this directly after loading this project
* (i.e. lookup the actual objects after they've been parsed and
* are present).
*/
if (property)
{
g_object_set_data_full (G_OBJECT (property), "glade-loaded-object",
g_strdup (string), g_free);
}
return NULL;
}
static GValue *
glade_property_read_atk_action (GladeProperty *property,
GladePropertyClass *pclass,
GladeProject *project,
GladeWidgetInfo *info,
gboolean free_value)
{
GValue *gvalue = NULL;
const gchar *class_id;
gchar *id;
gint i;
for (i = 0; i < info->n_atk_actions; ++i)
{
GladeAtkActionInfo *ainfo = info->atk_actions + i;
id = glade_util_read_prop_name (ainfo->action_name);
class_id = glade_property_class_atk_realname (pclass->id);
if (!strcmp (id, class_id))
{
/* Need special case for NULL values here ??? */
gvalue = glade_property_class_make_gvalue_from_string
(pclass, ainfo->description, project);
if (property)
GLADE_PROPERTY_GET_KLASS
(property)->set_value (property, gvalue);
if (free_value)
{
g_value_unset (gvalue);
g_free (gvalue);
}
g_free (id);
break;
}
g_free (id);
}
return gvalue;
}
static GValue *
glade_property_read_accel_prop (GladeProperty *property,
GladePropertyClass *pclass,
GladeProject *project,
GladeWidgetInfo *info,
gboolean free_value)
{
GValue *gvalue = NULL;
GList *accels = NULL;
gint i;
for (i = 0; i < info->n_accels; ++i)
{
GladeAccelInfo *ainfo = info->accels + i;
GladeAccelInfo *ainfo_dup = g_new0 (GladeAccelInfo, 1);
ainfo_dup = g_new0 (GladeAccelInfo, 1);
ainfo_dup->signal = g_strdup (ainfo->signal);
ainfo_dup->key = ainfo->key;
ainfo_dup->modifiers = ainfo->modifiers;
accels = g_list_prepend (accels, ainfo_dup);
}
gvalue = g_new0 (GValue, 1);
g_value_init (gvalue, GLADE_TYPE_ACCEL_GLIST);
g_value_take_boxed (gvalue, accels);
if (property)
GLADE_PROPERTY_GET_KLASS
(property)->set_value (property, gvalue);
if (free_value)
{
g_value_unset (gvalue);
g_free (gvalue);
}
return gvalue;
}
/*******************************************************************************
API
*******************************************************************************/
/**
* glade_property_new:
* @klass: A #GladePropertyClass defining this property
* @widget: The #GladeWidget this property is created for
* @value: The initial #GValue of the property or %NULL
* (the #GladeProperty will assume ownership of @value)
*
* Creates a #GladeProperty of type @klass for @widget with @value; if
* @value is %NULL, then the introspected default value for that property
* will be used.
*
* Returns: The newly created #GladeProperty
*/
GladeProperty *
glade_property_new (GladePropertyClass *klass,
GladeWidget *widget,
GValue *value)
{
GladeProperty *property;
g_return_val_if_fail (GLADE_IS_PROPERTY_CLASS (klass), NULL);
property =
(GladeProperty *)g_object_new (GLADE_TYPE_PROPERTY, NULL);
property->klass = klass;
property->widget = widget;
property->value = value;
if (klass->optional)
property->enabled = klass->optional_default;
if (property->value == NULL)
{
g_assert (klass->orig_def);
property->value = g_new0 (GValue, 1);
g_value_init (property->value, klass->orig_def->g_type);
g_value_copy (klass->orig_def, property->value);
}
return property;
}
/**
* glade_property_dup:
* @template_prop: A #GladeProperty
* @widget: A #GladeWidget
*
* Returns: A newly duplicated property based on the new widget
*/
GladeProperty *
glade_property_dup (GladeProperty *template_prop, GladeWidget *widget)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (template_prop), NULL);
return GLADE_PROPERTY_GET_KLASS (template_prop)->dup (template_prop, widget);
}
static void
glade_property_reset_common (GladeProperty *property, gboolean original)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
GLADE_PROPERTY_GET_KLASS (property)->set_value
(property, (original) ? property->klass->orig_def : property->klass->def);
}
/**
* glade_property_reset:
* @property: A #GladeProperty
*
* Resets this property to its default value
*/
void
glade_property_reset (GladeProperty *property)
{
glade_property_reset_common (property, FALSE);
}
/**
* glade_property_original_reset:
* @property: A #GladeProperty
*
* Resets this property to its original default value
*/
void
glade_property_original_reset (GladeProperty *property)
{
glade_property_reset_common (property, TRUE);
}
static gboolean
glade_property_default_common (GladeProperty *property, gboolean orig)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
return GLADE_PROPERTY_GET_KLASS (property)->equals_value
(property, (orig) ? property->klass->orig_def : property->klass->def);
}
/**
* glade_property_default:
* @property: A #GladeProperty
*
* Returns: Whether this property is at its default value
*/
gboolean
glade_property_default (GladeProperty *property)
{
return glade_property_default_common (property, FALSE);
}
/**
* glade_property_original_default:
* @property: A #GladeProperty
*
* Returns: Whether this property is at its original default value
*/
gboolean
glade_property_original_default (GladeProperty *property)
{
return glade_property_default_common (property, TRUE);
}
/**
* glade_property_equals_value:
* @property: a #GladeProperty
* @value: a #GValue
*
* Returns: Whether this property is equal to the value provided
*/
gboolean
glade_property_equals_value (GladeProperty *property,
const GValue *value)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
return GLADE_PROPERTY_GET_KLASS (property)->equals_value (property, value);
}
/**
* glade_property_equals_va_list:
* @property: a #GladeProperty
* @vl: a va_list
*
* Returns: Whether this property is equal to the value provided
*/
static gboolean
glade_property_equals_va_list (GladeProperty *property, va_list vl)
{
GValue *value;
gboolean ret;
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
value = glade_property_class_make_gvalue_from_vl (property->klass, vl);
ret = GLADE_PROPERTY_GET_KLASS (property)->equals_value (property, value);
g_value_unset (value);
g_free (value);
return ret;
}
/**
* glade_property_equals:
* @property: a #GladeProperty
* @...: a provided property value
*
* Returns: Whether this property is equal to the value provided
*/
gboolean
glade_property_equals (GladeProperty *property, ...)
{
va_list vl;
gboolean ret;
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
va_start (vl, property);
ret = glade_property_equals_va_list (property, vl);
va_end (vl);
return ret;
}
/**
* glade_property_set_value:
* @property: a #GladeProperty
* @value: a #GValue
*
* Sets the property's value
*/
void
glade_property_set_value (GladeProperty *property, const GValue *value)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
g_return_if_fail (value != NULL);
GLADE_PROPERTY_GET_KLASS (property)->set_value (property, value);
}
/**
* glade_property_set_va_list:
* @property: a #GladeProperty
* @vl: a va_list with value to set
*
* Sets the property's value
*/
void
glade_property_set_va_list (GladeProperty *property, va_list vl)
{
GValue *value;
g_return_if_fail (GLADE_IS_PROPERTY (property));
value = glade_property_class_make_gvalue_from_vl (property->klass, vl);
GLADE_PROPERTY_GET_KLASS (property)->set_value (property, value);
g_value_unset (value);
g_free (value);
}
/**
* glade_property_set:
* @property: a #GladeProperty
* @...: the value to set
*
* Sets the property's value (in a convenient way)
*/
void
glade_property_set (GladeProperty *property, ...)
{
va_list vl;
g_return_if_fail (GLADE_IS_PROPERTY (property));
va_start (vl, property);
glade_property_set_va_list (property, vl);
va_end (vl);
}
/**
* glade_property_get_value:
* @property: a #GladeProperty
* @value: a #GValue
*
* Retrieve the property value
*/
void
glade_property_get_value (GladeProperty *property, GValue *value)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
g_return_if_fail (value != NULL);
GLADE_PROPERTY_GET_KLASS (property)->get_value (property, value);
}
/**
* glade_property_get_default:
* @property: a #GladeProperty
* @value: a #GValue
*
* Retrieve the default property value
*/
void
glade_property_get_default (GladeProperty *property, GValue *value)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
g_return_if_fail (value != NULL);
GLADE_PROPERTY_GET_KLASS (property)->get_default (property, value);
}
/**
* glade_property_get_va_list:
* @property: a #GladeProperty
* @vl: a va_list
*
* Retrieve the property value
*/
void
glade_property_get_va_list (GladeProperty *property, va_list vl)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
glade_property_class_set_vl_from_gvalue (property->klass, property->value, vl);
}
/**
* glade_property_get:
* @property: a #GladeProperty
* @...: An address to store the value
*
* Retrieve the property value
*/
void
glade_property_get (GladeProperty *property, ...)
{
va_list vl;
g_return_if_fail (GLADE_IS_PROPERTY (property));
va_start (vl, property);
glade_property_get_va_list (property, vl);
va_end (vl);
}
/**
* glade_property_sync:
* @property: a #GladeProperty
*
* Synchronize the object with this property
*/
void
glade_property_sync (GladeProperty *property)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
GLADE_PROPERTY_GET_KLASS (property)->sync (property);
}
/**
* glade_property_load:
* @property: a #GladeProperty
*
* Loads the value of @property from the coresponding object instance
*/
void
glade_property_load (GladeProperty *property)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
GLADE_PROPERTY_GET_KLASS (property)->load (property);
}
/**
* glade_property_read:
* @property: a #GladeProperty or #NULL
* @pclass: the #GladePropertyClass
* @project: the #GladeProject
* @info: a #GladeWidgetInfo struct or a #GladeChildInfo struct if
* a packing property is passed.
* @free_value: Whether the return value should be freed after applying
* it to the property or if it should be returned in tact.
*
* Read the value and any attributes for @property from @info, assumes
* @property is being loaded for @project
*
* Returns: The newly created #GValue if successfull (and if @free_value == FALSE)
*
* Note that object values will only be resolved after the project is
* completely loaded
*/
GValue *
glade_property_read (GladeProperty *property,
GladePropertyClass *pclass,
GladeProject *project,
gpointer info,
gboolean free_value)
{
GValue *ret = NULL;
g_return_val_if_fail (pclass != NULL, FALSE);
g_return_val_if_fail (info != NULL, FALSE);
if (pclass->packing)
{
ret = glade_property_read_packing
(property, pclass, project, (GladeChildInfo *)info, free_value);
}
else switch (pclass->type)
{
case GPC_NORMAL:
ret = glade_property_read_normal
(property, pclass, project, (GladeWidgetInfo *)info, free_value);
break;
case GPC_ATK_PROPERTY:
ret = glade_property_read_atk_prop
(property, pclass, project, (GladeWidgetInfo *)info, free_value);
break;
case GPC_ATK_RELATION:
ret = glade_property_read_atk_relation
(property, pclass, project, (GladeWidgetInfo *)info);
break;
case GPC_ATK_ACTION:
ret = glade_property_read_atk_action
(property, pclass, project, (GladeWidgetInfo *)info, free_value);
break;
case GPC_ACCEL_PROPERTY:
ret = glade_property_read_accel_prop
(property, pclass, project, (GladeWidgetInfo *)info, free_value);
break;
default:
break;
}
return ret;
}
/**
* glade_property_write:
* @property: a #GladeProperty
* @interface: a #GladeInterface
* @props: a GArray of #GladePropInfo
*
* Write this property to the GladeInterface metadata
*/
gboolean
glade_property_write (GladeProperty *property, GladeInterface *interface, GArray *props)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
g_return_val_if_fail (interface != NULL, FALSE);
g_return_val_if_fail (props != NULL, FALSE);
return GLADE_PROPERTY_GET_KLASS (property)->write (property, interface, props);
}
/**
* glade_property_add_object:
* @property: a #GladeProperty
* @object: The #GObject to add
*
* Adds @object to the object list in @property.
*
* Note: This function expects @property to be a #GladeParamSpecObjects
* or #GParamSpecObject type property.
*/
void
glade_property_add_object (GladeProperty *property,
GObject *object)
{
GList *list = NULL, *new_list = NULL;
g_return_if_fail (GLADE_IS_PROPERTY (property));
g_return_if_fail (G_IS_OBJECT (object));
g_return_if_fail (GLADE_IS_PARAM_SPEC_OBJECTS (property->klass->pspec) ||
G_IS_PARAM_SPEC_OBJECT (property->klass->pspec));
if (GLADE_IS_PARAM_SPEC_OBJECTS (property->klass->pspec))
{
glade_property_get (property, &list);
new_list = g_list_copy (list);
new_list = g_list_append (new_list, object);
glade_property_set (property, new_list);
/* ownership of the list is not passed
* through glade_property_set()
*/
g_list_free (new_list);
}
else
{
glade_property_set (property, object);
}
}
/**
* glade_property_remove_object:
* @property: a #GladeProperty
* @object: The #GObject to add
*
* Removes @object from the object list in @property.
*
* Note: This function expects @property to be a #GladeParamSpecObjects
* or #GParamSpecObject type property.
*/
void
glade_property_remove_object (GladeProperty *property,
GObject *object)
{
GList *list = NULL, *new_list = NULL;
g_return_if_fail (GLADE_IS_PROPERTY (property));
g_return_if_fail (G_IS_OBJECT (object));
g_return_if_fail (GLADE_IS_PARAM_SPEC_OBJECTS (property->klass->pspec) ||
G_IS_PARAM_SPEC_OBJECT (property->klass->pspec));
if (GLADE_IS_PARAM_SPEC_OBJECTS (property->klass->pspec))
{
/* If object isnt in list; list should stay in tact.
* not bothering to check for now.
*/
glade_property_get (property, &list);
new_list = g_list_copy (list);
new_list = g_list_remove (new_list, object);
glade_property_set (property, new_list);
/* ownership of the list is not passed
* through glade_property_set()
*/
g_list_free (new_list);
}
else
{
glade_property_set (property, object);
}
glade_property_class_get_from_gvalue (property->klass,
property->value,
&list);
glade_property_set (property, list);
}
/**
* glade_property_get_tooltip:
* @property: a #GladeProperty
*
* Returns: The appropriate tooltip for the editor
*/
G_CONST_RETURN gchar *
glade_property_get_tooltip (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
return GLADE_PROPERTY_GET_KLASS (property)->get_tooltip (property);
}
/* Parameters for translatable properties. */
void
glade_property_i18n_set_comment (GladeProperty *property,
const gchar *str)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
if (property->i18n_comment)
g_free (property->i18n_comment);
property->i18n_comment = g_strdup (str);
g_object_notify (G_OBJECT (property), "i18n-comment");
}
const gchar *
glade_property_i18n_get_comment (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
return property->i18n_comment;
}
void
glade_property_i18n_set_translatable (GladeProperty *property,
gboolean translatable)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
property->i18n_translatable = translatable;
g_object_notify (G_OBJECT (property), "i18n-translatable");
}
gboolean
glade_property_i18n_get_translatable (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
return property->i18n_translatable;
}
void
glade_property_i18n_set_has_context (GladeProperty *property,
gboolean has_context)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
property->i18n_has_context = has_context;
g_object_notify (G_OBJECT (property), "i18n-has-context");
}
gboolean
glade_property_i18n_get_has_context (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
return property->i18n_has_context;
}
void
glade_property_set_sensitive (GladeProperty *property,
gboolean sensitive,
const gchar *reason)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
/* reason is only why we're disableing it */
if (sensitive == FALSE)
{
if (property->insensitive_tooltip)
g_free (property->insensitive_tooltip);
property->insensitive_tooltip =
g_strdup (reason);
}
if (property->sensitive != sensitive)
{
gchar *tooltip;
property->sensitive = sensitive;
tooltip = (gchar *)GLADE_PROPERTY_GET_KLASS
(property)->get_tooltip (property);
g_signal_emit (G_OBJECT (property),
glade_property_signals[TOOLTIP_CHANGED],
0, tooltip);
}
g_object_notify (G_OBJECT (property), "sensitive");
}
gboolean
glade_property_get_sensitive (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
return property->sensitive;
}
/**
* glade_property_set_save_always:
* @property: A #GladeProperty
* @setting: the value to set
*
* Sets whether this property should be special cased
* to always be saved regardless of its default value.
* (used for some special cases like properties
* that are assigned initial values in composite widgets
* or derived widget code).
*/
void
glade_property_set_save_always (GladeProperty *property,
gboolean setting)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
property->save_always = setting;
}
/**
* glade_property_get_save_always:
* @property: A #GladeProperty
*
* Returns whether this property is special cased
* to always be saved regardless of its default value.
*/
gboolean
glade_property_get_save_always (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
return property->save_always;
}
void
glade_property_set_enabled (GladeProperty *property,
gboolean enabled)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
property->enabled = enabled;
if (enabled)
glade_property_sync (property);
g_object_notify (G_OBJECT (property), "enabled");
}
gboolean
glade_property_get_enabled (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
return property->enabled;
}
static gint glade_property_su_stack = 0;
void
glade_property_push_superuser (void)
{
glade_property_su_stack++;
}
void
glade_property_pop_superuser (void)
{
if (--glade_property_su_stack < 0)
{
g_critical ("Bug: property super user stack is corrupt.\n");
}
}
gboolean
glade_property_superuser (void)
{
return glade_property_su_stack > 0;
}
syntax highlighted by Code2HTML, v. 0.9.1