]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktreestore.c
Updated Bulgarian translation
[~andy/gtk] / gtk / gtktreestore.c
index f13c5ec74a19accf09f3271a548a517b39ecc983..7047dfb861cd712e1c3dc723fce6262a26d5d174 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
-#include <config.h>
+#include "config.h"
 #include <string.h>
 #include <gobject/gvaluecollector.h>
 #include "gtktreemodel.h"
 #include "gtktreestore.h"
 #include "gtktreedatalist.h"
 #include "gtktreednd.h"
+#include "gtkbuildable.h"
 #include "gtkintl.h"
-#include "gtkalias.h"
+
 
 #define G_NODE(node) ((GNode *)node)
 #define GTK_TREE_STORE_IS_SORTED(tree) (((GtkTreeStore*)(tree))->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
@@ -35,6 +36,7 @@ static void         gtk_tree_store_tree_model_init (GtkTreeModelIface *iface);
 static void         gtk_tree_store_drag_source_init(GtkTreeDragSourceIface *iface);
 static void         gtk_tree_store_drag_dest_init  (GtkTreeDragDestIface   *iface);
 static void         gtk_tree_store_sortable_init   (GtkTreeSortableIface   *iface);
+static void         gtk_tree_store_buildable_init  (GtkBuildableIface      *iface);
 static void         gtk_tree_store_finalize        (GObject           *object);
 static GtkTreeModelFlags gtk_tree_store_get_flags  (GtkTreeModel      *tree_model);
 static gint         gtk_tree_store_get_n_columns   (GtkTreeModel      *tree_model);
@@ -108,13 +110,28 @@ static void     gtk_tree_store_set_sort_func           (GtkTreeSortable        *
                                                        gint                    sort_column_id,
                                                        GtkTreeIterCompareFunc  func,
                                                        gpointer                data,
-                                                       GtkDestroyNotify        destroy);
+                                                       GDestroyNotify          destroy);
 static void     gtk_tree_store_set_default_sort_func   (GtkTreeSortable        *sortable,
                                                        GtkTreeIterCompareFunc  func,
                                                        gpointer                data,
-                                                       GtkDestroyNotify        destroy);
+                                                       GDestroyNotify          destroy);
 static gboolean gtk_tree_store_has_default_sort_func   (GtkTreeSortable        *sortable);
 
+
+/* buildable */
+
+static gboolean gtk_tree_store_buildable_custom_tag_start (GtkBuildable  *buildable,
+                                                          GtkBuilder    *builder,
+                                                          GObject       *child,
+                                                          const gchar   *tagname,
+                                                          GMarkupParser *parser,
+                                                          gpointer      *data);
+static void     gtk_tree_store_buildable_custom_finished (GtkBuildable          *buildable,
+                                                         GtkBuilder     *builder,
+                                                         GObject        *child,
+                                                         const gchar    *tagname,
+                                                         gpointer        user_data);
+
 static void     validate_gnode                         (GNode *node);
 
 static void     gtk_tree_store_move                    (GtkTreeStore           *tree_store,
@@ -142,7 +159,9 @@ G_DEFINE_TYPE_WITH_CODE (GtkTreeStore, gtk_tree_store, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
                                                gtk_tree_store_drag_dest_init)
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
-                                               gtk_tree_store_sortable_init))
+                                               gtk_tree_store_sortable_init)
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+                                               gtk_tree_store_buildable_init))
 
 static void
 gtk_tree_store_class_init (GtkTreeStoreClass *class)
@@ -196,6 +215,13 @@ gtk_tree_store_sortable_init (GtkTreeSortableIface *iface)
   iface->has_default_sort_func = gtk_tree_store_has_default_sort_func;
 }
 
+void
+gtk_tree_store_buildable_init (GtkBuildableIface *iface)
+{
+  iface->custom_tag_start = gtk_tree_store_buildable_custom_tag_start;
+  iface->custom_finished = gtk_tree_store_buildable_custom_finished;
+}
+
 static void
 gtk_tree_store_init (GtkTreeStore *tree_store)
 {
@@ -209,7 +235,7 @@ gtk_tree_store_init (GtkTreeStore *tree_store)
   while (tree_store->stamp == 0);
 
   tree_store->sort_list = NULL;
-  tree_store->sort_column_id = -2;
+  tree_store->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
   tree_store->columns_dirty = FALSE;
 }
 
@@ -248,9 +274,9 @@ gtk_tree_store_new (gint n_columns,
       GType type = va_arg (args, GType);
       if (! _gtk_tree_data_list_check_type (type))
        {
-         g_warning ("%s: Invalid type %s passed to gtk_tree_store_new_with_types\n",
-                    G_STRLOC, g_type_name (type));
+         g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
          g_object_unref (retval);
+          va_end (args);
          return NULL;
        }
       gtk_tree_store_set_column_type (retval, i, type);
@@ -262,7 +288,7 @@ gtk_tree_store_new (gint n_columns,
 /**
  * gtk_tree_store_newv:
  * @n_columns: number of columns in the tree store
- * @types: an array of #GType types for the columns, from first to last
+ * @types: (array length=n_columns): an array of #GType types for the columns, from first to last
  *
  * Non vararg creation function.  Used primarily by language bindings.
  *
@@ -284,8 +310,7 @@ gtk_tree_store_newv (gint   n_columns,
     {
       if (! _gtk_tree_data_list_check_type (types[i]))
        {
-         g_warning ("%s: Invalid type %s passed to gtk_tree_store_new_with_types\n",
-                    G_STRLOC, g_type_name (types[i]));
+         g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
          g_object_unref (retval);
          return NULL;
        }
@@ -300,7 +325,7 @@ gtk_tree_store_newv (gint   n_columns,
  * gtk_tree_store_set_column_types:
  * @tree_store: A #GtkTreeStore
  * @n_columns: Number of columns for the tree store
- * @types: An array of #GType types, one for each column
+ * @types: (array length=n_columns): An array of #GType types, one for each column
  * 
  * This function is meant primarily for #GObjects that inherit from 
  * #GtkTreeStore, and should only be used when constructing a new 
@@ -322,7 +347,7 @@ gtk_tree_store_set_column_types (GtkTreeStore *tree_store,
     {
       if (! _gtk_tree_data_list_check_type (types[i]))
        {
-         g_warning ("%s: Invalid type %s passed to gtk_tree_store_set_column_types\n", G_STRLOC, g_type_name (types[i]));
+         g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
          continue;
        }
       gtk_tree_store_set_column_type (tree_store, i, types[i]);
@@ -333,30 +358,20 @@ static void
 gtk_tree_store_set_n_columns (GtkTreeStore *tree_store,
                              gint          n_columns)
 {
-  GType *new_columns;
+  int i;
 
   if (tree_store->n_columns == n_columns)
     return;
 
-  new_columns = g_new0 (GType, n_columns);
-  if (tree_store->column_headers)
-    {
-      /* copy the old header orders over */
-      if (n_columns >= tree_store->n_columns)
-       memcpy (new_columns, tree_store->column_headers, tree_store->n_columns * sizeof (gchar *));
-      else
-       memcpy (new_columns, tree_store->column_headers, n_columns * sizeof (GType));
-
-      g_free (tree_store->column_headers);
-    }
+  tree_store->column_headers = g_renew (GType, tree_store->column_headers, n_columns);
+  for (i = tree_store->n_columns; i < n_columns; i++)
+    tree_store->column_headers[i] = G_TYPE_INVALID;
+  tree_store->n_columns = n_columns;
 
   if (tree_store->sort_list)
     _gtk_tree_data_list_header_free (tree_store->sort_list);
 
   tree_store->sort_list = _gtk_tree_data_list_header_new (n_columns, tree_store->column_headers);
-
-  tree_store->column_headers = new_columns;
-  tree_store->n_columns = n_columns;
 }
 
 /**
@@ -378,7 +393,7 @@ gtk_tree_store_set_column_type (GtkTreeStore *tree_store,
 {
   if (!_gtk_tree_data_list_check_type (type))
     {
-      g_warning ("%s: Invalid type %s passed to gtk_tree_store_new_with_types\n", G_STRLOC, g_type_name (type));
+      g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
       return;
     }
   tree_store->column_headers[column] = type;
@@ -407,7 +422,7 @@ gtk_tree_store_finalize (GObject *object)
 
   if (tree_store->default_sort_destroy)
     {
-      GtkDestroyNotify d = tree_store->default_sort_destroy;
+      GDestroyNotify d = tree_store->default_sort_destroy;
 
       tree_store->default_sort_destroy = NULL;
       d (tree_store->default_sort_data);
@@ -595,7 +610,10 @@ gtk_tree_store_iter_next (GtkTreeModel  *tree_model,
       return TRUE;
     }
   else
-    return FALSE;
+    {
+      iter->stamp = 0;
+      return FALSE;
+    }
 }
 
 static gboolean
@@ -621,7 +639,10 @@ gtk_tree_store_iter_children (GtkTreeModel *tree_model,
       return TRUE;
     }
   else
-    return FALSE;
+    {
+      iter->stamp = 0;
+      return FALSE;
+    }
 }
 
 static gboolean
@@ -683,7 +704,10 @@ gtk_tree_store_iter_nth_child (GtkTreeModel *tree_model,
       return TRUE;
     }
   else
-    return FALSE;
+    {
+      iter->stamp = 0;
+      return FALSE;
+    }
 }
 
 static gboolean
@@ -708,7 +732,10 @@ gtk_tree_store_iter_parent (GtkTreeModel *tree_model,
       return TRUE;
     }
   else
-    return FALSE;
+    {
+      iter->stamp = 0;
+      return FALSE;
+    }
 }
 
 
@@ -865,6 +892,34 @@ gtk_tree_store_get_compare_func (GtkTreeStore *tree_store)
   return func;
 }
 
+static void
+gtk_tree_store_set_vector_internal (GtkTreeStore *tree_store,
+                                   GtkTreeIter  *iter,
+                                   gboolean     *emit_signal,
+                                   gboolean     *maybe_need_sort,
+                                   gint         *columns,
+                                   GValue       *values,
+                                   gint          n_values)
+{
+  gint i;
+  GtkTreeIterCompareFunc func = NULL;
+
+  func = gtk_tree_store_get_compare_func (tree_store);
+  if (func != _gtk_tree_data_list_compare_func)
+    *maybe_need_sort = TRUE;
+
+  for (i = 0; i < n_values; i++)
+    {
+      *emit_signal = gtk_tree_store_real_set_value (tree_store, iter,
+                                                   columns[i], &values[i],
+                                                   FALSE) || *emit_signal;
+
+      if (func == _gtk_tree_data_list_compare_func &&
+         columns[i] == tree_store->sort_column_id)
+       *maybe_need_sort = TRUE;
+    }
+}
+
 static void
 gtk_tree_store_set_valist_internal (GtkTreeStore *tree_store,
                                     GtkTreeIter  *iter,
@@ -886,7 +941,7 @@ gtk_tree_store_set_valist_internal (GtkTreeStore *tree_store,
       GValue value = { 0, };
       gchar *error = NULL;
 
-      if (column >= tree_store->n_columns)
+      if (column < 0 || column >= tree_store->n_columns)
        {
          g_warning ("%s: Invalid column number %d added to iter (remember to end your list of columns with a -1)", G_STRLOC, column);
          break;
@@ -921,6 +976,52 @@ gtk_tree_store_set_valist_internal (GtkTreeStore *tree_store,
     }
 }
 
+/**
+ * gtk_tree_store_set_valuesv:
+ * @tree_store: A #GtkTreeStore
+ * @iter: A valid #GtkTreeIter for the row being modified
+ * @columns: (array length=n_values): an array of column numbers
+ * @values: (array length=n_values): an array of GValues
+ * @n_values: the length of the @columns and @values arrays
+ *
+ * A variant of gtk_tree_store_set_valist() which takes
+ * the columns and values as two arrays, instead of varargs.  This
+ * function is mainly intended for language bindings or in case
+ * the number of columns to change is not known until run-time.
+ *
+ * Since: 2.12
+ **/
+void
+gtk_tree_store_set_valuesv (GtkTreeStore *tree_store,
+                           GtkTreeIter  *iter,
+                           gint         *columns,
+                           GValue       *values,
+                           gint          n_values)
+{
+  gboolean emit_signal = FALSE;
+  gboolean maybe_need_sort = FALSE;
+
+  g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
+  g_return_if_fail (VALID_ITER (iter, tree_store));
+
+  gtk_tree_store_set_vector_internal (tree_store, iter,
+                                     &emit_signal,
+                                     &maybe_need_sort,
+                                     columns, values, n_values);
+
+  if (maybe_need_sort && GTK_TREE_STORE_IS_SORTED (tree_store))
+    gtk_tree_store_sort_iter_changed (tree_store, iter, tree_store->sort_column_id, TRUE);
+
+  if (emit_signal)
+    {
+      GtkTreePath *path;
+
+      path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+      gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter);
+      gtk_tree_path_free (path);
+    }
+}
+
 /**
  * gtk_tree_store_set_valist:
  * @tree_store: A #GtkTreeStore
@@ -972,6 +1073,9 @@ gtk_tree_store_set_valist (GtkTreeStore *tree_store,
  * The list is terminated by a -1. For example, to set column 0 with type
  * %G_TYPE_STRING to "Foo", you would write 
  * <literal>gtk_tree_store_set (store, iter, 0, "Foo", -1)</literal>.
+ *
+ * The value will be referenced by the store if it is a %G_TYPE_OBJECT, and it
+ * will be copied if it is a %G_TYPE_STRING or %G_TYPE_BOXED.
  **/
 void
 gtk_tree_store_set (GtkTreeStore *tree_store,
@@ -1055,8 +1159,8 @@ gtk_tree_store_remove (GtkTreeStore *tree_store,
 /**
  * gtk_tree_store_insert:
  * @tree_store: A #GtkTreeStore
- * @iter: An unset #GtkTreeIter to set to the new row
- * @parent: A valid #GtkTreeIter, or %NULL
+ * @iter: (out): An unset #GtkTreeIter to set to the new row
+ * @parent: (allow-none): A valid #GtkTreeIter, or %NULL
  * @position: position to insert the new row
  *
  * Creates a new row at @position.  If parent is non-%NULL, then the row will be
@@ -1116,9 +1220,9 @@ gtk_tree_store_insert (GtkTreeStore *tree_store,
 /**
  * gtk_tree_store_insert_before:
  * @tree_store: A #GtkTreeStore
- * @iter: An unset #GtkTreeIter to set to the new row
- * @parent: A valid #GtkTreeIter, or %NULL
- * @sibling: A valid #GtkTreeIter, or %NULL
+ * @iter: (out): An unset #GtkTreeIter to set to the new row
+ * @parent: (allow-none): A valid #GtkTreeIter, or %NULL
+ * @sibling: (allow-none): A valid #GtkTreeIter, or %NULL
  *
  * Inserts a new row before @sibling.  If @sibling is %NULL, then the row will
  * be appended to @parent 's children.  If @parent and @sibling are %NULL, then
@@ -1196,9 +1300,9 @@ gtk_tree_store_insert_before (GtkTreeStore *tree_store,
 /**
  * gtk_tree_store_insert_after:
  * @tree_store: A #GtkTreeStore
- * @iter: An unset #GtkTreeIter to set to the new row
- * @parent: A valid #GtkTreeIter, or %NULL
- * @sibling: A valid #GtkTreeIter, or %NULL
+ * @iter: (out): An unset #GtkTreeIter to set to the new row
+ * @parent: (allow-none): A valid #GtkTreeIter, or %NULL
+ * @sibling: (allow-none): A valid #GtkTreeIter, or %NULL
  *
  * Inserts a new row after @sibling.  If @sibling is %NULL, then the row will be
  * prepended to @parent 's children.  If @parent and @sibling are %NULL, then
@@ -1277,8 +1381,8 @@ gtk_tree_store_insert_after (GtkTreeStore *tree_store,
 /**
  * gtk_tree_store_insert_with_values:
  * @tree_store: A #GtkTreeStore
- * @iter: An unset #GtkTreeIter to set the new row, or %NULL.
- * @parent: A valid #GtkTreeIter, or %NULL
+ * @iter: (out) (allow-none): An unset #GtkTreeIter to set the new row, or %NULL.
+ * @parent: (allow-none): A valid #GtkTreeIter, or %NULL
  * @position: position to insert the new row
  * @Varargs: pairs of column number and value, terminated with -1
  *
@@ -1290,10 +1394,10 @@ gtk_tree_store_insert_after (GtkTreeStore *tree_store,
  * Calling
  * <literal>gtk_tree_store_insert_with_values (tree_store, iter, position, ...)</literal>
  * has the same effect as calling
- * <informalexample><programlisting>
+ * |[
  * gtk_tree_store_insert (tree_store, iter, position);
  * gtk_tree_store_set (tree_store, iter, ...);
- * </programlisting></informalexample>
+ * ]|
  * with the different that the former will only emit a row_inserted signal,
  * while the latter will emit row_inserted, row_changed and if the tree store
  * is sorted, rows_reordered.  Since emitting the rows_reordered signal
@@ -1368,8 +1472,8 @@ gtk_tree_store_insert_with_values (GtkTreeStore *tree_store,
 /**
  * gtk_tree_store_insert_with_valuesv:
  * @tree_store: A #GtkTreeStore
- * @iter: An unset #GtkTreeIter to set the new row, or %NULL.
- * @parent: A valid #GtkTreeIter, or %NULL
+ * @iter: (out) (allow-none): An unset #GtkTreeIter to set the new row, or %NULL.
+ * @parent: (allow-none): A valid #GtkTreeIter, or %NULL
  * @position: position to insert the new row
  * @columns: an array of column numbers
  * @values: an array of GValues
@@ -1396,8 +1500,6 @@ gtk_tree_store_insert_with_valuesv (GtkTreeStore *tree_store,
   GtkTreeIter tmp_iter;
   gboolean changed = FALSE;
   gboolean maybe_need_sort = FALSE;
-  GtkTreeIterCompareFunc func = NULL;
-  gint i;
 
   g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
 
@@ -1420,20 +1522,9 @@ gtk_tree_store_insert_with_valuesv (GtkTreeStore *tree_store,
   iter->user_data = new_node;
   g_node_insert (parent_node, position, new_node);
 
-  func = gtk_tree_store_get_compare_func (tree_store);
-  if (func != _gtk_tree_data_list_compare_func)
-    maybe_need_sort = TRUE;
-
-  for (i = 0; i < n_values; i++)
-    {
-      changed = gtk_tree_store_real_set_value (tree_store, iter,
-                                              columns[i], &values[i],
-                                              FALSE) || changed;
-
-      if (func == _gtk_tree_data_list_compare_func &&
-         columns[i] == tree_store->sort_column_id)
-       maybe_need_sort = TRUE;
-    }
+  gtk_tree_store_set_vector_internal (tree_store, iter,
+                                     &changed, &maybe_need_sort,
+                                     columns, values, n_values);
 
   if (maybe_need_sort && GTK_TREE_STORE_IS_SORTED (tree_store))
     gtk_tree_store_sort_iter_changed (tree_store, iter, tree_store->sort_column_id, FALSE);
@@ -1458,8 +1549,8 @@ gtk_tree_store_insert_with_valuesv (GtkTreeStore *tree_store,
 /**
  * gtk_tree_store_prepend:
  * @tree_store: A #GtkTreeStore
- * @iter: An unset #GtkTreeIter to set to the prepended row
- * @parent: A valid #GtkTreeIter, or %NULL
+ * @iter: (out): An unset #GtkTreeIter to set to the prepended row
+ * @parent: (allow-none): A valid #GtkTreeIter, or %NULL
  * 
  * Prepends a new row to @tree_store.  If @parent is non-%NULL, then it will prepend
  * the new row before the first child of @parent, otherwise it will prepend a row
@@ -1516,8 +1607,8 @@ gtk_tree_store_prepend (GtkTreeStore *tree_store,
 /**
  * gtk_tree_store_append:
  * @tree_store: A #GtkTreeStore
- * @iter: An unset #GtkTreeIter to set to the appended row
- * @parent: A valid #GtkTreeIter, or %NULL
+ * @iter: (out): An unset #GtkTreeIter to set to the appended row
+ * @parent: (allow-none): A valid #GtkTreeIter, or %NULL
  * 
  * Appends a new row to @tree_store.  If @parent is non-%NULL, then it will append the
  * new row after the last child of @parent, otherwise it will append a row to
@@ -2601,7 +2692,7 @@ free_paths_and_out:
  * gtk_tree_store_move_before:
  * @tree_store: A #GtkTreeStore.
  * @iter: A #GtkTreeIter.
- * @position: A #GtkTreeIter or %NULL.
+ * @position: (allow-none): A #GtkTreeIter or %NULL.
  *
  * Moves @iter in @tree_store to the position before @position. @iter and
  * @position should be in the same level. Note that this function only
@@ -2622,7 +2713,7 @@ gtk_tree_store_move_before (GtkTreeStore *tree_store,
  * gtk_tree_store_move_after:
  * @tree_store: A #GtkTreeStore.
  * @iter: A #GtkTreeIter.
- * @position: A #GtkTreeIter.
+ * @position: (allow-none): A #GtkTreeIter.
  *
  * Moves @iter in @tree_store to the position after @position. @iter and
  * @position should be in the same level. Note that this function only
@@ -3054,7 +3145,7 @@ gtk_tree_store_set_sort_func (GtkTreeSortable        *sortable,
                              gint                    sort_column_id,
                              GtkTreeIterCompareFunc  func,
                              gpointer                data,
-                             GtkDestroyNotify        destroy)
+                             GDestroyNotify          destroy)
 {
   GtkTreeStore *tree_store = (GtkTreeStore *) sortable;
 
@@ -3070,13 +3161,13 @@ static void
 gtk_tree_store_set_default_sort_func (GtkTreeSortable        *sortable,
                                      GtkTreeIterCompareFunc  func,
                                      gpointer                data,
-                                     GtkDestroyNotify        destroy)
+                                     GDestroyNotify          destroy)
 {
   GtkTreeStore *tree_store = (GtkTreeStore *) sortable;
 
   if (tree_store->default_sort_destroy)
     {
-      GtkDestroyNotify d = tree_store->default_sort_destroy;
+      GDestroyNotify d = tree_store->default_sort_destroy;
 
       tree_store->default_sort_destroy = NULL;
       d (tree_store->default_sort_data);
@@ -3114,5 +3205,128 @@ validate_gnode (GNode* node)
     }
 }
 
-#define __GTK_TREE_STORE_C__
-#include "gtkaliasdef.c"
+/* GtkBuildable custom tag implementation
+ *
+ * <columns>
+ *   <column type="..."/>
+ *   <column type="..."/>
+ * </columns>
+ */
+typedef struct {
+  GtkBuilder *builder;
+  GObject *object;
+  GSList *items;
+} GSListSubParserData;
+
+static void
+tree_model_start_element (GMarkupParseContext *context,
+                         const gchar         *element_name,
+                         const gchar        **names,
+                         const gchar        **values,
+                         gpointer            user_data,
+                         GError            **error)
+{
+  guint i;
+  GSListSubParserData *data = (GSListSubParserData*)user_data;
+
+  for (i = 0; names[i]; i++)
+    {
+      if (strcmp (names[i], "type") == 0)
+       data->items = g_slist_prepend (data->items, g_strdup (values[i]));
+    }
+}
+
+static void
+tree_model_end_element (GMarkupParseContext *context,
+                       const gchar         *element_name,
+                       gpointer             user_data,
+                       GError             **error)
+{
+  GSListSubParserData *data = (GSListSubParserData*)user_data;
+
+  g_assert(data->builder);
+
+  if (strcmp (element_name, "columns") == 0)
+    {
+      GSList *l;
+      GType *types;
+      int i;
+      GType type;
+
+      data = (GSListSubParserData*)user_data;
+      data->items = g_slist_reverse (data->items);
+      types = g_new0 (GType, g_slist_length (data->items));
+
+      for (l = data->items, i = 0; l; l = l->next, i++)
+        {
+          type = gtk_builder_get_type_from_name (data->builder, l->data);
+          if (type == G_TYPE_INVALID)
+            {
+              g_warning ("Unknown type %s specified in treemodel %s",
+                         (const gchar*)l->data,
+                         gtk_buildable_get_name (GTK_BUILDABLE (data->object)));
+              continue;
+            }
+          types[i] = type;
+
+          g_free (l->data);
+        }
+
+      gtk_tree_store_set_column_types (GTK_TREE_STORE (data->object), i, types);
+
+      g_free (types);
+    }
+}
+
+static const GMarkupParser tree_model_parser =
+  {
+    tree_model_start_element,
+    tree_model_end_element
+  };
+
+
+static gboolean
+gtk_tree_store_buildable_custom_tag_start (GtkBuildable  *buildable,
+                                          GtkBuilder    *builder,
+                                          GObject       *child,
+                                          const gchar   *tagname,
+                                          GMarkupParser *parser,
+                                          gpointer      *data)
+{
+  GSListSubParserData *parser_data;
+
+  if (child)
+    return FALSE;
+
+  if (strcmp (tagname, "columns") == 0)
+    {
+      parser_data = g_slice_new0 (GSListSubParserData);
+      parser_data->builder = builder;
+      parser_data->items = NULL;
+      parser_data->object = G_OBJECT (buildable);
+
+      *parser = tree_model_parser;
+      *data = parser_data;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+gtk_tree_store_buildable_custom_finished (GtkBuildable *buildable,
+                                         GtkBuilder   *builder,
+                                         GObject      *child,
+                                         const gchar  *tagname,
+                                         gpointer      user_data)
+{
+  GSListSubParserData *data;
+
+  if (strcmp (tagname, "columns"))
+    return;
+
+  data = (GSListSubParserData*)user_data;
+
+  g_slist_free (data->items);
+  g_slice_free (GSListSubParserData, data);
+}