diff options
Diffstat (limited to 'gladeui/glade-property.c')
-rw-r--r-- | gladeui/glade-property.c | 1592 |
1 files changed, 1592 insertions, 0 deletions
diff --git a/gladeui/glade-property.c b/gladeui/glade-property.c new file mode 100644 index 00000000..5cfac0f9 --- /dev/null +++ b/gladeui/glade-property.c @@ -0,0 +1,1592 @@ +/* -*- 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 + +#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; +} + +void +glade_property_reset_impl (GladeProperty *property) +{ + GLADE_PROPERTY_GET_KLASS (property)->set_value + (property, property->klass->def); +} + +gboolean +glade_property_default_impl (GladeProperty *property) +{ + return GLADE_PROPERTY_GET_KLASS (property)->equals_value + (property, property->klass->def); +} + +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 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_widget_adaptor_verify_property (property->widget->adaptor, + property->widget->object, + property->klass->id, + 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) + 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) + */ + if (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->reset = glade_property_reset_impl; + prop_class->def = glade_property_default_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) + * @catalog_default: if specified; use any default value supplied + * by the catalog; otherwise use the introspected default. + * + * + * 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; unless otherwise specified by @catalog_default. + * + * Note that we want to use catalog defaults when creating properties for + * any newly created #GladeWidget; but we want to stay with the introspected + * defaults at load time (since the absence of the property who's default + * has been overridden; is interpreted as explicitly set to the default + * by the user). + * + * Returns: The newly created #GladeProperty + */ +GladeProperty * +glade_property_new (GladePropertyClass *klass, + GladeWidget *widget, + GValue *value, + gboolean catalog_default) +{ + GladeProperty *property; + GValue *def; + + 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) + { + def = catalog_default ? klass->def : klass->orig_def; + g_assert (def); + + property->value = g_new0 (GValue, 1); + g_value_init (property->value, def->g_type); + g_value_copy (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); +} + +/** + * glade_property_reset: + * @property: A #GladeProperty + * + * Resets this property to its default value + */ +void +glade_property_reset (GladeProperty *property) +{ + g_return_if_fail (GLADE_IS_PROPERTY (property)); + GLADE_PROPERTY_GET_KLASS (property)->reset (property); +} + +/** + * glade_property_default: + * @property: A #GladeProperty + * + * Returns: Whether this property is at its default value + */ +gboolean +glade_property_default (GladeProperty *property) +{ + g_return_val_if_fail (GLADE_IS_PROPERTY (property), FALSE); + return GLADE_PROPERTY_GET_KLASS (property)->def (property); +} + +/** + * 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 + */ +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; +} + +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; +} |