/* gda-set.c * * Copyright (C) 2003 - 2010 Vivien Malerba * * This Library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this Library; see the file COPYING.LIB. If not, * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #include #ifdef HAVE_LOCALE_H #include #endif #include "gda-set.h" #include "gda-marshal.h" #include "gda-data-model.h" #include "gda-data-model-import.h" #include "gda-holder.h" #include "gda-connection.h" #include "gda-server-provider.h" #include "gda-util.h" #include #include extern xmlDtdPtr gda_paramlist_dtd; extern gchar *gda_lang_locale; /* * Main static functions */ static void gda_set_class_init (GdaSetClass *class); static void gda_set_init (GdaSet *set); static void gda_set_dispose (GObject *object); static void gda_set_finalize (GObject *object); static void set_remove_node (GdaSet *set, GdaSetNode *node); static void set_remove_source (GdaSet *set, GdaSetSource *source); static void changed_holder_cb (GdaHolder *holder, GdaSet *dataset); static GError *validate_change_holder_cb (GdaHolder *holder, const GValue *value, GdaSet *dataset); static void source_changed_holder_cb (GdaHolder *holder, GdaSet *dataset); static void att_holder_changed_cb (GdaHolder *holder, const gchar *att_name, const GValue *att_value, GdaSet *dataset); static void holder_notify_cb (GdaHolder *holder, GParamSpec *pspec, GdaSet *dataset); static void compute_public_data (GdaSet *set); static gboolean gda_set_real_add_holder (GdaSet *set, GdaHolder *holder); /* get a pointer to the parents to be able to call their destructor */ static GObjectClass *parent_class = NULL; /* properties */ enum { PROP_0, PROP_ID, PROP_NAME, PROP_DESCR, PROP_HOLDERS }; /* signals */ enum { HOLDER_CHANGED, PUBLIC_DATA_CHANGED, HOLDER_ATTR_CHANGED, VALIDATE_HOLDER_CHANGE, VALIDATE_SET, HOLDER_TYPE_SET, SOURCE_MODEL_CHANGED, LAST_SIGNAL }; static gint gda_set_signals[LAST_SIGNAL] = { 0, 0, 0, 0, 0, 0 }; /* private structure */ struct _GdaSetPrivate { gchar *id; gchar *name; gchar *descr; GHashTable *holders_hash; /* key = GdaHoler ID, value = GdaHolder */ GArray *holders_array; gboolean read_only; }; static void gda_set_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { GdaSet* set; set = GDA_SET (object); switch (param_id) { case PROP_ID: g_free (set->priv->id); set->priv->id = g_value_dup_string (value); break; case PROP_NAME: g_free (set->priv->name); set->priv->name = g_value_dup_string (value); break; case PROP_DESCR: g_free (set->priv->descr); set->priv->descr = g_value_dup_string (value); break; case PROP_HOLDERS: { /* add the holders */ GSList* holders; for (holders = (GSList*) g_value_get_pointer(value); holders; holders = holders->next) gda_set_real_add_holder (set, GDA_HOLDER (holders->data)); compute_public_data (set); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void gda_set_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { GdaSet* set; set = GDA_SET (object); switch (param_id) { case PROP_ID: g_value_set_string (value, set->priv->id); break; case PROP_NAME: if (set->priv->name) g_value_set_string (value, set->priv->name); else g_value_set_string (value, set->priv->id); break; case PROP_DESCR: g_value_set_string (value, set->priv->descr); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } /* module error */ GQuark gda_set_error_quark (void) { static GQuark quark; if (!quark) quark = g_quark_from_static_string ("gda_set_error"); return quark; } GType gda_set_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static GStaticMutex registering = G_STATIC_MUTEX_INIT; static const GTypeInfo info = { sizeof (GdaSetClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gda_set_class_init, NULL, NULL, sizeof (GdaSet), 0, (GInstanceInitFunc) gda_set_init, 0 }; g_static_mutex_lock (®istering); if (type == 0) type = g_type_register_static (G_TYPE_OBJECT, "GdaSet", &info, 0); g_static_mutex_unlock (®istering); } return type; } static gboolean validate_accumulator (G_GNUC_UNUSED GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, G_GNUC_UNUSED gpointer data) { GError *error; error = g_value_get_boxed (handler_return); g_value_set_boxed (return_accu, error); return error ? FALSE : TRUE; /* stop signal if an error has been set */ } static GError * m_validate_holder_change (G_GNUC_UNUSED GdaSet *set, G_GNUC_UNUSED GdaHolder *holder, G_GNUC_UNUSED const GValue *new_value) { return NULL; } static GError * m_validate_set (G_GNUC_UNUSED GdaSet *set) { return NULL; } static void gda_set_class_init (GdaSetClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); parent_class = g_type_class_peek_parent (class); gda_set_signals[HOLDER_CHANGED] = g_signal_new ("holder-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GdaSetClass, holder_changed), NULL, NULL, _gda_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GDA_TYPE_HOLDER); /** * GdaSet::validate-holder-change * @set: the #GdaSet * @holder: the #GdaHolder which is going to change * @new_value: the proposed new value for @holder * * Gets emitted when a #GdaHolder's in @set is going to change its value. One can connect to * this signal to control which values @holder can have (for example to implement some business rules) * * Return value: NULL if @holder is allowed to change its value to @new_value, or a #GError * otherwise. */ gda_set_signals[VALIDATE_HOLDER_CHANGE] = g_signal_new ("validate-holder-change", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GdaSetClass, validate_holder_change), validate_accumulator, NULL, _gda_marshal_ERROR__OBJECT_VALUE, GDA_TYPE_ERROR, 2, GDA_TYPE_HOLDER, G_TYPE_VALUE); /** * GdaSet::validate-set * @set: the #GdaSet * * Gets emitted when gda_set_is_valid() is called, use * this signal to control which combination of values @set's holder can have (for example to implement some business rules) * * Return value: NULL if @set's contents has been validated, or a #GError * otherwise. */ gda_set_signals[VALIDATE_SET] = g_signal_new ("validate-set", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GdaSetClass, validate_set), validate_accumulator, NULL, _gda_marshal_ERROR__VOID, GDA_TYPE_ERROR, 0); /** * GdaSet::holder-attr-changed * @set: the #GdaSet * @holder: the GdaHolder for which an attribute changed * @attr_name: attribute's name * @attr_value: attribute's value * * Gets emitted when an attribute for any of the #GdaHolder objects managed by @set has changed */ gda_set_signals[HOLDER_ATTR_CHANGED] = g_signal_new ("holder-attr-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GdaSetClass, holder_attr_changed), NULL, NULL, _gda_marshal_VOID__OBJECT_STRING_VALUE, G_TYPE_NONE, 3, GDA_TYPE_HOLDER, G_TYPE_STRING, G_TYPE_VALUE); /** * GdaSet::public-data-changed * @set: the #GdaSet * * Gets emitted when @set's public data (#GdaSetNode, #GdaSetGroup or #GdaSetSource values) have changed */ gda_set_signals[PUBLIC_DATA_CHANGED] = g_signal_new ("public-data-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GdaSetClass, public_data_changed), NULL, NULL, _gda_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * GdaSet::holder-type-set * @set: the #GdaSet * @holder: the #GdaHolder for which the #GType has been set * * Gets emitted when @holder in @set has its type finally set, in case * it was #GDA_TYPE_NULL * * Since: 4.2 */ gda_set_signals[HOLDER_TYPE_SET] = g_signal_new ("holder-type-set", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GdaSetClass, holder_type_set), NULL, NULL, _gda_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GDA_TYPE_HOLDER); /** * GdaSet::source-model-changed * @set: the #GdaSet * @source: the #GdaSetSource for which the @data_model attribute has changed * * Gets emitted when the data model in @source has changed * * Since: 4.2 */ gda_set_signals[SOURCE_MODEL_CHANGED] = g_signal_new ("source-model-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GdaSetClass, source_model_changed), NULL, NULL, _gda_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); class->holder_changed = NULL; class->validate_holder_change = m_validate_holder_change; class->validate_set = m_validate_set; class->holder_attr_changed = NULL; class->public_data_changed = NULL; class->holder_type_set = NULL; class->source_model_changed = NULL; /* Properties */ object_class->set_property = gda_set_set_property; object_class->get_property = gda_set_get_property; g_object_class_install_property (object_class, PROP_ID, g_param_spec_string ("id", NULL, "Id", NULL, (G_PARAM_READABLE | G_PARAM_WRITABLE))); g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", NULL, "Name", NULL, (G_PARAM_READABLE | G_PARAM_WRITABLE))); g_object_class_install_property (object_class, PROP_DESCR, g_param_spec_string ("description", NULL, "Description", NULL, (G_PARAM_READABLE | G_PARAM_WRITABLE))); g_object_class_install_property (object_class, PROP_HOLDERS, g_param_spec_pointer ("holders", "GSList of GdaHolders", "GdaHolder objects the set should contain", ( G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY))); object_class->dispose = gda_set_dispose; object_class->finalize = gda_set_finalize; } static void gda_set_init (GdaSet *set) { set->priv = g_new0 (GdaSetPrivate, 1); set->holders = NULL; set->nodes_list = NULL; set->sources_list = NULL; set->groups_list = NULL; set->priv->holders_hash = g_hash_table_new (g_str_hash, g_str_equal); set->priv->holders_array = NULL; set->priv->read_only = FALSE; } /** * gda_set_new: * @holders: (element-type GdaHolder) (transfer:none): a list of #GdaHolder objects * * Creates a new #GdaSet object, and populates it with the list given as argument. * The list can then be freed as it is copied. All the value holders in @holders are referenced counted * and modified, so they should not be used anymore afterwards. * * Returns: a new #GdaSet object */ GdaSet * gda_set_new (GSList *holders) { GObject *obj; obj = g_object_new (GDA_TYPE_SET, NULL); for (; holders; holders = holders->next) gda_set_real_add_holder ((GdaSet*) obj, GDA_HOLDER (holders->data)); compute_public_data ((GdaSet*) obj); return (GdaSet*) obj; } /** * gda_set_new_read_only: * @holders: (element-type GdaHolder) (transfer:none): a list of #GdaHolder objects * * Creates a new #GdaSet like gda_set_new(), but does not allow modifications to any of the #GdaHolder * object in @holders. This function is used for Libgda's database providers' implementation. * * Returns: a new #GdaSet object * * Since: 4.2 */ GdaSet * gda_set_new_read_only (GSList *holders) { GObject *obj; obj = g_object_new (GDA_TYPE_SET, NULL); ((GdaSet*) obj)->priv->read_only = TRUE; for (; holders; holders = holders->next) gda_set_real_add_holder ((GdaSet*) obj, GDA_HOLDER (holders->data)); compute_public_data ((GdaSet*) obj); return (GdaSet*) obj; } /** * gda_set_copy: * @set: a #GdaSet object * * Creates a new #GdaSet object, copy of @set * * Returns: (transfer full): a new #GdaSet object */ GdaSet * gda_set_copy (GdaSet *set) { GdaSet *copy; GSList *list, *holders = NULL; g_return_val_if_fail (GDA_IS_SET (set), NULL); for (list = set->holders; list; list = list->next) holders = g_slist_prepend (holders, gda_holder_copy (GDA_HOLDER (list->data))); holders = g_slist_reverse (holders); copy = g_object_new (GDA_TYPE_SET, "holders", holders, NULL); g_slist_foreach (holders, (GFunc) g_object_unref, NULL); g_slist_free (holders); return copy; } /** * gda_set_new_inline: * @nb: the number of value holders which will be contained in the new #GdaSet * @...: a serie of a (const gchar*) id, (GType) type, and value * * Creates a new #GdaSet containing holders defined by each triplet in ... * For each triplet (id, Glib type and value), * the value must be of the correct type (gchar * if type is G_STRING, ...) * * Note that this function is a utility function and that only a limited set of types are supported. Trying * to use an unsupported type will result in a warning, and the returned value holder holding a safe default * value. * * Returns: (transfer full): a new #GdaSet object */ GdaSet * gda_set_new_inline (gint nb, ...) { GdaSet *set = NULL; GSList *holders = NULL; va_list ap; gchar *id; gint i; gboolean allok = TRUE; /* build the list of holders */ va_start (ap, nb); for (i = 0; i < nb; i++) { GType type; GdaHolder *holder; GValue *value; GError *lerror = NULL; id = va_arg (ap, char *); type = va_arg (ap, GType); holder = (GdaHolder *) g_object_new (GDA_TYPE_HOLDER, "g-type", type, "id", id, NULL); value = gda_value_new (type); if (type == G_TYPE_BOOLEAN) g_value_set_boolean (value, va_arg (ap, int)); else if (type == G_TYPE_STRING) g_value_set_string (value, va_arg (ap, gchar *)); else if (type == G_TYPE_OBJECT) g_value_set_object (value, va_arg (ap, gpointer)); else if (type == G_TYPE_INT) g_value_set_int (value, va_arg (ap, gint)); else if (type == G_TYPE_UINT) g_value_set_uint (value, va_arg (ap, guint)); else if (type == GDA_TYPE_BINARY) gda_value_set_binary (value, va_arg (ap, GdaBinary *)); else if (type == G_TYPE_INT64) g_value_set_int64 (value, va_arg (ap, gint64)); else if (type == G_TYPE_UINT64) g_value_set_uint64 (value, va_arg (ap, guint64)); else if (type == GDA_TYPE_SHORT) gda_value_set_short (value, va_arg (ap, int)); else if (type == GDA_TYPE_USHORT) gda_value_set_ushort (value, va_arg (ap, guint)); else if (type == G_TYPE_CHAR) g_value_set_char (value, va_arg (ap, int)); else if (type == G_TYPE_UCHAR) g_value_set_uchar (value, va_arg (ap, guint)); else if (type == G_TYPE_FLOAT) g_value_set_float (value, va_arg (ap, double)); else if (type == G_TYPE_DOUBLE) g_value_set_double (value, va_arg (ap, gdouble)); else if (type == GDA_TYPE_NUMERIC) gda_value_set_numeric (value, va_arg (ap, GdaNumeric *)); else if (type == G_TYPE_DATE) g_value_set_boxed (value, va_arg (ap, GDate *)); else if (type == G_TYPE_LONG) g_value_set_long (value, va_arg (ap, glong)); else if (type == G_TYPE_ULONG) g_value_set_ulong (value, va_arg (ap, gulong)); else if (type == G_TYPE_GTYPE) g_value_set_gtype (value, va_arg(ap, GType)); else { g_warning (_("%s() does not handle values of type '%s'."), __FUNCTION__, g_type_name (type)); g_object_unref (holder); allok = FALSE; break; } if (!gda_holder_take_value (holder, value, &lerror)) { g_warning (_("Unable to set holder's value: %s"), lerror && lerror->message ? lerror->message : _("No detail")); if (lerror) g_error_free (lerror); g_object_unref (holder); allok = FALSE; break; } holders = g_slist_append (holders, holder); } va_end (ap); /* create the set */ if (allok) set = gda_set_new (holders); if (holders) { g_slist_foreach (holders, (GFunc) g_object_unref, NULL); g_slist_free (holders); } return set; } /** * gda_set_set_holder_value: * @set: a #GdaSet object * @error: (allow-none): a place to store errors, or %NULL * @holder_id: the ID of the holder to set the value * @...: value, of the correct type, depending on the requested holder's type (not NULL) * * Set the value of the #GdaHolder which ID is @holder_id to a specified value * * Returns: %TRUE if no error occurred and the value was set correctly */ gboolean gda_set_set_holder_value (GdaSet *set, GError **error, const gchar *holder_id, ...) { GdaHolder *holder; va_list ap; GValue *value; GType type; g_return_val_if_fail (GDA_IS_SET (set), FALSE); g_return_val_if_fail (set->priv, FALSE); holder = gda_set_get_holder (set, holder_id); if (!holder) { g_set_error (error, GDA_SET_ERROR, GDA_SET_HOLDER_NOT_FOUND_ERROR, _("GdaHolder with ID '%s' not found in set"), holder_id); return FALSE; } type = gda_holder_get_g_type (holder); va_start (ap, holder_id); value = gda_value_new (type); if (type == G_TYPE_BOOLEAN) g_value_set_boolean (value, va_arg (ap, int)); else if (type == G_TYPE_STRING) g_value_set_string (value, va_arg (ap, gchar *)); else if (type == G_TYPE_OBJECT) g_value_set_object (value, va_arg (ap, gpointer)); else if (type == G_TYPE_INT) g_value_set_int (value, va_arg (ap, gint)); else if (type == G_TYPE_UINT) g_value_set_uint (value, va_arg (ap, guint)); else if (type == GDA_TYPE_BINARY) gda_value_set_binary (value, va_arg (ap, GdaBinary *)); else if (type == G_TYPE_INT64) g_value_set_int64 (value, va_arg (ap, gint64)); else if (type == G_TYPE_UINT64) g_value_set_uint64 (value, va_arg (ap, guint64)); else if (type == GDA_TYPE_SHORT) gda_value_set_short (value, va_arg (ap, int)); else if (type == GDA_TYPE_USHORT) gda_value_set_ushort (value, va_arg (ap, guint)); else if (type == G_TYPE_CHAR) g_value_set_char (value, va_arg (ap, int)); else if (type == G_TYPE_UCHAR) g_value_set_uchar (value, va_arg (ap, guint)); else if (type == G_TYPE_FLOAT) g_value_set_float (value, va_arg (ap, double)); else if (type == G_TYPE_DOUBLE) g_value_set_double (value, va_arg (ap, gdouble)); else if (type == GDA_TYPE_NUMERIC) gda_value_set_numeric (value, va_arg (ap, GdaNumeric *)); else if (type == G_TYPE_DATE) g_value_set_boxed (value, va_arg (ap, GDate *)); else if (type == G_TYPE_LONG) g_value_set_long (value, va_arg (ap, glong)); else if (type == G_TYPE_ULONG) g_value_set_ulong (value, va_arg (ap, gulong)); else if (type == G_TYPE_GTYPE) g_value_set_gtype (value, va_arg (ap, GType)); else { g_set_error (error, 0, 0, _("%s() does not handle values of type '%s'."), __FUNCTION__, g_type_name (type)); va_end (ap); return FALSE; } va_end (ap); return gda_holder_take_value (holder, value, error); } /** * gda_set_get_holder_value: * @set: a #GdaSet object * @holder_id: the ID of the holder to set the value * * Get the value of the #GdaHolder which ID is @holder_id * * Returns: (transfer none): the requested GValue, or %NULL (see gda_holder_get_value()) */ const GValue * gda_set_get_holder_value (GdaSet *set, const gchar *holder_id) { GdaHolder *holder; g_return_val_if_fail (GDA_IS_SET (set), FALSE); g_return_val_if_fail (set->priv, FALSE); holder = gda_set_get_holder (set, holder_id); if (holder) return gda_holder_get_value (holder); else return NULL; } static void xml_validity_error_func (void *ctx, const char *msg, ...) { va_list args; gchar *str, *str2, *newerr; va_start (args, msg); str = g_strdup_vprintf (msg, args); va_end (args); str2 = *((gchar **) ctx); if (str2) { newerr = g_strdup_printf ("%s\n%s", str2, str); g_free (str2); } else newerr = g_strdup (str); g_free (str); *((gchar **) ctx) = newerr; } /** * gda_set_new_from_spec_string: * @xml_spec: a string * @error: (allow-none): a place to store the error, or %NULL * * Creates a new #GdaSet object from the @xml_spec * specifications * * Returns: (transfer full): a new object, or %NULL if an error occurred */ GdaSet * gda_set_new_from_spec_string (const gchar *xml_spec, GError **error) { xmlDocPtr doc; xmlNodePtr root; GdaSet *set; /* string parsing */ doc = xmlParseMemory (xml_spec, strlen (xml_spec)); if (!doc) return NULL; { /* doc validation */ xmlValidCtxtPtr validc; int xmlcheck; gchar *err_str = NULL; xmlDtdPtr old_dtd = NULL; validc = g_new0 (xmlValidCtxt, 1); validc->userData = &err_str; validc->error = xml_validity_error_func; validc->warning = NULL; xmlcheck = xmlDoValidityCheckingDefaultValue; xmlDoValidityCheckingDefaultValue = 1; /* replace the DTD with ours */ if (gda_paramlist_dtd) { old_dtd = doc->intSubset; doc->intSubset = gda_paramlist_dtd; } #ifndef G_OS_WIN32 if (doc->intSubset && !xmlValidateDocument (validc, doc)) { if (gda_paramlist_dtd) doc->intSubset = old_dtd; xmlFreeDoc (doc); g_free (validc); if (err_str) { g_set_error (error, GDA_SET_ERROR, GDA_SET_XML_SPEC_ERROR, "XML spec. does not conform to DTD:\n%s", err_str); g_free (err_str); } else g_set_error (error, GDA_SET_ERROR, GDA_SET_XML_SPEC_ERROR, "%s", "XML spec. does not conform to DTD"); xmlDoValidityCheckingDefaultValue = xmlcheck; return NULL; } #endif if (gda_paramlist_dtd) doc->intSubset = old_dtd; xmlDoValidityCheckingDefaultValue = xmlcheck; g_free (validc); } /* doc is now non NULL */ root = xmlDocGetRootElement (doc); if (strcmp ((gchar*)root->name, "data-set-spec") != 0){ g_set_error (error, GDA_SET_ERROR, GDA_SET_XML_SPEC_ERROR, _("Spec's root node != 'data-set-spec': '%s'"), root->name); return NULL; } /* creating holders */ root = root->xmlChildrenNode; while (xmlNodeIsText (root)) root = root->next; set = gda_set_new_from_spec_node (root, error); xmlFreeDoc(doc); return set; } /** * gda_set_new_from_spec_node: * @xml_spec: a #xmlNodePtr for a <holders> tag * @error: (allow-none): a place to store the error, or %NULL * * Creates a new #GdaSet object from the @xml_spec * specifications * * Returns: (transfer full): a new object, or %NULL if an error occurred */ GdaSet * gda_set_new_from_spec_node (xmlNodePtr xml_spec, GError **error) { GdaSet *set = NULL; GSList *holders = NULL, *sources = NULL; GSList *list; const gchar *lang = gda_lang_locale; xmlNodePtr cur; gboolean allok = TRUE; gchar *str; if (strcmp ((gchar*)xml_spec->name, "parameters") != 0){ g_set_error (error, GDA_SET_ERROR, GDA_SET_XML_SPEC_ERROR, _("Missing node : '%s'"), xml_spec->name); return NULL; } /* Holders' sources, not mandatory: makes the @sources list */ cur = xml_spec->next; while (cur && (xmlNodeIsText (cur) || strcmp ((gchar*)cur->name, "sources"))) cur = cur->next; if (allok && cur && !strcmp ((gchar*)cur->name, "sources")){ for (cur = cur->xmlChildrenNode; (cur != NULL) && allok; cur = cur->next) { if (xmlNodeIsText (cur)) continue; if (!strcmp ((gchar*)cur->name, "gda_array")) { GdaDataModel *model; GSList *errors; model = gda_data_model_import_new_xml_node (cur); errors = gda_data_model_import_get_errors (GDA_DATA_MODEL_IMPORT (model)); if (errors) { GError *err = (GError *) errors->data; g_set_error (error, 0, 0, "%s", err->message); g_object_unref (model); model = NULL; allok = FALSE; } else { sources = g_slist_prepend (sources, model); str = (gchar*)xmlGetProp(cur, (xmlChar*) "name"); if (str) g_object_set_data_full (G_OBJECT (model), "name", str, xmlFree); } } } } /* holders */ for (cur = xml_spec->xmlChildrenNode; cur && allok; cur = cur->next) { if (xmlNodeIsText (cur)) continue; if (!strcmp ((gchar*)cur->name, "parameter")) { GdaHolder *holder = NULL; gchar *str, *id; xmlChar *this_lang; xmlChar *gdatype; /* don't care about entries for the wrong locale */ this_lang = xmlGetProp(cur, (xmlChar*)"lang"); if (this_lang && strncmp ((gchar*)this_lang, lang, strlen ((gchar*)this_lang))) { g_free (this_lang); continue; } /* find if there is already a holder with the same ID */ id = (gchar*)xmlGetProp(cur, (xmlChar*)"id"); for (list = holders; list && !holder; list = list->next) { str = (gchar *) gda_holder_get_id ((GdaHolder *) list->data); if (str && id && !strcmp (str, id)) holder = (GdaHolder *) list->data; } if (id) xmlFree (id); if (holder && !this_lang) { xmlFree (this_lang); continue; } g_free (this_lang); /* find data type and create GdaHolder */ gdatype = xmlGetProp (cur, BAD_CAST "gdatype"); if (!holder) { holder = (GdaHolder*) (g_object_new (GDA_TYPE_HOLDER, "g-type", gdatype ? gda_g_type_from_string ((gchar *) gdatype) : G_TYPE_STRING, NULL)); holders = g_slist_append (holders, holder); } if (gdatype) xmlFree (gdatype); /* set holder's attributes */ if (! gda_utility_holder_load_attributes (holder, cur, sources, error)) allok = FALSE; } } /* setting prepared new names from sources (models) */ for (list = sources; list; list = list->next) { str = g_object_get_data (G_OBJECT (list->data), "newname"); if (str) { g_object_set_data_full (G_OBJECT (list->data), "name", g_strdup (str), g_free); g_object_set_data (G_OBJECT (list->data), "newname", NULL); } str = g_object_get_data (G_OBJECT (list->data), "newdescr"); if (str) { g_object_set_data_full (G_OBJECT (list->data), "descr", g_strdup (str), g_free); g_object_set_data (G_OBJECT (list->data), "newdescr", NULL); } } /* holders' values, constraints: TODO */ /* GdaSet creation */ if (allok) { xmlChar *prop;; set = gda_set_new (holders); prop = xmlGetProp(xml_spec, (xmlChar*)"id"); if (prop) { set->priv->id = g_strdup ((gchar*)prop); xmlFree (prop); } prop = xmlGetProp(xml_spec, (xmlChar*)"name"); if (prop) { set->priv->name = g_strdup ((gchar*)prop); xmlFree (prop); } prop = xmlGetProp(xml_spec, (xmlChar*)"descr"); if (prop) { set->priv->descr = g_strdup ((gchar*)prop); xmlFree (prop); } } g_slist_foreach (holders, (GFunc) g_object_unref, NULL); g_slist_free (holders); g_slist_foreach (sources, (GFunc) g_object_unref, NULL); g_slist_free (sources); return set; } /** * gda_set_remove_holder: * @set: a #GdaSet object * @holder: the #GdaHolder to remove from @set * * Removes a #GdaHolder from the list of holders managed by @set */ void gda_set_remove_holder (GdaSet *set, GdaHolder *holder) { GdaSetNode *node; g_return_if_fail (GDA_IS_SET (set)); g_return_if_fail (set->priv); g_return_if_fail (g_slist_find (set->holders, holder)); g_signal_handlers_disconnect_by_func (G_OBJECT (holder), G_CALLBACK (validate_change_holder_cb), set); if (! set->priv->read_only) { g_signal_handlers_disconnect_by_func (G_OBJECT (holder), G_CALLBACK (changed_holder_cb), set); g_signal_handlers_disconnect_by_func (G_OBJECT (holder), G_CALLBACK (source_changed_holder_cb), set); g_signal_handlers_disconnect_by_func (G_OBJECT (holder), G_CALLBACK (att_holder_changed_cb), set); } g_signal_handlers_disconnect_by_func (holder, G_CALLBACK (holder_notify_cb), set); /* now destroy the GdaSetNode and the GdaSetSource if necessary */ node = gda_set_get_node (set, holder); g_assert (node); if (node->source_model) { GdaSetSource *source; source = gda_set_get_source_for_model (set, node->source_model); g_assert (source); g_assert (source->nodes); if (! source->nodes->next) set_remove_source (set, source); } set_remove_node (set, node); set->holders = g_slist_remove (set->holders, holder); g_hash_table_remove (set->priv->holders_hash, gda_holder_get_id (holder)); if (set->priv->holders_array) { g_array_free (set->priv->holders_array, TRUE); set->priv->holders_array = NULL; } g_object_unref (G_OBJECT (holder)); } static void source_changed_holder_cb (G_GNUC_UNUSED GdaHolder *holder, GdaSet *set) { compute_public_data (set); } static void att_holder_changed_cb (GdaHolder *holder, const gchar *att_name, const GValue *att_value, GdaSet *set) { #ifdef GDA_DEBUG_signal g_print (">> 'HOLDER_ATTR_CHANGED' from %s\n", __FUNCTION__); #endif g_signal_emit (G_OBJECT (set), gda_set_signals[HOLDER_ATTR_CHANGED], 0, holder, att_name, att_value); #ifdef GDA_DEBUG_signal g_print ("<< 'HOLDER_ATTR_CHANGED' from %s\n", __FUNCTION__); #endif } static GError * validate_change_holder_cb (GdaHolder *holder, const GValue *value, GdaSet *set) { /* signal the holder validate-change */ GError *error = NULL; if (set->priv->read_only) g_set_error (&error, GDA_SET_ERROR, GDA_SET_READ_ONLY_ERROR, _("Data set does not allow modifications")); else { #ifdef GDA_DEBUG_signal g_print (">> 'VALIDATE_HOLDER_CHANGE' from %s\n", __FUNCTION__); #endif g_signal_emit (G_OBJECT (set), gda_set_signals[VALIDATE_HOLDER_CHANGE], 0, holder, value, &error); #ifdef GDA_DEBUG_signal g_print ("<< 'VALIDATE_HOLDER_CHANGED' from %s\n", __FUNCTION__); #endif } return error; } static void changed_holder_cb (GdaHolder *holder, GdaSet *set) { /* signal the holder change */ #ifdef GDA_DEBUG_signal g_print (">> 'HOLDER_CHANGED' from %s\n", __FUNCTION__); #endif g_signal_emit (G_OBJECT (set), gda_set_signals[HOLDER_CHANGED], 0, holder); #ifdef GDA_DEBUG_signal g_print ("<< 'HOLDER_CHANGED' from %s\n", __FUNCTION__); #endif } static void group_free (GdaSetGroup *group, G_GNUC_UNUSED gpointer data) { g_slist_free (group->nodes); g_free (group); } static void gda_set_dispose (GObject *object) { GdaSet *set; GSList *list; g_return_if_fail (object != NULL); g_return_if_fail (GDA_IS_SET (object)); set = GDA_SET (object); /* free the holders list */ if (set->holders) { for (list = set->holders; list; list = list->next) { g_signal_handlers_disconnect_by_func (G_OBJECT (list->data), G_CALLBACK (validate_change_holder_cb), set); if (! set->priv->read_only) { g_signal_handlers_disconnect_by_func (G_OBJECT (list->data), G_CALLBACK (changed_holder_cb), set); g_signal_handlers_disconnect_by_func (G_OBJECT (list->data), G_CALLBACK (source_changed_holder_cb), set); g_signal_handlers_disconnect_by_func (G_OBJECT (list->data), G_CALLBACK (att_holder_changed_cb), set); } g_object_unref (list->data); } g_slist_free (set->holders); } if (set->priv->holders_hash) { g_hash_table_destroy (set->priv->holders_hash); set->priv->holders_hash = NULL; } if (set->priv->holders_array) { g_array_free (set->priv->holders_array, TRUE); set->priv->holders_array = NULL; } /* free the nodes if there are some */ while (set->nodes_list) set_remove_node (set, GDA_SET_NODE (set->nodes_list->data)); while (set->sources_list) set_remove_source (set, GDA_SET_SOURCE (set->sources_list->data)); g_slist_foreach (set->groups_list, (GFunc) group_free, NULL); g_slist_free (set->groups_list); set->groups_list = NULL; /* parent class */ parent_class->dispose (object); } static void gda_set_finalize (GObject *object) { GdaSet *set; g_return_if_fail (object != NULL); g_return_if_fail (GDA_IS_SET (object)); set = GDA_SET (object); if (set->priv) { g_free (set->priv->id); g_free (set->priv->name); g_free (set->priv->descr); g_free (set->priv); set->priv = NULL; } /* parent class */ parent_class->finalize (object); } /* * Resets and computes set->nodes, and if some nodes already exist, they are previously discarded */ static void compute_public_data (GdaSet *set) { GSList *list; GdaSetNode *node; GdaSetSource *source; GdaSetGroup *group; GHashTable *groups = NULL; /* * Get rid of all the previous structures */ while (set->nodes_list) set_remove_node (set, GDA_SET_NODE (set->nodes_list->data)); while (set->sources_list) set_remove_source (set, GDA_SET_SOURCE (set->sources_list->data)); g_slist_foreach (set->groups_list, (GFunc) group_free, NULL); g_slist_free (set->groups_list); set->groups_list = NULL; /* * Creation of the GdaSetNode structures */ for (list = set->holders; list; list = list->next) { node = g_new0 (GdaSetNode, 1); node->holder = GDA_HOLDER (list->data); node->source_model = gda_holder_get_source_model (node->holder, &(node->source_column)); if (node->source_model) g_object_ref (node->source_model); set->nodes_list = g_slist_prepend (set->nodes_list, node); } set->nodes_list = g_slist_reverse (set->nodes_list); /* * Creation of the GdaSetSource and GdaSetGroup structures */ for (list = set->nodes_list; list;list = list->next) { node = GDA_SET_NODE (list->data); /* source */ source = NULL; if (node->source_model) { source = gda_set_get_source_for_model (set, node->source_model); if (source) source->nodes = g_slist_append (source->nodes, node); else { source = g_new0 (GdaSetSource, 1); source->data_model = node->source_model; source->nodes = g_slist_append (NULL, node); set->sources_list = g_slist_prepend (set->sources_list, source); } } /* group */ group = NULL; if (node->source_model && groups) group = g_hash_table_lookup (groups, node->source_model); if (group) group->nodes = g_slist_append (group->nodes, node); else { group = g_new0 (GdaSetGroup, 1); group->nodes = g_slist_append (NULL, node); group->nodes_source = source; set->groups_list = g_slist_prepend (set->groups_list, group); if (node->source_model) { if (!groups) groups = g_hash_table_new (NULL, NULL); /* key = source model, value = GdaSetGroup */ g_hash_table_insert (groups, node->source_model, group); } } } set->groups_list = g_slist_reverse (set->groups_list); if (groups) g_hash_table_destroy (groups); #ifdef GDA_DEBUG_signal g_print (">> 'PUBLIC_DATA_CHANGED' from %p\n", set); #endif g_signal_emit (set, gda_set_signals[PUBLIC_DATA_CHANGED], 0); #ifdef GDA_DEBUG_signal g_print ("<< 'PUBLIC_DATA_CHANGED' from %p\n", set); #endif } /** * gda_set_add_holder: * @set: a #GdaSet object * @holder: a #GdaHolder object * * Adds @holder to the list of holders managed within @set. * * NOTE: if @set already has a #GdaHolder with the same ID as @holder, then @holder * will not be added to the set (even if @holder's type or value is not the same as the * one already in @set). * * Returns: TRUE if @holder has been added to @set (and FALSE if it has not been added because there is another #GdaHolder * with the same ID) */ gboolean gda_set_add_holder (GdaSet *set, GdaHolder *holder) { gboolean added; g_return_val_if_fail (GDA_IS_SET (set), FALSE); g_return_val_if_fail (GDA_IS_HOLDER (holder), FALSE); added = gda_set_real_add_holder (set, holder); if (added) compute_public_data (set); return added; } static void holder_notify_cb (GdaHolder *holder, GParamSpec *pspec, GdaSet *dataset) { GType gtype; gtype = gda_holder_get_g_type (holder); if (!strcmp (pspec->name, "g-type")) { g_assert (gtype != GDA_TYPE_NULL); g_signal_emit (dataset, gda_set_signals[HOLDER_TYPE_SET], 0, holder); } else if (!strcmp (pspec->name, "name")) { #ifdef GDA_DEBUG_signal g_print (">> 'HOLDER_ATTR_CHANGED' from %s\n", __FUNCTION__); #endif g_signal_emit (G_OBJECT (dataset), gda_set_signals[HOLDER_ATTR_CHANGED], 0, holder, GDA_ATTRIBUTE_NAME, gda_holder_get_attribute (holder, GDA_ATTRIBUTE_NAME)); #ifdef GDA_DEBUG_signal g_print ("<< 'HOLDER_ATTR_CHANGED' from %s\n", __FUNCTION__); #endif } else if (!strcmp (pspec->name, "description")) { #ifdef GDA_DEBUG_signal g_print (">> 'HOLDER_ATTR_CHANGED' from %s\n", __FUNCTION__); #endif g_signal_emit (G_OBJECT (dataset), gda_set_signals[HOLDER_ATTR_CHANGED], 0, holder, GDA_ATTRIBUTE_DESCRIPTION, gda_holder_get_attribute (holder, GDA_ATTRIBUTE_DESCRIPTION)); #ifdef GDA_DEBUG_signal g_print ("<< 'HOLDER_ATTR_CHANGED' from %s\n", __FUNCTION__); #endif } } static gboolean gda_set_real_add_holder (GdaSet *set, GdaHolder *holder) { GdaHolder *similar; const gchar *hid; /* * try to find a similar holder in the set->holders: * a holder B is similar to a holder A if it has the same ID */ hid = gda_holder_get_id (holder); if (!hid) { g_warning (_("GdaHolder needs to have an ID")); return FALSE; } similar = (GdaHolder*) g_hash_table_lookup (set->priv->holders_hash, hid); if (!similar) { /* really add @holder to the set */ set->holders = g_slist_append (set->holders, holder); g_hash_table_insert (set->priv->holders_hash, (gchar*) hid, holder); if (set->priv->holders_array) { g_array_free (set->priv->holders_array, TRUE); set->priv->holders_array = NULL; } g_object_ref (holder); g_signal_connect (G_OBJECT (holder), "validate-change", G_CALLBACK (validate_change_holder_cb), set); if (! set->priv->read_only) { g_signal_connect (G_OBJECT (holder), "changed", G_CALLBACK (changed_holder_cb), set); g_signal_connect (G_OBJECT (holder), "source-changed", G_CALLBACK (source_changed_holder_cb), set); g_signal_connect (G_OBJECT (holder), "attribute-changed", G_CALLBACK (att_holder_changed_cb), set); } if (gda_holder_get_g_type (holder) == GDA_TYPE_NULL) g_signal_connect (G_OBJECT (holder), "notify::g-type", G_CALLBACK (holder_notify_cb), set); g_signal_connect (G_OBJECT (holder), "notify::name", G_CALLBACK (holder_notify_cb), set); g_signal_connect (G_OBJECT (holder), "notify::description", G_CALLBACK (holder_notify_cb), set); return TRUE; } else if (similar == holder) return FALSE; else { #ifdef GDA_DEBUG_NO g_print ("In Set %p, Holder %p and %p are similar, keeping %p only\n", set, similar, holder, similar); #endif return FALSE; } } /** * gda_set_merge_with_set: * @set: a #GdaSet object * @set_to_merge: a #GdaSet object * * Add to @set all the holders of @set_to_merge. * Note1: only the #GdaHolder of @set_to_merge for which no holder in @set has the same ID are merged * Note2: all the #GdaHolder merged in @set are still used by @set_to_merge. */ void gda_set_merge_with_set (GdaSet *set, GdaSet *set_to_merge) { GSList *holders; g_return_if_fail (GDA_IS_SET (set)); g_return_if_fail (set_to_merge && GDA_IS_SET (set_to_merge)); for (holders = set_to_merge->holders; holders; holders = holders->next) gda_set_real_add_holder (set, GDA_HOLDER (holders->data)); compute_public_data (set); } static void set_remove_node (GdaSet *set, GdaSetNode *node) { g_return_if_fail (g_slist_find (set->nodes_list, node)); if (node->source_model) g_object_unref (G_OBJECT (node->source_model)); set->nodes_list = g_slist_remove (set->nodes_list, node); g_free (node); } static void set_remove_source (GdaSet *set, GdaSetSource *source) { g_return_if_fail (g_slist_find (set->sources_list, source)); if (source->nodes) g_slist_free (source->nodes); set->sources_list = g_slist_remove (set->sources_list, source); g_free (source); } /** * gda_set_is_valid: * @set: a #GdaSet object * @error: (allow-none): a place to store validation errors, or %NULL * * This method tells if all @set's #GdaHolder objects are valid, and if * they represent a valid combination of values, as defined by rules * external to Libgda: the "validate-set" signal is emitted and if none of the signal handlers return an * error, then the returned value is TRUE, otherwise the return value is FALSE as soon as a signal handler * returns an error. * * Returns: TRUE if the set is valid */ gboolean gda_set_is_valid (GdaSet *set, GError **error) { GSList *holders; g_return_val_if_fail (GDA_IS_SET (set), FALSE); g_return_val_if_fail (set->priv, FALSE); for (holders = set->holders; holders; holders = holders->next) { if (!gda_holder_is_valid ((GdaHolder*) holders->data)) { g_set_error (error, GDA_SET_ERROR, GDA_SET_INVALID_ERROR, "%s", _("One or more values are invalid")); return FALSE; } } return _gda_set_validate (set, error); } gboolean _gda_set_validate (GdaSet *set, GError **error) { /* signal the holder validate-set */ GError *lerror = NULL; #ifdef GDA_DEBUG_signal g_print (">> 'VALIDATE_SET' from %s\n", __FUNCTION__); #endif g_signal_emit (G_OBJECT (set), gda_set_signals[VALIDATE_SET], 0, &lerror); #ifdef GDA_DEBUG_signal g_print ("<< 'VALIDATE_SET' from %s\n", __FUNCTION__); #endif if (lerror) { g_propagate_error (error, lerror); return FALSE; } return TRUE; } /** * gda_set_get_holder: * @set: a #GdaSet object * @holder_id: the ID of the requested value holder * * Finds a #GdaHolder using its ID * * Returns: (transfer none): the requested #GdaHolder or %NULL */ GdaHolder * gda_set_get_holder (GdaSet *set, const gchar *holder_id) { g_return_val_if_fail (GDA_IS_SET (set), NULL); g_return_val_if_fail (holder_id, NULL); return (GdaHolder *) g_hash_table_lookup (set->priv->holders_hash, holder_id); } /** * gda_set_get_nth_holder: * @set: a #GdaSet object * @pos: the position of the requested #GdaHolder, starting at %0 * * Finds a #GdaHolder using its position * * Returns: (transfer none): the requested #GdaHolder or %NULL * * Since: 4.2 */ GdaHolder * gda_set_get_nth_holder (GdaSet *set, gint pos) { g_return_val_if_fail (GDA_IS_SET (set), NULL); g_return_val_if_fail (pos >= 0, NULL); if (! set->priv->holders_array) { GSList *list; set->priv->holders_array = g_array_sized_new (FALSE, FALSE, sizeof (GdaHolder*), g_slist_length (set->holders)); for (list = set->holders; list; list = list->next) g_array_append_val (set->priv->holders_array, list->data); } if ((guint)pos >= set->priv->holders_array->len) return NULL; else return g_array_index (set->priv->holders_array, GdaHolder*, pos); } /** * gda_set_get_node: * @set: a #GdaSet object * @holder: a #GdaHolder object * * Finds a #GdaSetNode holding information for @holder, don't modify the returned structure * * Returns: (transfer none): the requested #GdaSetNode or %NULL */ GdaSetNode * gda_set_get_node (GdaSet *set, GdaHolder *holder) { GdaSetNode *retval = NULL; GSList *list; g_return_val_if_fail (GDA_IS_SET (set), NULL); g_return_val_if_fail (set->priv, NULL); g_return_val_if_fail (GDA_IS_HOLDER (holder), NULL); g_return_val_if_fail (g_slist_find (set->holders, holder), NULL); for (list = set->nodes_list; list && !retval; list = list->next) { if (GDA_SET_NODE (list->data)->holder == holder) retval = GDA_SET_NODE (list->data); } return retval; } /** * gda_set_get_source: * @set: a #GdaSet object * @holder: a #GdaHolder object * * Finds a #GdaSetSource which contains the #GdaDataModel restricting the possible values of * @holder, don't modify the returned structure. * * Returns: (transfer none): the requested #GdaSetSource or %NULL */ GdaSetSource * gda_set_get_source (GdaSet *set, GdaHolder *holder) { GdaSetNode *node; node = gda_set_get_node (set, holder); if (node && node->source_model) return gda_set_get_source_for_model (set, node->source_model); else return NULL; } /** * gda_set_get_group: * @set: a #GdaSet object * @holder: a #GdaHolder object * * Finds a #GdaSetGroup which lists a #GdaSetNode containing @holder, * don't modify the returned structure. * * Returns: (transfer none): the requested #GdaSetGroup or %NULL */ GdaSetGroup * gda_set_get_group (GdaSet *set, GdaHolder *holder) { GdaSetGroup *retval = NULL; GSList *list, *sublist; g_return_val_if_fail (GDA_IS_SET (set), NULL); g_return_val_if_fail (set->priv, NULL); g_return_val_if_fail (GDA_IS_HOLDER (holder), NULL); g_return_val_if_fail (g_slist_find (set->holders, holder), NULL); for (list = set->groups_list; list && !retval; list = list->next) { sublist = GDA_SET_GROUP (list->data)->nodes; while (sublist && !retval) { if (GDA_SET_NODE (sublist->data)->holder == holder) retval = GDA_SET_GROUP (list->data); else sublist = g_slist_next (sublist); } } return retval; } /** * gda_set_get_source_for_model: * @set: a #GdaSet object * @model: a #GdaDataModel object * * Finds the #GdaSetSource structure used in @set for which @model is a * the data model (the returned structure should not be modified). * * Returns: (transfer none): the requested #GdaSetSource pointer or %NULL. */ GdaSetSource * gda_set_get_source_for_model (GdaSet *set, GdaDataModel *model) { GdaSetSource *retval = NULL; GSList *list; g_return_val_if_fail (GDA_IS_SET (set), NULL); g_return_val_if_fail (set->priv, NULL); g_return_val_if_fail (GDA_IS_DATA_MODEL (model), NULL); list = set->sources_list; while (list && !retval) { if (GDA_SET_SOURCE (list->data)->data_model == model) retval = GDA_SET_SOURCE (list->data); list = g_slist_next (list); } return retval; } /** * gda_set_replace_source_model * @set: a #GdaSet object * @source: a pointer to a #GdaSetSource in @set * @model: a #GdaDataModel * * Replaces @source->data_model with @model, which must have the same * characteristics as @source->data_model (same column types) * * Also for each #GdaHolder for which @source->data_model is a source model, * this method calls gda_holder_set_source_model() with @model to replace * the source by the new model * * Since: 4.2 */ void gda_set_replace_source_model (GdaSet *set, GdaSetSource *source, GdaDataModel *model) { g_return_if_fail (GDA_IS_SET (set)); g_return_if_fail (source); g_return_if_fail (g_slist_find (set->sources_list, source)); g_return_if_fail (GDA_IS_DATA_MODEL (model)); /* compare models */ gint ncols, i; ncols = gda_data_model_get_n_columns (source->data_model); if (ncols != gda_data_model_get_n_columns (model)) { g_warning (_("Replacing data model must have the same characteristics as the " "data model it replaces")); return; } for (i = 0; i < ncols; i++) { GdaColumn *c1, *c2; GType t1, t2; c1 = gda_data_model_describe_column (source->data_model, i); c2 = gda_data_model_describe_column (model, i); t1 = gda_column_get_g_type (c1); t2 = gda_column_get_g_type (c2); if ((t1 != GDA_TYPE_NULL) && (t2 != GDA_TYPE_NULL) && (t1 != t2)) { g_warning (_("Replacing data model must have the same characteristics as the " "data model it replaces")); return; } } /* actually swap the models */ GSList *list; source->data_model = model; for (list = source->nodes; list; list = list->next) { GdaSetNode *node = (GdaSetNode*) list->data; g_object_unref (node->source_model); node->source_model = g_object_ref (model); g_signal_handlers_block_by_func (G_OBJECT (node->holder), G_CALLBACK (source_changed_holder_cb), set); gda_holder_set_source_model (GDA_HOLDER (node->holder), model, node->source_column, NULL); g_signal_handlers_unblock_by_func (G_OBJECT (node->holder), G_CALLBACK (source_changed_holder_cb), set); } #ifdef GDA_DEBUG_signal g_print (">> 'SOURCE_MODEL_CHANGED' from %s\n", __FUNCTION__); #endif g_signal_emit (G_OBJECT (set), gda_set_signals[SOURCE_MODEL_CHANGED], 0, source); #ifdef GDA_DEBUG_signal g_print ("<< 'SOURCE_MODEL_CHANGED' from %s\n", __FUNCTION__); #endif }