Commit 7613e0d4 authored by Tristan Van Berkom's avatar Tristan Van Berkom

Added load/save/edit support for GtkTreeStore/GtkListStore basic columns


	* plugins/gtk+/glade-gtk.c, plugins/gtk+/glade-column-types.[ch],	
	  plugins/gtk+/Makefile.am, plugins/gtk+/gtk+.xml.in: Added load/save/edit
	  support for GtkTreeStore/GtkListStore basic columns and data definitions
	  (store data still not translatable...) - Juan Pablo Ugarte

	* gladeui/glade-utils.c: Added convenience funcs glade_utils_string_from_value()
	  and glade_utils_value_from_string(), these actually use glade-property-class api.


svn path=/trunk/; revision=1961
parent 036d95a4
2008-09-28 Tristan Van Berkom <tvb@gnome.org>
* plugins/gtk+/glade-gtk.c, plugins/gtk+/glade-column-types.[ch],
plugins/gtk+/Makefile.am, plugins/gtk+/gtk+.xml.in: Added load/save/edit
support for GtkTreeStore/GtkListStore basic columns and data definitions
(store data still not translatable...) - Juan Pablo Ugarte
* gladeui/glade-utils.c: Added convenience funcs glade_utils_string_from_value()
and glade_utils_value_from_string(), these actually use glade-property-class api.
2008-09-22 Juan Pablo Ugarte <juanpablougarte@gmail.com>
* plugins/gtk+/glade-gtk.c: fixed bug #543314
......
......@@ -11,3 +11,6 @@ GdkColor null values critical errors: http://bugzilla.gnome.org/show_bug.cgi?id=
Add versioning metadata for libglade unsupported properties and widgets.
Sort objects from widgets in the inspector widget.
Make popup work in workspace with no-window widgets
Take care of external object property references (sync them) at rebuild time
Liststore/Treestore data is not translatable
......@@ -110,6 +110,7 @@ libgladeuiinclude_HEADERS = \
glade-xml-utils.h \
glade-signal.h \
glade-cursor.h \
glade-catalog.h \
glade-widget-action.h
......
......@@ -325,7 +325,6 @@ glade_palette_finalize (GObject *object)
static void
glade_palette_toggled (GladePalette *palette)
{
GladeProject *project;
GladeWidgetAdaptor *adaptor;
GladeWidget *widget;
......
......@@ -1028,17 +1028,14 @@ glade_property_class_get_from_gvalue (GladePropertyClass *klass,
va_end (vl);
}
/**
* glade_property_class_new_from_spec:
* @handle: A generic pointer (i.e. a #GladeWidgetClass)
* @spec: A #GParamSpec
*
* Returns: a newly created #GladePropertyClass based on @spec
* or %NULL if its unsupported.
/* "need_handle": An evil trick to let us create pclasses without
* adaptors and editors.
*/
GladePropertyClass *
glade_property_class_new_from_spec (gpointer handle,
GParamSpec *spec)
glade_property_class_new_from_spec_full (gpointer handle,
GParamSpec *spec,
gboolean need_handle)
{
GObjectClass *gtk_widget_class;
GladePropertyClass *property_class;
......@@ -1063,7 +1060,7 @@ glade_property_class_new_from_spec (gpointer handle,
/* Register only editable properties.
*/
if (!(eprop = glade_widget_adaptor_create_eprop
if (need_handle && !(eprop = glade_widget_adaptor_create_eprop
(GLADE_WIDGET_ADAPTOR (handle), property_class, FALSE)))
goto failed;
......@@ -1101,6 +1098,21 @@ glade_property_class_new_from_spec (gpointer handle,
return NULL;
}
/**
* glade_property_class_new_from_spec:
* @handle: A generic pointer (i.e. a #GladeWidgetClass)
* @spec: A #GParamSpec
*
* Returns: a newly created #GladePropertyClass based on @spec
* or %NULL if its unsupported.
*/
GladePropertyClass *
glade_property_class_new_from_spec (gpointer handle,
GParamSpec *spec)
{
glade_property_class_new_from_spec_full (handle, spec, TRUE);
}
/**
* glade_property_class_is_visible:
* @property_class: A #GladePropertyClass
......
......@@ -182,6 +182,10 @@ GladePropertyClass *glade_property_class_new (gpointer
GladePropertyClass *glade_property_class_new_from_spec (gpointer handle,
GParamSpec *spec);
GladePropertyClass *glade_property_class_new_from_spec_full (gpointer handle,
GParamSpec *spec,
gboolean need_handle);
GladePropertyClass *glade_property_class_clone (GladePropertyClass *property_class);
void glade_property_class_free (GladePropertyClass *property_class);
......
......@@ -1940,37 +1940,188 @@ glade_util_icon_name_to_filename (const gchar *value)
gint
glade_utils_enum_value_from_string (GType enum_type, const gchar *strval)
{
GEnumClass *enum_class;
GEnumValue *enum_value;
gint value = 0;
gint value = 0;
GValue *gvalue;
enum_class = g_type_class_ref (enum_type);
if ((enum_value = g_enum_get_value_by_nick (enum_class, strval)) != NULL)
value = enum_value->value;
else
g_critical ("Couldnt find enum value for %s, type %s",
strval, g_type_name (enum_type));
g_type_class_unref (enum_class);
if ((gvalue = glade_utils_value_from_string (enum_type, strval, NULL)) != NULL)
{
value = g_value_get_enum (gvalue);
g_value_unset (gvalue);
g_free (gvalue);
}
return value;
}
gchar *
glade_utils_enum_string_from_value (GType enum_type, gint value)
{
GEnumClass *enum_class;
GEnumValue *enum_value;
gchar *ret = NULL;
GValue gvalue = { 0, };
gchar *string;
enum_class = g_type_class_ref (enum_type);
if ((enum_value = g_enum_get_value (enum_class, value)) != NULL)
ret = g_strdup (enum_value->value_nick);
else
g_critical ("Couldnt find enum value for %d, type %s",
value, g_type_name (enum_type));
g_type_class_unref (enum_class);
g_value_init (&gvalue, enum_type);
g_value_set_enum (&gvalue, value);
string = glade_utils_string_from_value (enum_type, &gvalue, NULL);
g_value_unset (&gvalue);
return string;
}
/* A hash table of generically created property classes for
* fundamental types, so we can easily use glade's conversion
* system without using properties (only GTypes)
*/
static GHashTable *generic_property_classes = NULL;
static gboolean
utils_gtype_equal (gconstpointer v1,
gconstpointer v2)
{
return *((const GType*) v1) == *((const GType*) v2);
}
static guint
utils_gtype_hash (gconstpointer v)
{
return *(const GType*) v;
}
static GladePropertyClass *
pclass_from_gtype (GType type)
{
GladePropertyClass *property_class = NULL;
GParamSpec *pspec = NULL;
if (!generic_property_classes)
generic_property_classes = g_hash_table_new_full (utils_gtype_hash, utils_gtype_equal,
g_free, (GDestroyNotify)glade_property_class_free);
return ret;
property_class = g_hash_table_lookup (generic_property_classes, &type);
if (!property_class)
{
/* Support enum and flag types, and a hardcoded list of fundamental types */
if (type == G_TYPE_CHAR)
pspec = g_param_spec_char ("dummy", "dummy", "dummy",
G_MININT8, G_MAXINT8, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_UCHAR)
pspec = g_param_spec_char ("dummy", "dummy", "dummy",
0, G_MAXUINT8, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_BOOLEAN)
pspec = g_param_spec_boolean ("dummy", "dummy", "dummy",
FALSE, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_INT)
pspec = g_param_spec_int ("dummy", "dummy", "dummy",
G_MININT, G_MAXINT, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_UINT)
pspec = g_param_spec_uint ("dummy", "dummy", "dummy",
0, G_MAXUINT, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_LONG)
pspec = g_param_spec_long ("dummy", "dummy", "dummy",
G_MINLONG, G_MAXLONG, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_ULONG)
pspec = g_param_spec_ulong ("dummy", "dummy", "dummy",
0, G_MAXULONG, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_INT64)
pspec = g_param_spec_int64 ("dummy", "dummy", "dummy",
G_MININT64, G_MAXINT64, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_UINT64)
pspec = g_param_spec_uint64 ("dummy", "dummy", "dummy",
0, G_MAXUINT64, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_FLOAT)
pspec = g_param_spec_float ("dummy", "dummy", "dummy",
G_MINFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_DOUBLE)
pspec = g_param_spec_double ("dummy", "dummy", "dummy",
G_MINDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_STRING)
pspec = g_param_spec_string ("dummy", "dummy", "dummy",
NULL, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (type == G_TYPE_OBJECT || g_type_is_a (type, G_TYPE_OBJECT))
pspec = g_param_spec_object ("dummy", "dummy", "dummy",
type, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (G_TYPE_IS_ENUM (type))
pspec = g_param_spec_enum ("dummy", "dummy", "dummy",
type, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
else if (G_TYPE_IS_FLAGS (type))
pspec = g_param_spec_flags ("dummy", "dummy", "dummy",
type, 0, G_PARAM_READABLE|G_PARAM_WRITABLE);
if (pspec)
{
if ((property_class =
glade_property_class_new_from_spec_full (NULL, pspec, FALSE)) != NULL)
{
/* XXX If we ever free the hash table, property classes wont touch
* the allocated pspecs, so they would theoretically be leaked.
*/
g_hash_table_insert (generic_property_classes,
g_memdup (&type, sizeof (GType)), property_class);
}
else
g_warning ("Unable to create property class for type %s", g_type_name (type));
}
else
g_warning ("No generic conversion support for type %s", g_type_name (type));
}
return property_class;
}
/**
* glade_utils_value_from_string:
* @type: a #GType to convert with
* @string: the string to convert
* @project: the #GladeProject to look for formats of object names when needed
*
* Allocates and sets a #GValue of type @type
* set to @string (using glade conversion routines)
*
* Returns: A newly allocated and set #GValue
*/
GValue *
glade_utils_value_from_string (GType type,
const gchar *string,
GladeProject *project)
{
GladePropertyClass *pclass;
g_return_val_if_fail (type != G_TYPE_INVALID, NULL);
g_return_val_if_fail (string != NULL, NULL);
if ((pclass = pclass_from_gtype (type)) != NULL)
return glade_property_class_make_gvalue_from_string (pclass, string, project);
return NULL;
}
/**
* glade_utils_string_from_value:
* @type: a #GType to convert with
* @value: the value to convert
* @project: the #GladeProject to look for formats of object names when needed
*
* Serializes #GValue into a string
* (using glade conversion routines)
*
* Returns: A newly allocated string
*/
gchar *
glade_utils_string_from_value (GType type,
const GValue *value,
GladeProject *project)
{
GladePropertyClass *pclass;
g_return_val_if_fail (type != G_TYPE_INVALID, NULL);
g_return_val_if_fail (value != NULL, NULL);
if ((pclass = pclass_from_gtype (type)) != NULL)
return glade_property_class_make_string_from_gvalue (pclass, value,
glade_project_get_format (project));
return NULL;
}
......@@ -137,6 +137,12 @@ gint glade_utils_enum_value_from_string (GType enum_type, const gch
gchar *glade_utils_enum_string_from_value (GType enum_type, gint value);
GValue *glade_utils_value_from_string (GType type,
const gchar *string,
GladeProject *project);
gchar *glade_utils_string_from_value (GType type,
const GValue *value,
GladeProject *project);
G_END_DECLS
......
......@@ -2544,6 +2544,7 @@ glade_widget_get_pack_property (GladeWidget *widget, const gchar *id_property)
* Gets the value of @id_property in @widget
*
* Returns: whether @id_property was found or not.
*
*/
gboolean
glade_widget_property_get (GladeWidget *widget,
......
......@@ -23,7 +23,8 @@ libgladegtk_la_CFLAGS = \
$(PLUGINS_WARN_CFLAGS) \
$(AM_CFLAGS)
libgladegtk_la_SOURCES = glade-gtk.c glade-accels.c glade-attributes.c glade-convert.c fixed-bg.xpm
libgladegtk_la_SOURCES = glade-gtk.c glade-accels.c glade-attributes.c glade-convert.c fixed-bg.xpm \
glade-column-types.c glade-column-types.h
libgladegtk_la_LDFLAGS = -module -avoid-version $(AM_LDFLAGS)
libgladegtk_la_LIBADD = $(libgladeui) $(GTK_LIBS)
......
......@@ -850,9 +850,6 @@ sync_object (GladeEPropAttrs *eprop_attrs,
COLUMN_EDIT_TYPE, &edit_type,
COLUMN_TEXT, &strval,
-1);
g_print ("Feeding attributes with a type %d value '%s'\n", type, strval);
gattr = glade_gtk_attribute_from_string (type, (edit_type == EDIT_TOGGLE) ? "" : strval);
strval = (g_free (strval), NULL);
......@@ -917,8 +914,6 @@ value_text_editing_started (GtkCellRenderer *renderer,
new_text = gdk_color_to_string (&color);
g_print ("Setting color in store to '%s'\n", new_text);
gtk_tree_store_set (GTK_TREE_STORE (eprop_attrs->model), &iter,
COLUMN_TEXT, new_text,
COLUMN_NAME_WEIGHT, PANGO_WEIGHT_BOLD,
......
......@@ -28,6 +28,7 @@
#include "fixed-bg.xpm"
#include "glade-accels.h"
#include "glade-attributes.h"
#include "glade-column-types.h"
#include <gladeui/glade-editor-property.h>
#include <gladeui/glade-base-editor.h>
......@@ -856,7 +857,6 @@ glade_gtk_widget_write_atk_props (GladeWidget *widget,
fmt = glade_project_get_format (widget->project);
atk_node = glade_xml_node_new (context, GLADE_TAG_A11Y_A11Y);
glade_xml_node_append_child (node, atk_node);
if (fmt == GLADE_PROJECT_FORMAT_LIBGLADE)
glade_gtk_widget_write_atk_properties_libglade (widget, context, atk_node);
......@@ -865,10 +865,9 @@ glade_gtk_widget_write_atk_props (GladeWidget *widget,
glade_gtk_widget_write_atk_actions (widget, context, atk_node);
if (!glade_xml_node_get_children (atk_node))
{
glade_xml_node_remove (atk_node);
glade_xml_node_delete (atk_node);
}
else
glade_xml_node_append_child (node, atk_node);
if (fmt == GLADE_PROJECT_FORMAT_GTKBUILDER)
glade_gtk_widget_write_atk_properties_gtkbuilder (widget, context, node);
......@@ -7247,8 +7246,6 @@ glade_gtk_label_set_attributes (GObject *object, const GValue *value)
GdkColor *color;
GList *list;
g_print ("Setting attributes !!!\n");
for (list = g_value_get_boxed (value); list; list = list->next)
{
gattr = list->data;
......@@ -7492,15 +7489,14 @@ glade_gtk_label_write_widget (GladeWidgetAdaptor *adaptor,
GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node);
attrs_node = glade_xml_node_new (context, GLADE_TAG_ATTRIBUTES);
glade_xml_node_append_child (node, attrs_node);
glade_gtk_write_attributes (widget, context, attrs_node);
if (!glade_xml_node_get_children (attrs_node))
{
glade_xml_node_remove (attrs_node);
glade_xml_node_delete (attrs_node);
}
else
glade_xml_node_append_child (node, attrs_node);
}
gchar *
......@@ -7757,34 +7753,6 @@ glade_gtk_spin_button_set_property (GladeWidgetAdaptor *adaptor,
id, value);
}
/* ----------------------------- GtkTreeView ------------------------------ */
void
glade_gtk_tree_view_post_create (GladeWidgetAdaptor *adaptor,
GObject *object,
GladeCreateReason reason)
{
GtkWidget *tree_view = GTK_WIDGET (object);
GtkTreeStore *store;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (store));
g_object_unref (G_OBJECT (store));
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes
("Column 1", renderer, "text", 0, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes
("Column 2", renderer, "text", 1, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
}
/* ----------------------------- GtkCombo ------------------------------ */
void
glade_gtk_combo_post_create (GladeWidgetAdaptor *adaptor,
......@@ -8539,7 +8507,6 @@ glade_gtk_size_group_write_widgets (GladeWidget *widget,
GladeWidget *awidget;
widgets_node = glade_xml_node_new (context, GLADE_TAG_SIZEGROUP_WIDGETS);
glade_xml_node_append_child (node, widgets_node);
if (glade_widget_property_get (widget, "widgets", &widgets))
{
......@@ -8553,10 +8520,10 @@ glade_gtk_size_group_write_widgets (GladeWidget *widget,
}
if (!glade_xml_node_get_children (widgets_node))
{
glade_xml_node_remove (widgets_node);
glade_xml_node_delete (widgets_node);
}
else
glade_xml_node_append_child (node, widgets_node);
}
......@@ -8609,3 +8576,373 @@ glade_gtk_size_group_set_property (GladeWidgetAdaptor *adaptor,
GWA_GET_CLASS (G_TYPE_OBJECT)->set_property (adaptor, object,
property_name, value);
}
/*--------------------------- GtkListStore ---------------------------------*/
#define GLADE_TAG_COLUMNS "columns"
#define GLADE_TAG_COLUMN "column"
#define GLADE_TAG_TYPE "type"
#define GLADE_TAG_ROW "row"
#define GLADE_TAG_DATA "data"
#define GLADE_TAG_COL "col"
static void
glade_gtk_store_set_columns (GObject *object,
const GValue *value)
{
GList *l = g_value_get_boxed (value);
gint i, n = g_list_length (l);
GType *types = g_new (GType, n);
for (i = 0; l; l = g_list_next (l), i++)
{
GladeColumnType *data = l->data;
types[i] = data->type;
}
if (GTK_IS_LIST_STORE (object))
gtk_list_store_set_column_types (GTK_LIST_STORE (object), n, types);
else
gtk_tree_store_set_column_types (GTK_TREE_STORE (object), n, types);
}
static void
glade_gtk_store_set_data (GObject *object,
const GValue *value)
{
GladeWidget *gwidget = glade_widget_get_from_gobject (object);
GList *columns = NULL, *list;
GladeColumnType *column;
gchar **split, **subsplit;
gint i, j;
GtkTreeIter row_iter;
if (GTK_IS_LIST_STORE (object))
gtk_list_store_clear (GTK_LIST_STORE (object));
else
gtk_tree_store_clear (GTK_TREE_STORE (object));
glade_widget_property_get (gwidget, "columns", &columns);
/* Nothing to enter without columns defined */
if (!columns) return;
split = g_value_get_boxed (value);
for (i = 0; split && split[i]; i++)
{
if ((subsplit = g_strsplit (split[i], " ", 0)) != NULL)
{
if (GTK_IS_LIST_STORE (object))
gtk_list_store_append (GTK_LIST_STORE (object), &row_iter);
else
/* (for now no child data... ) */
gtk_tree_store_append (GTK_TREE_STORE (object), &row_iter, NULL);
for (j = 0; subsplit[j]; j++)
{
if ((list = g_list_nth (columns, j)) != NULL)
{
GValue *row_value;
column = list->data;
if ((row_value =
glade_utils_value_from_string (column->type, subsplit[j], NULL)) != NULL)
{
if (GTK_IS_LIST_STORE (object))
gtk_list_store_set_value (GTK_LIST_STORE (object),
&row_iter,
j, row_value);
else
gtk_tree_store_set_value (GTK_TREE_STORE (object),
&row_iter,
j, row_value);
}
g_value_unset (row_value);
g_free (row_value);
}
}
g_strfreev (subsplit);
}
}
}
void
glade_gtk_store_set_property (GladeWidgetAdaptor *adaptor,
GObject *object,
const gchar *property_name,
const GValue *value)
{
if (strcmp (property_name, "columns") == 0)
{
glade_gtk_store_set_columns (object, value);
}
else if (strcmp (property_name, "data") == 0)
{
glade_gtk_store_set_data (object, value);
}
else
/* Chain Up */
GWA_GET_CLASS (G_TYPE_OBJECT)->set_property (adaptor,
object,
property_name,
value);
}
GladeEditorProperty *
glade_gtk_store_create_eprop (GladeWidgetAdaptor *adaptor,
GladePropertyClass *klass,
gboolean use_command)
{
GladeEditorProperty *eprop;
/* chain up.. */
if (GLADE_IS_PARAM_SPEC_COLUMN_TYPES (klass->pspec))
eprop = g_object_new (GLADE_TYPE_EPROP_COLUMN_TYPES,
"property-class", klass,
"use-command", use_command,
NULL);
else
eprop = GWA_GET_CLASS
(G_TYPE_OBJECT)->create_eprop (adaptor,
klass,
use_command);
return eprop;
}
gchar *
glade_gtk_store_from_value (GladeWidgetAdaptor *adaptor,
GladePropertyClass *klass,
const GValue *value,
GladeProjectFormat fmt)
{
if (GLADE_IS_PARAM_SPEC_COLUMN_TYPES (klass->pspec))
{
GString *val = g_string_new ("");
gchar *retval;
GList *l;
for (l = g_value_get_boxed (value); l; l = g_list_next (l))
{
GladeColumnType *data = l->data;
g_string_append_printf (val, (g_list_next (l)) ? "%s|" : "%s",
g_type_name (data->type));
}
retval = val->str;
g_string_free (val, FALSE);
return retval;
}
else
return GWA_GET_CLASS
(G_TYPE_OBJECT)->string_from_value (adaptor,
klass,
value,
fmt);
}
static void
glade_gtk_store_write_columns (GladeWidget *widget,
GladeXmlContext *context,
GladeXmlNode *node)
{
GladeXmlNode *columns_node;
GladeProperty *prop;
GList *l;
prop = glade_widget_get_property (widget, "columns");
columns_node = glade_xml_node_new (context, GLADE_TAG_COLUMNS);
for (l = g_value_get_boxed (prop->value); l; l = g_list_next (l))
{
GladeColumnType *data = l->data;
GladeXmlNode *column_node;
column_node = glade_xml_node_new (context, GLADE_TAG_COLUMN);
glade_xml_node_append_child (columns_node, column_node);
glade_xml_node_set_property_string (column_node, GLADE_TAG_TYPE,
g_type_name (data->type));
}
if (!glade_xml_node_get_children (columns_node))
glade_xml_node_delete (columns_node);
else
glade_xml_node_append_child (node, columns_node);
}
static void
glade_gtk_store_write_data (GladeWidget *widget,
GladeXmlContext *context,
GladeXmlNode *node)
{
GladeXmlNode *data_node, *col_node, *row_node;
GList *columns = NULL;
gchar **split = NULL, **subsplit, *column_number;
gint i, j;
glade_widget_property_get (widget, "data", &split);
glade_widget_property_get (widget, "columns", &columns);
/* XXX log errors about data not fitting columns here when
* loggin is available
*/
if (!split || !columns)
return;
data_node = glade_xml_node_new (context, GLADE_TAG_DATA);
for (i = 0; split[i]; i++)
{
row_node = glade_xml_node_new (context, GLADE_TAG_ROW);
glade_xml_node_append_child (data_node, row_node);
if ((subsplit = g_strsplit (split[i], " ", 0)) != NULL)
{
for (j = 0; subsplit[j]; j++)
{
/* XXX Log error: data col j exceeds columns on row i */
if (!g_list_nth (columns, j))
break;
column_number = g_strdup_printf ("%d", j);
col_node = glade_xml_node_new (context, GLADE_TAG_COL);
glade_xml_node_append_child (row_node, col_node);
glade_xml_node_set_property_string (col_node, GLADE_TAG_ID,
column_number);
glade_xml_set_content (col_node, subsplit[j]);
g_free (column_number);
}
g_strfreev (subsplit);
}
}
if (!glade_xml_node_get_children (data_node))