/*
* 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-project.h"
#include "glade-widget-adaptor.h"
#include "glade-debug.h"
#include "glade-app.h"
#include "glade-editor.h"
#include "glade-marshallers.h"
struct _GladePropertyPrivate {
GladePropertyClass *klass; /* A pointer to the GladeProperty that this
* setting specifies
*/
GladeWidget *widget; /* A pointer to the GladeWidget that this
* GladeProperty is modifying
*/
GladePropertyState state; /* Current property state, used by editing widgets.
*/
GValue *value; /* The value of the property
*/
gchar *insensitive_tooltip; /* Tooltip to display when in insensitive state
* (used to explain why the property is
* insensitive)
*/
gchar *support_warning; /* Tooltip to display when the property
* has format problems
* (used to explain why the property is
* insensitive)
*/
guint support_disabled : 1; /* Whether this property is disabled due
* to format conflicts
*/
guint sensitive : 1; /* Whether this property is sensitive (if the
* property is "optional" this takes precedence).
*/
guint enabled : 1; /* Enabled is a flag that is used for GladeProperties
* that have the optional flag set to let us know
* if this widget has this setting enabled or
* not. (Like default size, it can be specified or
* unspecified). This flag also sets the state
* of the property->input state for the loaded
* widget.
*/
guint save_always : 1; /* Used to make a special case exception and always
* save this property regardless of what the default
* value is (used for some special cases like properties
* that are assigned initial values in composite widgets
* or derived widget code).
*/
/* Used only for translatable strings. */
guint i18n_translatable : 1;
gchar *i18n_context;
gchar *i18n_comment;
gint syncing; /* Avoid recursion while synchronizing object with value */
gint sync_tolerance;
};
enum
{
VALUE_CHANGED,
TOOLTIP_CHANGED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_CLASS,
PROP_ENABLED,
PROP_SENSITIVE,
PROP_I18N_TRANSLATABLE,
PROP_I18N_CONTEXT,
PROP_I18N_COMMENT,
PROP_STATE,
N_PROPERTIES
};
static GParamSpec *properties[N_PROPERTIES];
static guint glade_property_signals[LAST_SIGNAL] = { 0 };
static GObjectClass *glade_property_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,
"class", template_prop->priv->klass,
"i18n-translatable", template_prop->priv->i18n_translatable,
"i18n-context", template_prop->priv->i18n_context,
"i18n-comment", template_prop->priv->i18n_comment,
NULL);
property->priv->widget = widget;
property->priv->value = g_new0 (GValue, 1);
g_value_init (property->priv->value, template_prop->priv->value->g_type);
/* Cannot duplicate parentless_widget property */
if (glade_property_class_parentless_widget (template_prop->priv->klass))
{
if (!G_IS_PARAM_SPEC_OBJECT (glade_property_class_get_pspec (template_prop->priv->klass)))
g_warning ("Parentless widget property should be of object type");
g_value_set_object (property->priv->value, NULL);
}
else
g_value_copy (template_prop->priv->value, property->priv->value);
property->priv->enabled = template_prop->priv->enabled;
property->priv->state = template_prop->priv->state;
glade_property_set_sensitive (property, template_prop->priv->sensitive,
template_prop->priv->insensitive_tooltip);
return property;
}
static gboolean
glade_property_equals_value_impl (GladeProperty *property,
const GValue *value)
{
return !glade_property_class_compare (property->priv->klass, property->priv->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 (glade_property_class_get_pspec (property->priv->klass)))
{
/* 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);
if (gold != NULL)
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);
if (gnew != NULL)
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);
g_return_if_fail (gold != NULL);
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);
g_return_if_fail (gnew != NULL);
glade_widget_add_prop_ref (gnew, property);
}
}
}
static gboolean
glade_property_verify (GladeProperty *property, const GValue *value)
{
gboolean ret = FALSE;
GladeWidget *parent;
parent = glade_widget_get_parent (property->priv->widget);
if (glade_property_class_get_is_packing (property->priv->klass) && parent)
ret =
glade_widget_adaptor_child_verify_property (glade_widget_get_adaptor (parent),
glade_widget_get_object (parent),
glade_widget_get_object (property->priv->widget),
glade_property_class_id (property->priv->klass),
value);
else if (!glade_property_class_get_is_packing (property->priv->klass))
ret = glade_widget_adaptor_verify_property (glade_widget_get_adaptor (property->priv->widget),
glade_widget_get_object (property->priv->widget),
glade_property_class_id (property->priv->klass), value);
return ret;
}
static void
glade_property_fix_state (GladeProperty *property)
{
property->priv->state = GLADE_STATE_NORMAL;
/* Properties are 'changed' state if they are not default, or if
* they are optional and enabled, optional enabled properties
* are saved regardless of default value
*/
if (glade_property_class_optional (property->priv->klass))
{
if (glade_property_get_enabled (property))
property->priv->state |= GLADE_STATE_CHANGED;
}
else if (!glade_property_original_default (property))
property->priv->state |= GLADE_STATE_CHANGED;
if (property->priv->support_warning)
property->priv->state |= GLADE_STATE_UNSUPPORTED;
if (property->priv->support_disabled)
property->priv->state |= GLADE_STATE_SUPPORT_DISABLED;
g_object_notify_by_pspec (G_OBJECT (property), properties[PROP_STATE]);
}
static gboolean
glade_property_set_value_impl (GladeProperty *property, const GValue *value)
{
GladeProject *project = property->priv->widget ?
glade_widget_get_project (property->priv->widget) : NULL;
gboolean changed = FALSE;
GValue old_value = { 0, };
gboolean warn_before, warn_after;
#ifdef GLADE_ENABLE_DEBUG
if (glade_get_debug_flags () & GLADE_DEBUG_PROPERTIES)
{
g_print ("PROPERTY: Setting %s property %s on %s ",
glade_property_class_get_is_packing (property->priv->klass) ? "packing" : "normal",
glade_property_class_id (property->priv->klass),
property->priv->widget ? glade_widget_get_name (property->priv->widget) : "unknown");
gchar *str1 =
glade_widget_adaptor_string_from_value (glade_property_class_get_adaptor (property->priv->klass),
property->priv->klass, property->priv->value);
gchar *str2 =
glade_widget_adaptor_string_from_value (glade_property_class_get_adaptor (property->priv->klass),
property->priv->klass, value);
g_print ("from %s to %s\n", str1, str2);
g_free (str1);
g_free (str2);
}
#endif /* GLADE_ENABLE_DEBUG */
if (!g_value_type_compatible (G_VALUE_TYPE (property->priv->value), G_VALUE_TYPE (value)))
{
g_warning ("Trying to assign an incompatible value to property %s\n",
glade_property_class_id (property->priv->klass));
return FALSE;
}
/* Check if the backend doesnt give us permission to
* set this value.
*/
if (glade_property_superuser () == FALSE && property->priv->widget &&
project && glade_project_is_loading (project) == FALSE &&
glade_property_verify (property, value) == FALSE)
{
return FALSE;
}
/* save "changed" state.
*/
changed = !glade_property_equals_value (property, value);
/* Add/Remove references from widget ref stacks here
* (before assigning the value)
*/
if (property->priv->widget && changed &&
glade_property_class_is_object (property->priv->klass))
glade_property_update_prop_refs (property, property->priv->value, value);
/* Check pre-changed warning state */
warn_before = glade_property_warn_usage (property);
/* Make a copy of the old value */
g_value_init (&old_value, G_VALUE_TYPE (property->priv->value));
g_value_copy (property->priv->value, &old_value);
/* Assign property first so that; if the object need be
* rebuilt, it will reflect the new value
*/
g_value_reset (property->priv->value);
g_value_copy (value, property->priv->value);
GLADE_PROPERTY_GET_KLASS (property)->sync (property);
glade_property_fix_state (property);
if (changed && property->priv->widget)
{
g_signal_emit (G_OBJECT (property),
glade_property_signals[VALUE_CHANGED],
0, &old_value, property->priv->value);
glade_project_verify_property (property);
/* Check post change warning state */
warn_after = glade_property_warn_usage (property);
/* Update owning widget's warning state if need be */
if (property->priv->widget != NULL && warn_before != warn_after)
glade_widget_verify (property->priv->widget);
}
/* Special case parentless widget properties */
if (glade_property_class_parentless_widget (property->priv->klass))
{
GladeWidget *gobj;
GObject *obj;
if ((obj = g_value_get_object (&old_value)) &&
(gobj = glade_widget_get_from_gobject (obj)))
glade_widget_show (gobj);
if ((obj = g_value_get_object (value)) &&
(gobj = glade_widget_get_from_gobject (obj)))
glade_widget_hide (gobj);
}
g_value_unset (&old_value);
return TRUE;
}
static void
glade_property_get_value_impl (GladeProperty *property, GValue *value)
{
GParamSpec *pspec;
pspec = glade_property_class_get_pspec (property->priv->klass);
g_value_init (value, pspec->value_type);
g_value_copy (property->priv->value, value);
}
static void
glade_property_sync_impl (GladeProperty *property)
{
GladePropertyPrivate *priv = property->priv;
GladePropertyClass *klass = priv->klass;
const GValue *value;
const gchar *id;
/* 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 */
klass == NULL ||
/* explicit "never sync" flag */
glade_property_class_get_ignore (klass) ||
/* recursion guards */
priv->syncing >= priv->sync_tolerance ||
/* No widget owns this property yet */
priv->widget == NULL)
return;
id = glade_property_class_id (klass);
/* Only the properties from widget->properties should affect the runtime widget.
* (other properties may be used for convenience in the plugin).
*/
if ((glade_property_class_get_is_packing (klass) &&
!glade_widget_get_pack_property (priv->widget, id))
|| !glade_widget_get_property (priv->widget, id))
return;
priv->syncing++;
/* optional properties that are disabled get the default runtime value */
value = (priv->enabled) ? priv->value : glade_property_class_get_default (klass);
/* In the case of construct_only, the widget instance must be rebuilt
* to apply the property
*/
if (glade_property_class_get_construct_only (klass) && priv->syncing == 1)
{
/* Virtual properties can be construct only, in which
* case they are allowed to trigger a rebuild, and in
* the process are allowed to get "synced" after the
* instance is rebuilt.
*/
if (glade_property_class_get_virtual (klass))
priv->sync_tolerance++;
glade_widget_rebuild (priv->widget);
if (glade_property_class_get_virtual (klass))
priv->sync_tolerance--;
}
else if (glade_property_class_get_is_packing (klass))
glade_widget_child_set_property (glade_widget_get_parent (priv->widget),
priv->widget, id, value);
else
glade_widget_object_set_property (priv->widget, id, value);
priv->syncing--;
}
static void
glade_property_load_impl (GladeProperty *property)
{
GObject *object;
GObjectClass *oclass;
GParamSpec *pspec;
pspec = glade_property_class_get_pspec (property->priv->klass);
if (property->priv->widget == NULL ||
glade_property_class_get_virtual (property->priv->klass) ||
glade_property_class_get_is_packing (property->priv->klass) ||
glade_property_class_get_ignore (property->priv->klass) ||
!(pspec->flags & G_PARAM_READABLE) || G_IS_PARAM_SPEC_OBJECT (pspec))
return;
object = glade_widget_get_object (property->priv->widget);
oclass = G_OBJECT_GET_CLASS (object);
if (g_object_class_find_property (oclass, glade_property_class_id (property->priv->klass)))
glade_widget_object_get_property (property->priv->widget,
glade_property_class_id (property->priv->klass),
property->priv->value);
}
/*******************************************************************************
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_CLASS:
property->priv->klass = g_value_get_pointer (value);
break;
case PROP_ENABLED:
glade_property_set_enabled (property, g_value_get_boolean (value));
break;
case PROP_SENSITIVE:
property->priv->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_CONTEXT:
glade_property_i18n_set_context (property, g_value_get_string (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_CLASS:
g_value_set_pointer (value, property->priv->klass);
break;
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_CONTEXT:
g_value_set_string (value, glade_property_i18n_get_context (property));
break;
case PROP_I18N_COMMENT:
g_value_set_string (value, glade_property_i18n_get_comment (property));
break;
case PROP_STATE:
g_value_set_int (value, property->priv->state);
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->priv->value)
{
g_value_unset (property->priv->value);
g_free (property->priv->value);
}
if (property->priv->i18n_comment)
g_free (property->priv->i18n_comment);
if (property->priv->i18n_context)
g_free (property->priv->i18n_context);
if (property->priv->support_warning)
g_free (property->priv->support_warning);
if (property->priv->insensitive_tooltip)
g_free (property->priv->insensitive_tooltip);
G_OBJECT_CLASS (glade_property_parent_class)->finalize (object);
}
static void
glade_property_init (GladeProperty *property)
{
property->priv = G_TYPE_INSTANCE_GET_PRIVATE (property,
GLADE_TYPE_PROPERTY,
GladePropertyPrivate);
property->priv->enabled = TRUE;
property->priv->sensitive = TRUE;
property->priv->i18n_translatable = TRUE;
property->priv->i18n_comment = NULL;
property->priv->sync_tolerance = 1;
}
static void
glade_property_klass_init (GladePropertyKlass * prop_class)
{
GObjectClass *object_class;
g_return_if_fail (prop_class != NULL);
glade_property_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->sync = glade_property_sync_impl;
prop_class->load = glade_property_load_impl;
prop_class->value_changed = NULL;
prop_class->tooltip_changed = NULL;
/* Properties */
properties[PROP_CLASS] =
g_param_spec_pointer ("class",
_("Class"),
_("The GladePropertyClass for this property"),
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
properties[PROP_ENABLED] =
g_param_spec_boolean ("enabled",
_("Enabled"),
_("If the property is optional, this is its enabled state"),
TRUE, G_PARAM_READWRITE);
properties[PROP_SENSITIVE] =
g_param_spec_boolean ("sensitive",
_("Sensitive"),
_("This gives backends control to set property sensitivity"),
TRUE, G_PARAM_READWRITE);
properties[PROP_I18N_CONTEXT] =
g_param_spec_string ("i18n-context",
_("Context"),
_("Context for translation"),
NULL,
G_PARAM_READWRITE);
properties[PROP_I18N_COMMENT] =
g_param_spec_string ("i18n-comment",
_("Comment"),
_("Comment for translators"),
NULL,
G_PARAM_READWRITE);
properties[PROP_I18N_TRANSLATABLE] =
g_param_spec_boolean ("i18n-translatable",
_("Translatable"),
_("Whether this property is translatable"),
TRUE,
G_PARAM_READWRITE);
properties[PROP_STATE] =
g_param_spec_int ("state",
_("Visual State"),
_("Priority information for the property editor to act on"),
GLADE_STATE_NORMAL,
G_MAXINT,
GLADE_STATE_NORMAL,
G_PARAM_READABLE);
/* Install all properties */
g_object_class_install_properties (object_class, N_PROPERTIES, properties);
/* 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,
_glade_marshal_VOID__STRING_STRING_STRING,
G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING);
g_type_class_add_private (prop_class, sizeof (GladePropertyPrivate));
}
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;
}
/*******************************************************************************
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->priv->klass = klass;
property->priv->widget = widget;
property->priv->value = value;
if (glade_property_class_optional (klass))
property->priv->enabled = glade_property_class_optional_default (klass);
if (property->priv->value == NULL)
{
const GValue *orig_def =
glade_property_class_get_original_default (klass);
property->priv->value = g_new0 (GValue, 1);
g_value_init (property->priv->value, orig_def->g_type);
g_value_copy (orig_def, property->priv->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)
{
const GValue *value;
g_return_if_fail (GLADE_IS_PROPERTY (property));
if (original)
value = glade_property_class_get_original_default (property->priv->klass);
else
value = glade_property_class_get_default (property->priv->klass);
GLADE_PROPERTY_GET_KLASS (property)->set_value (property, value);
}
/**
* 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)
{
const GValue *value;
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
if (orig)
value = glade_property_class_get_original_default (property->priv->klass);
else
value = glade_property_class_get_default (property->priv->klass);
return GLADE_PROPERTY_GET_KLASS (property)->equals_value (property, value);
}
/**
* 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->priv->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
*
* Returns: Whether the property was successfully set.
*/
gboolean
glade_property_set_value (GladeProperty *property, const GValue *value)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
g_return_val_if_fail (value != NULL, FALSE);
return 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
*/
gboolean
glade_property_set_va_list (GladeProperty *property, va_list vl)
{
GValue *value;
gboolean success;
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
value = glade_property_class_make_gvalue_from_vl (property->priv->klass, vl);
success = GLADE_PROPERTY_GET_KLASS (property)->set_value (property, value);
g_value_unset (value);
g_free (value);
return success;
}
/**
* glade_property_set:
* @property: a #GladeProperty
* @...: the value to set
*
* Sets the property's value (in a convenient way)
*/
gboolean
glade_property_set (GladeProperty *property, ...)
{
va_list vl;
gboolean success;
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
va_start (vl, property);
success = glade_property_set_va_list (property, vl);
va_end (vl);
return success;
}
/**
* 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)
{
GParamSpec *pspec;
g_return_if_fail (GLADE_IS_PROPERTY (property));
g_return_if_fail (value != NULL);
pspec = glade_property_class_get_pspec (property->priv->klass);
g_value_init (value, pspec->value_type);
g_value_copy (glade_property_class_get_default (property->priv->klass), 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->priv->klass, property->priv->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
* @project: the #GladeProject
* @node: the #GladeXmlNode to read, will either be a 'widget'
* node or a 'child' node for packing properties.
*
* Read the value and any attributes for @property from @node, assumes
* @property is being loaded for @project
*
* Note that object values will only be resolved after the project is
* completely loaded
*/
void
glade_property_read (GladeProperty *property,
GladeProject *project,
GladeXmlNode *prop)
{
GValue *gvalue = NULL;
gchar /* *id, *name, */ * value;
gint translatable = FALSE;
gchar *comment = NULL, *context = NULL;
g_return_if_fail (GLADE_IS_PROPERTY (property));
g_return_if_fail (GLADE_IS_PROJECT (project));
g_return_if_fail (prop != NULL);
if (!glade_xml_node_verify (prop, GLADE_XML_TAG_PROPERTY))
return;
if (!(value = glade_xml_get_content (prop)))
return;
/* If an optional property is specified in the
* glade file, its enabled
*/
property->priv->enabled = TRUE;
if (glade_property_class_is_object (property->priv->klass))
{
/* 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 (value), g_free);
}
else
{
gvalue =
glade_property_class_make_gvalue_from_string (property->priv->klass, value, project);
GLADE_PROPERTY_GET_KLASS (property)->set_value (property, gvalue);
g_value_unset (gvalue);
g_free (gvalue);
}
translatable =
glade_xml_get_property_boolean (prop, GLADE_TAG_TRANSLATABLE, FALSE);
comment = glade_xml_get_property_string (prop, GLADE_TAG_COMMENT);
context = glade_xml_get_property_string (prop, GLADE_TAG_CONTEXT);
glade_property_i18n_set_translatable (property, translatable);
glade_property_i18n_set_comment (property, comment);
glade_property_i18n_set_context (property, context);
g_free (comment);
g_free (context);
g_free (value);
}
/**
* glade_property_write:
* @property: a #GladeProperty
* @context: A #GladeXmlContext
* @node: A #GladeXmlNode
*
* Write @property to @node
*/
void
glade_property_write (GladeProperty *property,
GladeXmlContext *context,
GladeXmlNode *node)
{
GladeXmlNode *prop_node;
gchar *name, *value;
gboolean save_always;
g_return_if_fail (GLADE_IS_PROPERTY (property));
g_return_if_fail (node != NULL);
/* This code should work the same for <packing>, <widget> and <template> */
if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_PACKING) ||
glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) ||
glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE)))
return;
/* There can be a couple of reasons to forcefully save a property */
save_always = (glade_property_class_save_always (property->priv->klass) || property->priv->save_always);
save_always = save_always || (glade_property_class_optional (property->priv->klass) && property->priv->enabled);
/* Skip properties that are default by original pspec default
* (excepting those that specified otherwise).
*/
if (!save_always && glade_property_original_default (property))
return;
/* Escape our string and save with underscores */
name = g_strdup (glade_property_class_id (property->priv->klass));
glade_util_replace (name, '-', '_');
/* convert the value of this property to a string */
if (!(value = glade_widget_adaptor_string_from_value
(glade_property_class_get_adaptor (property->priv->klass), property->priv->klass,
property->priv->value)))
/* make sure we keep the empty string, also... upcomming
* funcs that may not like NULL.
*/
value = g_strdup ("");
/* Now dump the node values... */
prop_node = glade_xml_node_new (context, GLADE_XML_TAG_PROPERTY);
glade_xml_node_append_child (node, prop_node);
/* Name and value */
glade_xml_node_set_property_string (prop_node, GLADE_XML_TAG_NAME, name);
glade_xml_set_content (prop_node, value);
/* i18n stuff */
if (glade_property_class_translatable (property->priv->klass))
{
if (property->priv->i18n_translatable)
glade_xml_node_set_property_string (prop_node,
GLADE_TAG_TRANSLATABLE,
GLADE_XML_TAG_I18N_TRUE);
if (property->priv->i18n_context)
glade_xml_node_set_property_string (prop_node,
GLADE_TAG_CONTEXT,
property->priv->i18n_context);
if (property->priv->i18n_comment)
glade_xml_node_set_property_string (prop_node,
GLADE_TAG_COMMENT,
property->priv->i18n_comment);
}
g_free (name);
g_free (value);
}
/**
* 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;
GParamSpec *pspec;
g_return_if_fail (GLADE_IS_PROPERTY (property));
g_return_if_fail (G_IS_OBJECT (object));
pspec = glade_property_class_get_pspec (property->priv->klass);
g_return_if_fail (GLADE_IS_PARAM_SPEC_OBJECTS (pspec) ||
G_IS_PARAM_SPEC_OBJECT (pspec));
if (GLADE_IS_PARAM_SPEC_OBJECTS (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;
GParamSpec *pspec;
g_return_if_fail (GLADE_IS_PROPERTY (property));
g_return_if_fail (G_IS_OBJECT (object));
pspec = glade_property_class_get_pspec (property->priv->klass);
g_return_if_fail (GLADE_IS_PARAM_SPEC_OBJECTS (pspec) ||
G_IS_PARAM_SPEC_OBJECT (pspec));
if (GLADE_IS_PARAM_SPEC_OBJECTS (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, NULL);
}
}
GladePropertyClass *
glade_property_get_class (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
return property->priv->klass;
}
/* 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->priv->i18n_comment)
g_free (property->priv->i18n_comment);
property->priv->i18n_comment = g_strdup (str);
g_object_notify_by_pspec (G_OBJECT (property), properties[PROP_I18N_COMMENT]);
}
G_CONST_RETURN gchar *
glade_property_i18n_get_comment (GladeProperty * property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
return property->priv->i18n_comment;
}
void
glade_property_i18n_set_context (GladeProperty *property, const gchar *str)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
if (property->priv->i18n_context)
g_free (property->priv->i18n_context);
property->priv->i18n_context = g_strdup (str);
g_object_notify_by_pspec (G_OBJECT (property), properties[PROP_I18N_CONTEXT]);
}
G_CONST_RETURN gchar *
glade_property_i18n_get_context (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
return property->priv->i18n_context;
}
void
glade_property_i18n_set_translatable (GladeProperty *property,
gboolean translatable)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
property->priv->i18n_translatable = translatable;
g_object_notify_by_pspec (G_OBJECT (property), properties[PROP_I18N_TRANSLATABLE]);
}
gboolean
glade_property_i18n_get_translatable (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
return property->priv->i18n_translatable;
}
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->priv->insensitive_tooltip)
g_free (property->priv->insensitive_tooltip);
property->priv->insensitive_tooltip = g_strdup (reason);
}
if (property->priv->sensitive != sensitive)
{
property->priv->sensitive = sensitive;
/* Clear it */
if (sensitive)
property->priv->insensitive_tooltip =
(g_free (property->priv->insensitive_tooltip), NULL);
g_signal_emit (G_OBJECT (property),
glade_property_signals[TOOLTIP_CHANGED],
0,
glade_property_class_get_tooltip (property->priv->klass),
property->priv->insensitive_tooltip,
property->priv->support_warning);
}
g_object_notify_by_pspec (G_OBJECT (property), properties[PROP_SENSITIVE]);
}
G_CONST_RETURN gchar *
glade_propert_get_insensitive_tooltip (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
return property->priv->insensitive_tooltip;
}
gboolean
glade_property_get_sensitive (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
return property->priv->sensitive;
}
void
glade_property_set_support_warning (GladeProperty *property,
gboolean disable,
const gchar *reason)
{
gboolean warn_before, warn_after;
g_return_if_fail (GLADE_IS_PROPERTY (property));
/* Check pre-changed warning state */
warn_before = glade_property_warn_usage (property);
if (property->priv->support_warning)
g_free (property->priv->support_warning);
property->priv->support_warning = g_strdup (reason);
property->priv->support_disabled = disable;
g_signal_emit (G_OBJECT (property),
glade_property_signals[TOOLTIP_CHANGED],
0,
glade_property_class_get_tooltip (property->priv->klass),
property->priv->insensitive_tooltip,
property->priv->support_warning);
glade_property_fix_state (property);
/* Check post-changed warning state */
warn_after = glade_property_warn_usage (property);
/* Update owning widget's warning state if need be */
if (property->priv->widget != NULL && warn_before != warn_after)
glade_widget_verify (property->priv->widget);
}
G_CONST_RETURN gchar *
glade_property_get_support_warning (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
return property->priv->support_warning;
}
gboolean
glade_property_warn_usage (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
if (!property->priv->support_warning)
return FALSE;
return ((property->priv->state & GLADE_STATE_CHANGED) != 0);
}
/**
* 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->priv->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->priv->save_always;
}
void
glade_property_set_enabled (GladeProperty *property, gboolean enabled)
{
gboolean warn_before, warn_after;
g_return_if_fail (GLADE_IS_PROPERTY (property));
/* Check pre-changed warning state */
warn_before = glade_property_warn_usage (property);
property->priv->enabled = enabled;
glade_property_sync (property);
glade_property_fix_state (property);
/* Check post-changed warning state */
warn_after = glade_property_warn_usage (property);
/* Update owning widget's warning state if need be */
if (property->priv->widget != NULL && warn_before != warn_after)
glade_widget_verify (property->priv->widget);
g_object_notify_by_pspec (G_OBJECT (property), properties[PROP_ENABLED]);
}
gboolean
glade_property_get_enabled (GladeProperty * property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE);
return property->priv->enabled;
}
gchar *
glade_property_make_string (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
return glade_property_class_make_string_from_gvalue (property->priv->klass,
property->priv->value);
}
void
glade_property_set_widget (GladeProperty *property,
GladeWidget *widget)
{
g_return_if_fail (GLADE_IS_PROPERTY (property));
property->priv->widget = widget;
}
GladeWidget *
glade_property_get_widget (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
return property->priv->widget;
}
GValue *
glade_property_inline_value (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
return property->priv->value;
}
GladePropertyState
glade_property_get_state (GladeProperty *property)
{
g_return_val_if_fail (GLADE_IS_PROPERTY (property), 0);
return property->priv->state;
}
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;
}