X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkliststore.c;h=467bac0f15dc4da7b0a98336a01fe22f63952e7c;hb=45ad8a06ad511ad95a74172172b9fe459bc666ad;hp=84175b7c3e956513704f87c8dd1dc969a558822b;hpb=f8794ccccadc91bd37a4fe19577fc789cf0d0107;p=~andy%2Fgtk diff --git a/gtk/gtkliststore.c b/gtk/gtkliststore.c index 84175b7c3..467bac0f1 100644 --- a/gtk/gtkliststore.c +++ b/gtk/gtkliststore.c @@ -12,29 +12,204 @@ * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ -#include +#include "config.h" +#include +#include #include #include #include "gtktreemodel.h" #include "gtkliststore.h" #include "gtktreedatalist.h" #include "gtktreednd.h" -#include "gtksequence.h" #include "gtkintl.h" -#include "gtkalias.h" +#include "gtkbuildable.h" +#include "gtkbuilderprivate.h" -#define GTK_LIST_STORE_IS_SORTED(list) (((GtkListStore*)(list))->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) -#define VALID_ITER(iter, list_store) ((iter)!= NULL && (iter)->user_data != NULL && list_store->stamp == (iter)->stamp && !_gtk_sequence_ptr_is_end ((iter)->user_data) && _gtk_sequence_ptr_get_sequence ((iter)->user_data) == list_store->seq) +/** + * SECTION:gtkliststore + * @Short_description: A list-like data structure that can be used with the GtkTreeView + * @Title: GtkListStore + * @See_also:#GtkTreeModel, #GtkTreeStore + * + * The #GtkListStore object is a list model for use with a #GtkTreeView + * widget. It implements the #GtkTreeModel interface, and consequentialy, + * can use all of the methods available there. It also implements the + * #GtkTreeSortable interface so it can be sorted by the view. + * Finally, it also implements the tree drag and + * drop interfaces. + * + * The #GtkListStore can accept most GObject types as a column type, though + * it can't accept all custom types. Internally, it will keep a copy of + * data passed in (such as a string or a boxed pointer). Columns that + * accept #GObjects are handled a little differently. The + * #GtkListStore will keep a reference to the object instead of copying the + * value. As a result, if the object is modified, it is up to the + * application writer to call gtk_tree_model_row_changed() to emit the + * #GtkTreeModel::row_changed signal. This most commonly affects lists with + * #GdkPixbufs stored. + * + * + * Creating a simple list store. + * + * enum { + * COLUMN_STRING, + * COLUMN_INT, + * COLUMN_BOOLEAN, + * N_COLUMNS + * }; + * + * { + * GtkListStore *list_store; + * GtkTreePath *path; + * GtkTreeIter iter; + * gint i; + * + * list_store = gtk_list_store_new (N_COLUMNS, + * G_TYPE_STRING, + * G_TYPE_INT, + * G_TYPE_BOOLEAN); + * + * for (i = 0; i < 10; i++) + * { + * gchar *some_data; + * + * some_data = get_some_data (i); + * + * // Add a new row to the model + * gtk_list_store_append (list_store, &iter); + * gtk_list_store_set (list_store, &iter, + * COLUMN_STRING, some_data, + * COLUMN_INT, i, + * COLUMN_BOOLEAN, FALSE, + * -1); + * + * /* As the store will keep a copy of the string internally, we + * * free some_data. + * */ + * g_free (some_data); + * } + * + * // Modify a particular row + * path = gtk_tree_path_new_from_string ("4"); + * gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), + * &iter, + * path); + * gtk_tree_path_free (path); + * gtk_list_store_set (list_store, &iter, + * COLUMN_BOOLEAN, TRUE, + * -1); + * } + * + * + * + * + * Performance Considerations + * Internally, the #GtkListStore was implemented with a linked list with a + * tail pointer prior to GTK+ 2.6. As a result, it was fast at data + * insertion and deletion, and not fast at random data access. The + * #GtkListStore sets the #GTK_TREE_MODEL_ITERS_PERSIST flag, which means + * that #GtkTreeIters can be cached while the row exists. Thus, if + * access to a particular row is needed often and your code is expected to + * run on older versions of GTK+, it is worth keeping the iter around. + * + * + * Atomic Operations + * It is important to note that only the methods + * gtk_list_store_insert_with_values() and gtk_list_store_insert_with_valuesv() + * are atomic, in the sense that the row is being appended to the store and the + * values filled in in a single operation with regard to #GtkTreeModel signaling. + * In contrast, using e.g. gtk_list_store_append() and then gtk_list_store_set() + * will first create a row, which triggers the #GtkTreeModel::row-inserted signal + * on #GtkListStore. The row, however, is still empty, and any signal handler + * connecting to #GtkTreeModel::row-inserted on this particular store should be prepared + * for the situation that the row might be empty. This is especially important + * if you are wrapping the #GtkListStore inside a #GtkTreeModelFilter and are + * using a #GtkTreeModelFilterVisibleFunc. Using any of the non-atomic operations + * to append rows to the #GtkListStore will cause the + * #GtkTreeModelFilterVisibleFunc to be visited with an empty row first; the + * function must be prepared for that. + * + * + * GtkListStore as GtkBuildable + * + * The GtkListStore implementation of the GtkBuildable interface allows + * to specify the model columns with a <columns> element that may + * contain multiple <column> elements, each specifying one model + * column. The "type" attribute specifies the data type for the column. + * + * Additionally, it is possible to specify content for the list store + * in the UI definition, with the <data> element. It can contain + * multiple <row> elements, each specifying to content for one + * row of the list model. Inside a <row>, the <col> elements + * specify the content for individual cells. + * + * Note that it is probably more common to define your models + * in the code, and one might consider it a layering violation + * to specify the content of a list store in a UI definition, + * data, not presentation, + * and common wisdom is to separate the two, as far as possible. + * + * + * + * A UI Definition fragment for a list store + * + * + * + * + * + * + * + * + * John + * Doe + * 25 + * + * + * Johan + * Dahlin + * 50 + * + * + * + * ]]> + * + * + * + */ + + +struct _GtkListStorePrivate +{ + GtkTreeIterCompareFunc default_sort_func; + + GDestroyNotify default_sort_destroy; + GList *sort_list; + GType *column_headers; + + gint stamp; + gint n_columns; + gint sort_column_id; + gint length; + + GtkSortType order; + + guint columns_dirty : 1; + + gpointer default_sort_data; + gpointer seq; /* head of the list */ +}; + +#define GTK_LIST_STORE_IS_SORTED(list) (((GtkListStore*)(list))->priv->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) static void gtk_list_store_tree_model_init (GtkTreeModelIface *iface); static void gtk_list_store_drag_source_init(GtkTreeDragSourceIface *iface); static void gtk_list_store_drag_dest_init (GtkTreeDragDestIface *iface); static void gtk_list_store_sortable_init (GtkTreeSortableIface *iface); +static void gtk_list_store_buildable_init (GtkBuildableIface *iface); static void gtk_list_store_finalize (GObject *object); static GtkTreeModelFlags gtk_list_store_get_flags (GtkTreeModel *tree_model); static gint gtk_list_store_get_n_columns (GtkTreeModel *tree_model); @@ -51,6 +226,8 @@ static void gtk_list_store_get_value (GtkTreeModel *tree_mode GValue *value); static gboolean gtk_list_store_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter); +static gboolean gtk_list_store_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter); static gboolean gtk_list_store_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent); @@ -107,14 +284,27 @@ static void gtk_list_store_set_sort_func (GtkTreeSortable *so gint sort_column_id, GtkTreeIterCompareFunc func, gpointer data, - GtkDestroyNotify destroy); + GDestroyNotify destroy); static void gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable, GtkTreeIterCompareFunc func, gpointer data, - GtkDestroyNotify destroy); + GDestroyNotify destroy); static gboolean gtk_list_store_has_default_sort_func (GtkTreeSortable *sortable); +/* buildable */ +static gboolean gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_list_store_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); + G_DEFINE_TYPE_WITH_CODE (GtkListStore, gtk_list_store, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, gtk_list_store_tree_model_init) @@ -123,7 +313,10 @@ G_DEFINE_TYPE_WITH_CODE (GtkListStore, gtk_list_store, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, gtk_list_store_drag_dest_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, - gtk_list_store_sortable_init)) + gtk_list_store_sortable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_list_store_buildable_init)) + static void gtk_list_store_class_init (GtkListStoreClass *class) @@ -133,6 +326,8 @@ gtk_list_store_class_init (GtkListStoreClass *class) object_class = (GObjectClass*) class; object_class->finalize = gtk_list_store_finalize; + + g_type_class_add_private (class, sizeof (GtkListStorePrivate)); } static void @@ -145,6 +340,7 @@ gtk_list_store_tree_model_init (GtkTreeModelIface *iface) iface->get_path = gtk_list_store_get_path; iface->get_value = gtk_list_store_get_value; iface->iter_next = gtk_list_store_iter_next; + iface->iter_previous = gtk_list_store_iter_previous; iface->iter_children = gtk_list_store_iter_children; iface->iter_has_child = gtk_list_store_iter_has_child; iface->iter_n_children = gtk_list_store_iter_n_children; @@ -177,32 +373,57 @@ gtk_list_store_sortable_init (GtkTreeSortableIface *iface) iface->has_default_sort_func = gtk_list_store_has_default_sort_func; } +void +gtk_list_store_buildable_init (GtkBuildableIface *iface) +{ + iface->custom_tag_start = gtk_list_store_buildable_custom_tag_start; + iface->custom_tag_end = gtk_list_store_buildable_custom_tag_end; +} + static void gtk_list_store_init (GtkListStore *list_store) { - list_store->seq = _gtk_sequence_new (NULL); - list_store->sort_list = NULL; - list_store->stamp = g_random_int (); - list_store->sort_column_id = -2; - list_store->columns_dirty = FALSE; - list_store->length = 0; + GtkListStorePrivate *priv; + + list_store->priv = G_TYPE_INSTANCE_GET_PRIVATE (list_store, + GTK_TYPE_LIST_STORE, + GtkListStorePrivate); + priv = list_store->priv; + + priv->seq = g_sequence_new (NULL); + priv->sort_list = NULL; + priv->stamp = g_random_int (); + priv->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID; + priv->columns_dirty = FALSE; + priv->length = 0; +} + +static gboolean +iter_is_valid (GtkTreeIter *iter, + GtkListStore *list_store) +{ + return iter != NULL && + iter->user_data != NULL && + list_store->priv->stamp == iter->stamp && + !g_sequence_iter_is_end (iter->user_data) && + g_sequence_iter_get_sequence (iter->user_data) == list_store->priv->seq; } /** * gtk_list_store_new: * @n_columns: number of columns in the list store - * @Varargs: all #GType types for the columns, from first to last + * @...: all #GType types for the columns, from first to last * * Creates a new list store as with @n_columns columns each of the types passed - * in. Note that only types derived from standard GObject fundamental types - * are supported. + * in. Note that only types derived from standard GObject fundamental types + * are supported. * - * As an example, gtk_tree_store_new (3, G_TYPE_INT, G_TYPE_STRING, + * As an example, gtk_list_store_new (3, G_TYPE_INT, G_TYPE_STRING, * GDK_TYPE_PIXBUF); will create a new #GtkListStore with three columns, of type * int, string and #GdkPixbuf respectively. * * Return value: a new #GtkListStore - **/ + */ GtkListStore * gtk_list_store_new (gint n_columns, ...) @@ -222,11 +443,13 @@ gtk_list_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\n", G_STRLOC, g_type_name (type)); - g_object_unref (retval); - return NULL; - } + { + g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type)); + g_object_unref (retval); + va_end (args); + + return NULL; + } gtk_list_store_set_column_type (retval, i, type); } @@ -240,11 +463,12 @@ gtk_list_store_new (gint n_columns, /** * gtk_list_store_newv: * @n_columns: number of columns in the list 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. * - * Return value: a new #GtkListStore + * Return value: (transfer full): a new #GtkListStore + * Rename to: gtk_list_store_new **/ GtkListStore * gtk_list_store_newv (gint n_columns, @@ -277,8 +501,8 @@ gtk_list_store_newv (gint n_columns, * gtk_list_store_set_column_types: * @list_store: A #GtkListStore * @n_columns: Number of columns for the list store - * @types: An array length n of #GTypes - * + * @types: (array length=n_columns): An array length n of #GTypes + * * This function is meant primarily for #GObjects that inherit from #GtkListStore, * and should only be used when constructing a new #GtkListStore. It will not * function after a row has been added, or a method on the #GtkTreeModel @@ -289,13 +513,17 @@ gtk_list_store_set_column_types (GtkListStore *list_store, gint n_columns, GType *types) { + GtkListStorePrivate *priv; gint i; g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (list_store->columns_dirty == 0); + + priv = list_store->priv; + + g_return_if_fail (priv->columns_dirty == 0); gtk_list_store_set_n_columns (list_store, n_columns); - for (i = 0; i < n_columns; i++) + for (i = 0; i < n_columns; i++) { if (! _gtk_tree_data_list_check_type (types[i])) { @@ -310,30 +538,20 @@ static void gtk_list_store_set_n_columns (GtkListStore *list_store, gint n_columns) { - GType *new_columns; + GtkListStorePrivate *priv = list_store->priv; + int i; - if (list_store->n_columns == n_columns) + if (priv->n_columns == n_columns) return; - new_columns = g_new0 (GType, n_columns); - if (list_store->column_headers) - { - /* copy the old header orders over */ - if (n_columns >= list_store->n_columns) - memcpy (new_columns, list_store->column_headers, list_store->n_columns * sizeof (gchar *)); - else - memcpy (new_columns, list_store->column_headers, n_columns * sizeof (GType)); + priv->column_headers = g_renew (GType, priv->column_headers, n_columns); + for (i = priv->n_columns; i < n_columns; i++) + priv->column_headers[i] = G_TYPE_INVALID; + priv->n_columns = n_columns; - g_free (list_store->column_headers); - } - - if (list_store->sort_list) - _gtk_tree_data_list_header_free (list_store->sort_list); - - list_store->sort_list = _gtk_tree_data_list_header_new (n_columns, list_store->column_headers); - - list_store->column_headers = new_columns; - list_store->n_columns = n_columns; + if (priv->sort_list) + _gtk_tree_data_list_header_free (priv->sort_list); + priv->sort_list = _gtk_tree_data_list_header_new (n_columns, priv->column_headers); } static void @@ -341,39 +559,41 @@ gtk_list_store_set_column_type (GtkListStore *list_store, gint column, GType type) { + GtkListStorePrivate *priv = list_store->priv; + if (!_gtk_tree_data_list_check_type (type)) { g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type)); return; } - list_store->column_headers[column] = type; + priv->column_headers[column] = type; } static void gtk_list_store_finalize (GObject *object) { GtkListStore *list_store = GTK_LIST_STORE (object); + GtkListStorePrivate *priv = list_store->priv; - _gtk_sequence_foreach (list_store->seq, - (GFunc) _gtk_tree_data_list_free, list_store->column_headers); + g_sequence_foreach (priv->seq, + (GFunc) _gtk_tree_data_list_free, priv->column_headers); - _gtk_sequence_free (list_store->seq); + g_sequence_free (priv->seq); - _gtk_tree_data_list_header_free (list_store->sort_list); - g_free (list_store->column_headers); - - if (list_store->default_sort_destroy) + _gtk_tree_data_list_header_free (priv->sort_list); + g_free (priv->column_headers); + + if (priv->default_sort_destroy) { - GtkDestroyNotify d = list_store->default_sort_destroy; + GDestroyNotify d = priv->default_sort_destroy; - list_store->default_sort_destroy = NULL; - d (list_store->default_sort_data); - list_store->default_sort_data = NULL; + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); + priv->default_sort_data = NULL; } - /* must chain up */ - (* G_OBJECT_CLASS (gtk_list_store_parent_class)->finalize) (object); + G_OBJECT_CLASS (gtk_list_store_parent_class)->finalize (object); } /* Fulfill the GtkTreeModel requirements */ @@ -386,25 +606,26 @@ gtk_list_store_get_flags (GtkTreeModel *tree_model) static gint gtk_list_store_get_n_columns (GtkTreeModel *tree_model) { - GtkListStore *list_store = (GtkListStore *) tree_model; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; - list_store->columns_dirty = TRUE; + priv->columns_dirty = TRUE; - return list_store->n_columns; + return priv->n_columns; } static GType gtk_list_store_get_column_type (GtkTreeModel *tree_model, gint index) { - GtkListStore *list_store = (GtkListStore *) tree_model; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; - g_return_val_if_fail (index < GTK_LIST_STORE (tree_model)->n_columns, - G_TYPE_INVALID); + g_return_val_if_fail (index < priv->n_columns, G_TYPE_INVALID); - list_store->columns_dirty = TRUE; + priv->columns_dirty = TRUE; - return list_store->column_headers[index]; + return priv->column_headers[index]; } static gboolean @@ -412,21 +633,25 @@ gtk_list_store_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) { - GtkListStore *list_store = (GtkListStore *) tree_model; - GtkSequence *seq; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GSequence *seq; gint i; - list_store->columns_dirty = TRUE; + priv->columns_dirty = TRUE; + + seq = priv->seq; - seq = list_store->seq; - i = gtk_tree_path_get_indices (path)[0]; - if (i >= _gtk_sequence_get_length (seq)) - return FALSE; + if (i >= g_sequence_get_length (seq)) + { + iter->stamp = 0; + return FALSE; + } - iter->stamp = list_store->stamp; - iter->user_data = _gtk_sequence_get_ptr_at_pos (seq, i); + iter->stamp = priv->stamp; + iter->user_data = g_sequence_get_iter_at_pos (seq, i); return TRUE; } @@ -435,15 +660,17 @@ static GtkTreePath * gtk_list_store_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter) { + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; GtkTreePath *path; - g_return_val_if_fail (iter->stamp == GTK_LIST_STORE (tree_model)->stamp, NULL); + g_return_val_if_fail (iter->stamp == priv->stamp, NULL); - if (_gtk_sequence_ptr_is_end (iter->user_data)) + if (g_sequence_iter_is_end (iter->user_data)) return NULL; path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, _gtk_sequence_ptr_get_position (iter->user_data)); + gtk_tree_path_append_index (path, g_sequence_iter_get_position (iter->user_data)); return path; } @@ -454,23 +681,24 @@ gtk_list_store_get_value (GtkTreeModel *tree_model, gint column, GValue *value) { - GtkListStore *list_store = (GtkListStore *) tree_model; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; GtkTreeDataList *list; gint tmp_column = column; - g_return_if_fail (column < list_store->n_columns); - g_return_if_fail (VALID_ITER (iter, list_store)); + g_return_if_fail (column < priv->n_columns); + g_return_if_fail (iter_is_valid (iter, list_store)); - list = _gtk_sequence_ptr_get_data (iter->user_data); + list = g_sequence_get (iter->user_data); while (tmp_column-- > 0 && list) list = list->next; if (list == NULL) - g_value_init (value, list_store->column_headers[column]); + g_value_init (value, priv->column_headers[column]); else _gtk_tree_data_list_node_to_value (list, - list_store->column_headers[column], + priv->column_headers[column], value); } @@ -478,10 +706,38 @@ static gboolean gtk_list_store_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter) { - g_return_val_if_fail (GTK_LIST_STORE (tree_model)->stamp == iter->stamp, FALSE); - iter->user_data = _gtk_sequence_ptr_next (iter->user_data); + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + gboolean retval; + + g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); + iter->user_data = g_sequence_iter_next (iter->user_data); + + retval = g_sequence_iter_is_end (iter->user_data); + if (retval) + iter->stamp = 0; + + return !retval; +} + +static gboolean +gtk_list_store_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + + g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); - return !_gtk_sequence_ptr_is_end (iter->user_data); + if (g_sequence_iter_is_begin (iter->user_data)) + { + iter->stamp = 0; + return FALSE; + } + + iter->user_data = g_sequence_iter_prev (iter->user_data); + + return TRUE; } static gboolean @@ -490,19 +746,26 @@ gtk_list_store_iter_children (GtkTreeModel *tree_model, GtkTreeIter *parent) { GtkListStore *list_store = (GtkListStore *) tree_model; - + GtkListStorePrivate *priv = list_store->priv; + /* this is a list, nodes have no children */ if (parent) - return FALSE; + { + iter->stamp = 0; + return FALSE; + } - if (_gtk_sequence_get_length (list_store->seq) > 0) + if (g_sequence_get_length (priv->seq) > 0) { - iter->stamp = list_store->stamp; - iter->user_data = _gtk_sequence_get_begin_ptr (list_store->seq); + iter->stamp = priv->stamp; + iter->user_data = g_sequence_get_begin_iter (priv->seq); return TRUE; } else - return FALSE; + { + iter->stamp = 0; + return FALSE; + } } static gboolean @@ -516,12 +779,13 @@ static gint gtk_list_store_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter) { - GtkListStore *list_store = (GtkListStore *) tree_model; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; if (iter == NULL) - return _gtk_sequence_get_length (list_store->seq); + return g_sequence_get_length (priv->seq); - g_return_val_if_fail (list_store->stamp == iter->stamp, -1); + g_return_val_if_fail (priv->stamp == iter->stamp, -1); return 0; } @@ -532,18 +796,21 @@ gtk_list_store_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *parent, gint n) { - GtkListStore *list_store = (GtkListStore *) tree_model; - GtkSequencePtr child; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GSequenceIter *child; + + iter->stamp = 0; if (parent) return FALSE; - child = _gtk_sequence_get_ptr_at_pos (list_store->seq, n); + child = g_sequence_get_iter_at_pos (priv->seq, n); - if (_gtk_sequence_ptr_is_end (child)) + if (g_sequence_iter_is_end (child)) return FALSE; - iter->stamp = list_store->stamp; + iter->stamp = priv->stamp; iter->user_data = child; return TRUE; @@ -554,6 +821,7 @@ gtk_list_store_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) { + iter->stamp = 0; return FALSE; } @@ -564,37 +832,39 @@ gtk_list_store_real_set_value (GtkListStore *list_store, GValue *value, gboolean sort) { + GtkListStorePrivate *priv = list_store->priv; GtkTreeDataList *list; GtkTreeDataList *prev; gint old_column = column; - GValue real_value = {0, }; + GValue real_value = G_VALUE_INIT; gboolean converted = FALSE; gboolean retval = FALSE; - if (! g_type_is_a (G_VALUE_TYPE (value), list_store->column_headers[column])) + if (! g_type_is_a (G_VALUE_TYPE (value), priv->column_headers[column])) { - if (! (g_value_type_compatible (G_VALUE_TYPE (value), list_store->column_headers[column]) && - g_value_type_compatible (list_store->column_headers[column], G_VALUE_TYPE (value)))) + if (! (g_value_type_transformable (G_VALUE_TYPE (value), priv->column_headers[column]))) { g_warning ("%s: Unable to convert from %s to %s\n", G_STRLOC, g_type_name (G_VALUE_TYPE (value)), - g_type_name (list_store->column_headers[column])); + g_type_name (priv->column_headers[column])); return retval; } + + g_value_init (&real_value, priv->column_headers[column]); if (!g_value_transform (value, &real_value)) { g_warning ("%s: Unable to make conversion from %s to %s\n", G_STRLOC, g_type_name (G_VALUE_TYPE (value)), - g_type_name (list_store->column_headers[column])); + g_type_name (priv->column_headers[column])); g_value_unset (&real_value); return retval; } converted = TRUE; } - prev = list = _gtk_sequence_ptr_get_data (iter->user_data); + prev = list = g_sequence_get (iter->user_data); while (list != NULL) { @@ -617,10 +887,10 @@ gtk_list_store_real_set_value (GtkListStore *list_store, list = list->next; } - if (_gtk_sequence_ptr_get_data (iter->user_data) == NULL) + if (g_sequence_get (iter->user_data) == NULL) { list = _gtk_tree_data_list_alloc(); - _gtk_sequence_set (iter->user_data, list); + g_sequence_set (iter->user_data, list); list->next = NULL; } else @@ -671,10 +941,13 @@ gtk_list_store_set_value (GtkListStore *list_store, gint column, GValue *value) { + GtkListStorePrivate *priv; + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (VALID_ITER (iter, list_store)); - g_return_if_fail (column >= 0 && column < list_store->n_columns); + g_return_if_fail (iter_is_valid (iter, list_store)); g_return_if_fail (G_IS_VALUE (value)); + priv = list_store->priv; + g_return_if_fail (column >= 0 && column < priv->n_columns); if (gtk_list_store_real_set_value (list_store, iter, column, value, TRUE)) { @@ -689,28 +962,60 @@ gtk_list_store_set_value (GtkListStore *list_store, static GtkTreeIterCompareFunc gtk_list_store_get_compare_func (GtkListStore *list_store) { + GtkListStorePrivate *priv = list_store->priv; GtkTreeIterCompareFunc func = NULL; if (GTK_LIST_STORE_IS_SORTED (list_store)) { - if (list_store->sort_column_id != -1) + if (priv->sort_column_id != -1) { GtkTreeDataSortHeader *header; - header = _gtk_tree_data_list_get_header (list_store->sort_list, - list_store->sort_column_id); + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); g_return_val_if_fail (header != NULL, NULL); g_return_val_if_fail (header->func != NULL, NULL); func = header->func; } else { - func = list_store->default_sort_func; + func = priv->default_sort_func; } } return func; } +static void +gtk_list_store_set_vector_internal (GtkListStore *list_store, + GtkTreeIter *iter, + gboolean *emit_signal, + gboolean *maybe_need_sort, + gint *columns, + GValue *values, + gint n_values) +{ + GtkListStorePrivate *priv = list_store->priv; + gint i; + GtkTreeIterCompareFunc func = NULL; + + func = gtk_list_store_get_compare_func (list_store); + if (func != _gtk_tree_data_list_compare_func) + *maybe_need_sort = TRUE; + + for (i = 0; i < n_values; i++) + { + *emit_signal = gtk_list_store_real_set_value (list_store, + iter, + columns[i], + &values[i], + FALSE) || *emit_signal; + + if (func == _gtk_tree_data_list_compare_func && + columns[i] == priv->sort_column_id) + *maybe_need_sort = TRUE; + } +} + static void gtk_list_store_set_valist_internal (GtkListStore *list_store, GtkTreeIter *iter, @@ -718,6 +1023,7 @@ gtk_list_store_set_valist_internal (GtkListStore *list_store, gboolean *maybe_need_sort, va_list var_args) { + GtkListStorePrivate *priv = list_store->priv; gint column; GtkTreeIterCompareFunc func = NULL; @@ -729,17 +1035,17 @@ gtk_list_store_set_valist_internal (GtkListStore *list_store, while (column != -1) { - GValue value = { 0, }; + GValue value = G_VALUE_INIT; gchar *error = NULL; - if (column >= list_store->n_columns) + if (column < 0 || column >= priv->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; } - g_value_init (&value, list_store->column_headers[column]); - G_VALUE_COLLECT (&value, var_args, 0, &error); + G_VALUE_COLLECT_INIT (&value, priv->column_headers[column], + var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); @@ -759,7 +1065,7 @@ gtk_list_store_set_valist_internal (GtkListStore *list_store, FALSE) || *emit_signal; if (func == _gtk_tree_data_list_compare_func && - column == list_store->sort_column_id) + column == priv->sort_column_id) *maybe_need_sort = TRUE; g_value_unset (&value); @@ -768,6 +1074,57 @@ gtk_list_store_set_valist_internal (GtkListStore *list_store, } } +/** + * gtk_list_store_set_valuesv: + * @list_store: A #GtkListStore + * @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_list_store_set_valist() which + * takes the columns and values as two arrays, instead of + * varargs. This function is mainly intended for + * language-bindings and in case the number of columns to + * change is not known until run-time. + * + * Since: 2.12 + * Rename to: gtk_list_store_set + */ +void +gtk_list_store_set_valuesv (GtkListStore *list_store, + GtkTreeIter *iter, + gint *columns, + GValue *values, + gint n_values) +{ + GtkListStorePrivate *priv; + gboolean emit_signal = FALSE; + gboolean maybe_need_sort = FALSE; + + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + g_return_if_fail (iter_is_valid (iter, list_store)); + + priv = list_store->priv; + + gtk_list_store_set_vector_internal (list_store, iter, + &emit_signal, + &maybe_need_sort, + columns, values, n_values); + + if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) + gtk_list_store_sort_iter_changed (list_store, iter, priv->sort_column_id); + + if (emit_signal) + { + GtkTreePath *path; + + path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter); + gtk_tree_path_free (path); + } +} + /** * gtk_list_store_set_valist: * @list_store: A #GtkListStore @@ -783,11 +1140,14 @@ gtk_list_store_set_valist (GtkListStore *list_store, GtkTreeIter *iter, va_list var_args) { + GtkListStorePrivate *priv; gboolean emit_signal = FALSE; gboolean maybe_need_sort = FALSE; g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (VALID_ITER (iter, list_store)); + g_return_if_fail (iter_is_valid (iter, list_store)); + + priv = list_store->priv; gtk_list_store_set_valist_internal (list_store, iter, &emit_signal, @@ -795,7 +1155,7 @@ gtk_list_store_set_valist (GtkListStore *list_store, var_args); if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) - gtk_list_store_sort_iter_changed (list_store, iter, list_store->sort_column_id); + gtk_list_store_sort_iter_changed (list_store, iter, priv->sort_column_id); if (emit_signal) { @@ -811,7 +1171,7 @@ gtk_list_store_set_valist (GtkListStore *list_store, * gtk_list_store_set: * @list_store: a #GtkListStore * @iter: row iterator - * @Varargs: pairs of column number and value, terminated with -1 + * @...: pairs of column number and value, terminated with -1 * * Sets the value of one or more cells in the row referenced by @iter. * The variable argument list should contain integer column numbers, @@ -819,7 +1179,10 @@ gtk_list_store_set_valist (GtkListStore *list_store, * The list is terminated by a -1. For example, to set column 0 with type * %G_TYPE_STRING to "Foo", you would write gtk_list_store_set (store, iter, * 0, "Foo", -1). - **/ + * + * 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_list_store_set (GtkListStore *list_store, GtkTreeIter *iter, @@ -847,33 +1210,36 @@ gboolean gtk_list_store_remove (GtkListStore *list_store, GtkTreeIter *iter) { + GtkListStorePrivate *priv; GtkTreePath *path; - GtkSequencePtr ptr, next; + GSequenceIter *ptr, *next; g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE); - g_return_val_if_fail (VALID_ITER (iter, list_store), FALSE); + g_return_val_if_fail (iter_is_valid (iter, list_store), FALSE); + + priv = list_store->priv; path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); ptr = iter->user_data; - next = _gtk_sequence_ptr_next (ptr); + next = g_sequence_iter_next (ptr); - _gtk_tree_data_list_free (_gtk_sequence_ptr_get_data (ptr), list_store->column_headers); - _gtk_sequence_remove (iter->user_data); + _gtk_tree_data_list_free (g_sequence_get (ptr), priv->column_headers); + g_sequence_remove (iter->user_data); - list_store->length--; + priv->length--; gtk_tree_model_row_deleted (GTK_TREE_MODEL (list_store), path); gtk_tree_path_free (path); - if (_gtk_sequence_ptr_is_end (next)) + if (g_sequence_iter_is_end (next)) { iter->stamp = 0; return FALSE; } else { - iter->stamp = list_store->stamp; + iter->stamp = priv->stamp; iter->user_data = next; return TRUE; } @@ -882,13 +1248,13 @@ gtk_list_store_remove (GtkListStore *list_store, /** * gtk_list_store_insert: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the new row - * @position: position to insert the new row + * @iter: (out): An unset #GtkTreeIter to set to the new row + * @position: position to insert the new row, or -1 for last * * Creates a new row at @position. @iter will be changed to point to this new - * row. If @position is larger than the number of rows on the list, then the - * new row will be appended to the list. The row will be empty after this - * function is called. To fill in values, you need to call + * row. If @position is -1 or is larger than the number of rows on the list, + * then the new row will be appended to the list. The row will be empty after + * this function is called. To fill in values, you need to call * gtk_list_store_set() or gtk_list_store_set_value(). * **/ @@ -897,32 +1263,34 @@ gtk_list_store_insert (GtkListStore *list_store, GtkTreeIter *iter, gint position) { + GtkListStorePrivate *priv; GtkTreePath *path; - GtkSequence *seq; - GtkSequencePtr ptr; + GSequence *seq; + GSequenceIter *ptr; gint length; g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); - g_return_if_fail (position >= 0); - list_store->columns_dirty = TRUE; + priv = list_store->priv; - seq = list_store->seq; + priv->columns_dirty = TRUE; - length = _gtk_sequence_get_length (seq); - if (position > length) + seq = priv->seq; + + length = g_sequence_get_length (seq); + if (position > length || position < 0) position = length; - ptr = _gtk_sequence_get_ptr_at_pos (seq, position); - ptr = _gtk_sequence_insert (ptr, NULL); + ptr = g_sequence_get_iter_at_pos (seq, position); + ptr = g_sequence_insert_before (ptr, NULL); - iter->stamp = list_store->stamp; + iter->stamp = priv->stamp; iter->user_data = ptr; - g_assert (VALID_ITER (iter, list_store)); + g_assert (iter_is_valid (iter, list_store)); - list_store->length++; + priv->length++; path = gtk_tree_path_new (); gtk_tree_path_append_index (path, position); @@ -933,8 +1301,8 @@ gtk_list_store_insert (GtkListStore *list_store, /** * gtk_list_store_insert_before: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the new row - * @sibling: A valid #GtkTreeIter, or %NULL + * @iter: (out): An unset #GtkTreeIter to set to the new row + * @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 the end of the list. @iter will be changed to point to this @@ -947,26 +1315,30 @@ gtk_list_store_insert_before (GtkListStore *list_store, GtkTreeIter *iter, GtkTreeIter *sibling) { - GtkSequencePtr after; + GtkListStorePrivate *priv; + GSequenceIter *after; g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); + + priv = list_store->priv; + if (sibling) - g_return_if_fail (VALID_ITER (sibling, list_store)); + g_return_if_fail (iter_is_valid (sibling, list_store)); if (!sibling) - after = _gtk_sequence_get_end_ptr (list_store->seq); + after = g_sequence_get_end_iter (priv->seq); else after = sibling->user_data; - gtk_list_store_insert (list_store, iter, _gtk_sequence_ptr_get_position (after)); + gtk_list_store_insert (list_store, iter, g_sequence_iter_get_position (after)); } /** * gtk_list_store_insert_after: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the new row - * @sibling: A valid #GtkTreeIter, or %NULL + * @iter: (out): An unset #GtkTreeIter to set to the new row + * @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 the beginning of the list. @iter will be changed to point to @@ -979,25 +1351,29 @@ gtk_list_store_insert_after (GtkListStore *list_store, GtkTreeIter *iter, GtkTreeIter *sibling) { - GtkSequencePtr after; + GtkListStorePrivate *priv; + GSequenceIter *after; g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); + + priv = list_store->priv; + if (sibling) - g_return_if_fail (VALID_ITER (sibling, list_store)); + g_return_if_fail (iter_is_valid (sibling, list_store)); if (!sibling) - after = _gtk_sequence_get_begin_ptr (list_store->seq); + after = g_sequence_get_begin_iter (priv->seq); else - after = _gtk_sequence_ptr_next (sibling->user_data); + after = g_sequence_iter_next (sibling->user_data); - gtk_list_store_insert (list_store, iter, _gtk_sequence_ptr_get_position (after)); + gtk_list_store_insert (list_store, iter, g_sequence_iter_get_position (after)); } /** * gtk_list_store_prepend: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the prepend row + * @iter: (out): An unset #GtkTreeIter to set to the prepend row * * Prepends a new row to @list_store. @iter will be changed to point to this new * row. The row will be empty after this function is called. To fill in @@ -1017,7 +1393,7 @@ gtk_list_store_prepend (GtkListStore *list_store, /** * gtk_list_store_append: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the appended row + * @iter: (out): An unset #GtkTreeIter to set to the appended row * * Appends a new row to @list_store. @iter will be changed to point to this new * row. The row will be empty after this function is called. To fill in @@ -1031,17 +1407,19 @@ gtk_list_store_append (GtkListStore *list_store, g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); - gtk_list_store_insert (list_store, iter, _gtk_sequence_get_length (list_store->seq)); + gtk_list_store_insert (list_store, iter, -1); } static void gtk_list_store_increment_stamp (GtkListStore *list_store) { + GtkListStorePrivate *priv = list_store->priv; + do { - list_store->stamp++; + priv->stamp++; } - while (list_store->stamp == 0); + while (priv->stamp == 0); } /** @@ -1054,13 +1432,17 @@ gtk_list_store_increment_stamp (GtkListStore *list_store) void gtk_list_store_clear (GtkListStore *list_store) { + GtkListStorePrivate *priv; GtkTreeIter iter; + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - while (_gtk_sequence_get_length (list_store->seq) > 0) + priv = list_store->priv; + + while (g_sequence_get_length (priv->seq) > 0) { - iter.stamp = list_store->stamp; - iter.user_data = _gtk_sequence_get_begin_ptr (list_store->seq); + iter.stamp = priv->stamp; + iter.user_data = g_sequence_get_begin_iter (priv->seq); gtk_list_store_remove (list_store, &iter); } @@ -1088,13 +1470,7 @@ gtk_list_store_iter_is_valid (GtkListStore *list_store, g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE); g_return_val_if_fail (iter != NULL, FALSE); - if (!VALID_ITER (iter, list_store)) - return FALSE; - - if (_gtk_sequence_ptr_get_sequence (iter->user_data) != list_store->seq) - return FALSE; - - return TRUE; + return iter_is_valid (iter, list_store); } static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source, @@ -1149,15 +1525,13 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data) { - GtkTreeModel *tree_model; - GtkListStore *list_store; + GtkTreeModel *tree_model = GTK_TREE_MODEL (drag_dest); + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; GtkTreeModel *src_model = NULL; GtkTreePath *src_path = NULL; gboolean retval = FALSE; - tree_model = GTK_TREE_MODEL (drag_dest); - list_store = GTK_LIST_STORE (drag_dest); - if (gtk_tree_get_row_drag_data (selection_data, &src_model, &src_path) && @@ -1205,7 +1579,7 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, */ if (retval) { - GtkTreeDataList *dl = _gtk_sequence_ptr_get_data (src_iter.user_data); + GtkTreeDataList *dl = g_sequence_get (src_iter.user_data); GtkTreeDataList *copy_head = NULL; GtkTreeDataList *copy_prev = NULL; GtkTreeDataList *copy_iter = NULL; @@ -1216,7 +1590,7 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, while (dl) { copy_iter = _gtk_tree_data_list_node_copy (dl, - list_store->column_headers[col]); + priv->column_headers[col]); if (copy_head == NULL) copy_head = copy_iter; @@ -1230,8 +1604,8 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, ++col; } - dest_iter.stamp = list_store->stamp; - _gtk_sequence_set (dest_iter.user_data, copy_head); + dest_iter.stamp = priv->stamp; + g_sequence_set (dest_iter.user_data, copy_head); path = gtk_list_store_get_path (tree_model, &dest_iter); gtk_tree_model_row_changed (tree_model, path, &dest_iter); @@ -1282,7 +1656,7 @@ gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, indices = gtk_tree_path_get_indices (dest_path); - if (indices[0] <= _gtk_sequence_get_length (GTK_LIST_STORE (drag_dest)->seq)) + if (indices[0] <= g_sequence_get_length (GTK_LIST_STORE (drag_dest)->priv->seq)) retval = TRUE; out: @@ -1296,9 +1670,9 @@ gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, /* Reordering */ static gint -gtk_list_store_reorder_func (gconstpointer a, - gconstpointer b, - gpointer user_data) +gtk_list_store_reorder_func (GSequenceIter *a, + GSequenceIter *b, + gpointer user_data) { GHashTable *new_positions = user_data; gint apos = GPOINTER_TO_INT (g_hash_table_lookup (new_positions, a)); @@ -1314,9 +1688,10 @@ gtk_list_store_reorder_func (gconstpointer a, /** * gtk_list_store_reorder: * @store: A #GtkListStore. - * @new_order: an array of integers mapping the new position of each child - * to its old position before the re-ordering, - * i.e. @new_order[newpos] = oldpos. + * @new_order: (array zero-terminated=1): an array of integers mapping the new + * position of each child to its old position before the re-ordering, + * i.e. @new_order[newpos] = oldpos. It must have + * exactly as many items as the list store's length. * * Reorders @store to follow the order indicated by @new_order. Note that * this function only works with unsorted stores. @@ -1327,33 +1702,36 @@ void gtk_list_store_reorder (GtkListStore *store, gint *new_order) { + GtkListStorePrivate *priv; gint i; GtkTreePath *path; GHashTable *new_positions; - GtkSequencePtr ptr; + GSequenceIter *ptr; gint *order; g_return_if_fail (GTK_IS_LIST_STORE (store)); g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); g_return_if_fail (new_order != NULL); - order = g_new (gint, _gtk_sequence_get_length (store->seq)); - for (i = 0; i < _gtk_sequence_get_length (store->seq); i++) + priv = store->priv; + + order = g_new (gint, g_sequence_get_length (priv->seq)); + for (i = 0; i < g_sequence_get_length (priv->seq); i++) order[new_order[i]] = i; new_positions = g_hash_table_new (g_direct_hash, g_direct_equal); - ptr = _gtk_sequence_get_begin_ptr (store->seq); + ptr = g_sequence_get_begin_iter (priv->seq); i = 0; - while (!_gtk_sequence_ptr_is_end (ptr)) + while (!g_sequence_iter_is_end (ptr)) { g_hash_table_insert (new_positions, ptr, GINT_TO_POINTER (order[i++])); - ptr = _gtk_sequence_ptr_next (ptr); + ptr = g_sequence_iter_next (ptr); } g_free (order); - _gtk_sequence_sort (store->seq, gtk_list_store_reorder_func, new_positions); + g_sequence_sort_iter (priv->seq, gtk_list_store_reorder_func, new_positions); g_hash_table_destroy (new_positions); @@ -1365,37 +1743,37 @@ gtk_list_store_reorder (GtkListStore *store, } static GHashTable * -save_positions (GtkSequence *seq) +save_positions (GSequence *seq) { GHashTable *positions = g_hash_table_new (g_direct_hash, g_direct_equal); - GtkSequencePtr ptr; + GSequenceIter *ptr; - ptr = _gtk_sequence_get_begin_ptr (seq); - while (!_gtk_sequence_ptr_is_end (ptr)) + ptr = g_sequence_get_begin_iter (seq); + while (!g_sequence_iter_is_end (ptr)) { g_hash_table_insert (positions, ptr, - GINT_TO_POINTER (_gtk_sequence_ptr_get_position (ptr))); - ptr = _gtk_sequence_ptr_next (ptr); + GINT_TO_POINTER (g_sequence_iter_get_position (ptr))); + ptr = g_sequence_iter_next (ptr); } return positions; } static int * -generate_order (GtkSequence *seq, +generate_order (GSequence *seq, GHashTable *old_positions) { - GtkSequencePtr ptr; - int *order = g_new (int, _gtk_sequence_get_length (seq)); + GSequenceIter *ptr; + int *order = g_new (int, g_sequence_get_length (seq)); int i; i = 0; - ptr = _gtk_sequence_get_begin_ptr (seq); - while (!_gtk_sequence_ptr_is_end (ptr)) + ptr = g_sequence_get_begin_iter (seq); + while (!g_sequence_iter_is_end (ptr)) { int old_pos = GPOINTER_TO_INT (g_hash_table_lookup (old_positions, ptr)); order[i++] = old_pos; - ptr = _gtk_sequence_ptr_next (ptr); + ptr = g_sequence_iter_next (ptr); } g_hash_table_destroy (old_positions); @@ -1419,23 +1797,26 @@ gtk_list_store_swap (GtkListStore *store, GtkTreeIter *a, GtkTreeIter *b) { + GtkListStorePrivate *priv; GHashTable *old_positions; gint *order; GtkTreePath *path; g_return_if_fail (GTK_IS_LIST_STORE (store)); g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); - g_return_if_fail (VALID_ITER (a, store)); - g_return_if_fail (VALID_ITER (b, store)); + g_return_if_fail (iter_is_valid (a, store)); + g_return_if_fail (iter_is_valid (b, store)); + + priv = store->priv; if (a->user_data == b->user_data) return; - old_positions = save_positions (store->seq); + old_positions = save_positions (priv->seq); - _gtk_sequence_swap (a->user_data, b->user_data); + g_sequence_swap (a->user_data, b->user_data); - order = generate_order (store->seq, old_positions); + order = generate_order (priv->seq, old_positions); path = gtk_tree_path_new (); gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), @@ -1450,16 +1831,17 @@ gtk_list_store_move_to (GtkListStore *store, GtkTreeIter *iter, gint new_pos) { + GtkListStorePrivate *priv = store->priv; GHashTable *old_positions; GtkTreePath *path; gint *order; - - old_positions = save_positions (store->seq); - - _gtk_sequence_move (iter->user_data, _gtk_sequence_get_ptr_at_pos (store->seq, new_pos)); - order = generate_order (store->seq, old_positions); - + old_positions = save_positions (priv->seq); + + g_sequence_move (iter->user_data, g_sequence_get_iter_at_pos (priv->seq, new_pos)); + + order = generate_order (priv->seq, old_positions); + path = gtk_tree_path_new (); gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), path, NULL, order); @@ -1471,7 +1853,7 @@ gtk_list_store_move_to (GtkListStore *store, * gtk_list_store_move_before: * @store: A #GtkListStore. * @iter: A #GtkTreeIter. - * @position: A #GtkTreeIter, or %NULL. + * @position: (allow-none): A #GtkTreeIter, or %NULL. * * Moves @iter in @store to the position before @position. Note that this * function only works with unsorted stores. If @position is %NULL, @iter @@ -1488,12 +1870,12 @@ gtk_list_store_move_before (GtkListStore *store, g_return_if_fail (GTK_IS_LIST_STORE (store)); g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); - g_return_if_fail (VALID_ITER (iter, store)); + g_return_if_fail (iter_is_valid (iter, store)); if (position) - g_return_if_fail (VALID_ITER (position, store)); + g_return_if_fail (iter_is_valid (position, store)); if (position) - pos = _gtk_sequence_ptr_get_position (position->user_data); + pos = g_sequence_iter_get_position (position->user_data); else pos = -1; @@ -1504,7 +1886,7 @@ gtk_list_store_move_before (GtkListStore *store, * gtk_list_store_move_after: * @store: A #GtkListStore. * @iter: A #GtkTreeIter. - * @position: A #GtkTreeIter or %NULL. + * @position: (allow-none): A #GtkTreeIter or %NULL. * * Moves @iter in @store to the position after @position. Note that this * function only works with unsorted stores. If @position is %NULL, @iter @@ -1521,12 +1903,12 @@ gtk_list_store_move_after (GtkListStore *store, g_return_if_fail (GTK_IS_LIST_STORE (store)); g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); - g_return_if_fail (VALID_ITER (iter, store)); + g_return_if_fail (iter_is_valid (iter, store)); if (position) - g_return_if_fail (VALID_ITER (position, store)); + g_return_if_fail (iter_is_valid (position, store)); if (position) - pos = _gtk_sequence_ptr_get_position (position->user_data) + 1; + pos = g_sequence_iter_get_position (position->user_data) + 1; else pos = 0; @@ -1535,23 +1917,24 @@ gtk_list_store_move_after (GtkListStore *store, /* Sorting */ static gint -gtk_list_store_compare_func (gconstpointer a, - gconstpointer b, +gtk_list_store_compare_func (GSequenceIter *a, + GSequenceIter *b, gpointer user_data) { GtkListStore *list_store = user_data; + GtkListStorePrivate *priv = list_store->priv; GtkTreeIter iter_a; GtkTreeIter iter_b; gint retval; GtkTreeIterCompareFunc func; gpointer data; - if (list_store->sort_column_id != -1) + if (priv->sort_column_id != -1) { GtkTreeDataSortHeader *header; - header = _gtk_tree_data_list_get_header (list_store->sort_list, - list_store->sort_column_id); + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); g_return_val_if_fail (header != NULL, 0); g_return_val_if_fail (header->func != NULL, 0); @@ -1560,27 +1943,27 @@ gtk_list_store_compare_func (gconstpointer a, } else { - g_return_val_if_fail (list_store->default_sort_func != NULL, 0); - func = list_store->default_sort_func; - data = list_store->default_sort_data; + g_return_val_if_fail (priv->default_sort_func != NULL, 0); + func = priv->default_sort_func; + data = priv->default_sort_data; } - iter_a.stamp = list_store->stamp; + iter_a.stamp = priv->stamp; iter_a.user_data = (gpointer)a; - iter_b.stamp = list_store->stamp; + iter_b.stamp = priv->stamp; iter_b.user_data = (gpointer)b; - g_assert (VALID_ITER (&iter_a, list_store)); - g_assert (VALID_ITER (&iter_b, list_store)); - + g_assert (iter_is_valid (&iter_a, list_store)); + g_assert (iter_is_valid (&iter_b, list_store)); + retval = (* func) (GTK_TREE_MODEL (list_store), &iter_a, &iter_b, data); - if (list_store->order == GTK_SORT_DESCENDING) + if (priv->order == GTK_SORT_DESCENDING) { if (retval > 0) - retval = -1; + retval = -1; else if (retval < 0) - retval = 1; + retval = 1; } return retval; @@ -1589,20 +1972,21 @@ gtk_list_store_compare_func (gconstpointer a, static void gtk_list_store_sort (GtkListStore *list_store) { + GtkListStorePrivate *priv = list_store->priv; gint *new_order; GtkTreePath *path; GHashTable *old_positions; if (!GTK_LIST_STORE_IS_SORTED (list_store) || - _gtk_sequence_get_length (list_store->seq) <= 1) + g_sequence_get_length (priv->seq) <= 1) return; - old_positions = save_positions (list_store->seq); + old_positions = save_positions (priv->seq); - _gtk_sequence_sort (list_store->seq, gtk_list_store_compare_func, list_store); + g_sequence_sort_iter (priv->seq, gtk_list_store_compare_func, list_store); /* Let the world know about our new order */ - new_order = generate_order (list_store->seq, old_positions); + new_order = generate_order (priv->seq, old_positions); path = gtk_tree_path_new (); gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store), @@ -1615,17 +1999,17 @@ static gboolean iter_is_sorted (GtkListStore *list_store, GtkTreeIter *iter) { - GtkSequencePtr cmp; + GSequenceIter *cmp; - if (!_gtk_sequence_ptr_is_begin (iter->user_data)) + if (!g_sequence_iter_is_begin (iter->user_data)) { - cmp = _gtk_sequence_ptr_prev (iter->user_data); + cmp = g_sequence_iter_prev (iter->user_data); if (gtk_list_store_compare_func (cmp, iter->user_data, list_store) > 0) return FALSE; } - cmp = _gtk_sequence_ptr_next (iter->user_data); - if (!_gtk_sequence_ptr_is_end (cmp)) + cmp = g_sequence_iter_next (iter->user_data); + if (!g_sequence_iter_is_end (cmp)) { if (gtk_list_store_compare_func (iter->user_data, cmp, list_store) > 0) return FALSE; @@ -1640,6 +2024,7 @@ gtk_list_store_sort_iter_changed (GtkListStore *list_store, gint column) { + GtkListStorePrivate *priv = list_store->priv; GtkTreePath *path; path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); @@ -1651,11 +2036,11 @@ gtk_list_store_sort_iter_changed (GtkListStore *list_store, GHashTable *old_positions; gint *order; - old_positions = save_positions (list_store->seq); - _gtk_sequence_sort_changed (iter->user_data, - gtk_list_store_compare_func, - list_store); - order = generate_order (list_store->seq, old_positions); + old_positions = save_positions (priv->seq); + g_sequence_sort_changed_iter (iter->user_data, + gtk_list_store_compare_func, + list_store); + order = generate_order (priv->seq, old_positions); path = gtk_tree_path_new (); gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store), path, NULL, order); @@ -1669,15 +2054,16 @@ gtk_list_store_get_sort_column_id (GtkTreeSortable *sortable, gint *sort_column_id, GtkSortType *order) { - GtkListStore *list_store = (GtkListStore *) sortable; + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; if (sort_column_id) - * sort_column_id = list_store->sort_column_id; + * sort_column_id = priv->sort_column_id; if (order) - * order = list_store->order; + * order = priv->order; - if (list_store->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID || - list_store->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID || + priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) return FALSE; return TRUE; @@ -1688,10 +2074,11 @@ gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable, gint sort_column_id, GtkSortType order) { - GtkListStore *list_store = (GtkListStore *) sortable; + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; - if ((list_store->sort_column_id == sort_column_id) && - (list_store->order == order)) + if ((priv->sort_column_id == sort_column_id) && + (priv->order == order)) return; if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) @@ -1700,7 +2087,7 @@ gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable, { GtkTreeDataSortHeader *header = NULL; - header = _gtk_tree_data_list_get_header (list_store->sort_list, + header = _gtk_tree_data_list_get_header (priv->sort_list, sort_column_id); /* We want to make sure that we have a function */ @@ -1709,13 +2096,13 @@ gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable, } else { - g_return_if_fail (list_store->default_sort_func != NULL); + g_return_if_fail (priv->default_sort_func != NULL); } } - list_store->sort_column_id = sort_column_id; - list_store->order = order; + priv->sort_column_id = sort_column_id; + priv->order = order; gtk_tree_sortable_sort_column_changed (sortable); @@ -1727,15 +2114,16 @@ gtk_list_store_set_sort_func (GtkTreeSortable *sortable, gint sort_column_id, GtkTreeIterCompareFunc func, gpointer data, - GtkDestroyNotify destroy) + GDestroyNotify destroy) { - GtkListStore *list_store = (GtkListStore *) sortable; + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; - list_store->sort_list = _gtk_tree_data_list_set_header (list_store->sort_list, + priv->sort_list = _gtk_tree_data_list_set_header (priv->sort_list, sort_column_id, func, data, destroy); - if (list_store->sort_column_id == sort_column_id) + if (priv->sort_column_id == sort_column_id) gtk_list_store_sort (list_store); } @@ -1743,58 +2131,61 @@ static void gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable, GtkTreeIterCompareFunc func, gpointer data, - GtkDestroyNotify destroy) + GDestroyNotify destroy) { - GtkListStore *list_store = (GtkListStore *) sortable; + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; - if (list_store->default_sort_destroy) + if (priv->default_sort_destroy) { - GtkDestroyNotify d = list_store->default_sort_destroy; + GDestroyNotify d = priv->default_sort_destroy; - list_store->default_sort_destroy = NULL; - d (list_store->default_sort_data); + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); } - list_store->default_sort_func = func; - list_store->default_sort_data = data; - list_store->default_sort_destroy = destroy; + priv->default_sort_func = func; + priv->default_sort_data = data; + priv->default_sort_destroy = destroy; - if (list_store->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) gtk_list_store_sort (list_store); } static gboolean gtk_list_store_has_default_sort_func (GtkTreeSortable *sortable) { - GtkListStore *list_store = (GtkListStore *) sortable; + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; - return (list_store->default_sort_func != NULL); + return (priv->default_sort_func != NULL); } /** * gtk_list_store_insert_with_values: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the new row, or %NULL. - * @position: position to insert the new row - * @Varargs: pairs of column number and value, terminated with -1 + * @iter: (out) (allow-none): An unset #GtkTreeIter to set to the new row, or %NULL + * @position: position to insert the new row, or -1 to append after existing + * rows + * @...: pairs of column number and value, terminated with -1 + * + * Creates a new row at @position. @iter will be changed to point to this new + * row. If @position is -1, or larger than the number of rows in the list, then + * the new row will be appended to the list. The row will be filled with the + * values given to this function. * - * Creates a new row at @position. @iter will be changed to point to this new - * row. If @position is larger than the number of rows on the list, then the - * new row will be appended to the list. The row will be filled with the - * values given to this function. - * * Calling - * gtk_list_store_insert_with_values(list_store, iter, position...) - * has the same effect as calling - * + * gtk_list_store_insert_with_values (list_store, iter, position...) + * has the same effect as calling + * |[ * gtk_list_store_insert (list_store, iter, position); - * gtk_list_store_set (list_store_iter, ...); - * + * gtk_list_store_set (list_store, iter, ...); + * ]| * with the difference that the former will only emit a row_inserted signal, * while the latter will emit row_inserted, row_changed and, if the list store * is sorted, rows_reordered. Since emitting the rows_reordered signal - * repeatedly can affect the performance of the program, + * repeatedly can affect the performance of the program, * gtk_list_store_insert_with_values() should generally be preferred when * inserting rows in a sorted list store. * @@ -1806,9 +2197,10 @@ gtk_list_store_insert_with_values (GtkListStore *list_store, gint position, ...) { + GtkListStorePrivate *priv; GtkTreePath *path; - GtkSequence *seq; - GtkSequencePtr ptr; + GSequence *seq; + GSequenceIter *ptr; GtkTreeIter tmp_iter; gint length; gboolean changed = FALSE; @@ -1818,26 +2210,28 @@ gtk_list_store_insert_with_values (GtkListStore *list_store, /* FIXME: refactor to reduce overlap with gtk_list_store_set() */ g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + priv = list_store->priv; + if (!iter) iter = &tmp_iter; - list_store->columns_dirty = TRUE; + priv->columns_dirty = TRUE; - seq = list_store->seq; + seq = priv->seq; - length = _gtk_sequence_get_length (seq); - if (position > length) + length = g_sequence_get_length (seq); + if (position > length || position < 0) position = length; - ptr = _gtk_sequence_get_ptr_at_pos (seq, position); - ptr = _gtk_sequence_insert (ptr, NULL); + ptr = g_sequence_get_iter_at_pos (seq, position); + ptr = g_sequence_insert_before (ptr, NULL); - iter->stamp = list_store->stamp; + iter->stamp = priv->stamp; iter->user_data = ptr; - g_assert (VALID_ITER (iter, list_store)); + g_assert (iter_is_valid (iter, list_store)); - list_store->length++; + priv->length++; va_start (var_args, position); gtk_list_store_set_valist_internal (list_store, iter, @@ -1847,9 +2241,9 @@ gtk_list_store_insert_with_values (GtkListStore *list_store, /* Don't emit rows_reordered here */ if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) - _gtk_sequence_sort_changed (iter->user_data, - gtk_list_store_compare_func, - list_store); + g_sequence_sort_changed_iter (iter->user_data, + gtk_list_store_compare_func, + list_store); /* Just emit row_inserted */ path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); @@ -1861,10 +2255,10 @@ gtk_list_store_insert_with_values (GtkListStore *list_store, /** * gtk_list_store_insert_with_valuesv: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the new row, or %NULL. - * @position: position to insert the new row - * @columns: an array of column numbers - * @values: an array of GValues + * @iter: (out) (allow-none): An unset #GtkTreeIter to set to the new row, or %NULL. + * @position: position to insert the new row, or -1 for last + * @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_list_store_insert_with_values() which @@ -1882,64 +2276,52 @@ gtk_list_store_insert_with_valuesv (GtkListStore *list_store, GValue *values, gint n_values) { + GtkListStorePrivate *priv; GtkTreePath *path; - GtkSequence *seq; - GtkSequencePtr ptr; + GSequence *seq; + GSequenceIter *ptr; GtkTreeIter tmp_iter; gint length; gboolean changed = FALSE; gboolean maybe_need_sort = FALSE; - GtkTreeIterCompareFunc func = NULL; - gint i; /* FIXME refactor to reduce overlap with * gtk_list_store_insert_with_values() */ g_return_if_fail (GTK_IS_LIST_STORE (list_store)); + priv = list_store->priv; + if (!iter) iter = &tmp_iter; - list_store->columns_dirty = TRUE; + priv->columns_dirty = TRUE; - seq = list_store->seq; + seq = priv->seq; - length = _gtk_sequence_get_length (seq); - if (position > length) + length = g_sequence_get_length (seq); + if (position > length || position < 0) position = length; - ptr = _gtk_sequence_get_ptr_at_pos (seq, position); - ptr = _gtk_sequence_insert (ptr, NULL); + ptr = g_sequence_get_iter_at_pos (seq, position); + ptr = g_sequence_insert_before (ptr, NULL); - iter->stamp = list_store->stamp; + iter->stamp = priv->stamp; iter->user_data = ptr; - g_assert (VALID_ITER (iter, list_store)); + g_assert (iter_is_valid (iter, list_store)); - list_store->length++; + priv->length++; - func = gtk_list_store_get_compare_func (list_store); - if (func != _gtk_tree_data_list_compare_func) - maybe_need_sort = TRUE; - - for (i = 0; i < n_values; i++) - { - changed = gtk_list_store_real_set_value (list_store, - iter, - columns[i], - &values[i], - FALSE) || changed; - - if (func == _gtk_tree_data_list_compare_func && - columns[i] == list_store->sort_column_id) - maybe_need_sort = TRUE; - } + gtk_list_store_set_vector_internal (list_store, iter, + &changed, &maybe_need_sort, + columns, values, n_values); /* Don't emit rows_reordered here */ if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store)) - _gtk_sequence_sort_changed (iter->user_data, - gtk_list_store_compare_func, - list_store); + g_sequence_sort_changed_iter (iter->user_data, + gtk_list_store_compare_func, + list_store); /* Just emit row_inserted */ path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); @@ -1947,5 +2329,347 @@ gtk_list_store_insert_with_valuesv (GtkListStore *list_store, gtk_tree_path_free (path); } -#define __GTK_LIST_STORE_C__ -#include "gtkaliasdef.c" +/* GtkBuildable custom tag implementation + * + * + * + * + * + */ +typedef struct { + gboolean translatable; + gchar *context; + int id; +} ColInfo; + +typedef struct { + GtkBuilder *builder; + GObject *object; + GSList *column_type_names; + GType *column_types; + GValue *values; + gint *colids; + ColInfo **columns; + gint last_row; + gint n_columns; + gint row_column; + GQuark error_quark; + gboolean is_data; + const gchar *domain; +} SubParserData; + +static void +list_store_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + guint i; + SubParserData *data = (SubParserData*)user_data; + + if (strcmp (element_name, "col") == 0) + { + int i, id = -1; + gchar *context = NULL; + gboolean translatable = FALSE; + ColInfo *info; + + if (data->row_column >= data->n_columns) + { + g_set_error (error, data->error_quark, 0, + "Too many columns, maximum is %d\n", data->n_columns - 1); + return; + } + + for (i = 0; names[i]; i++) + if (strcmp (names[i], "id") == 0) + { + errno = 0; + id = atoi (values[i]); + if (errno) + { + g_set_error (error, data->error_quark, 0, + "the id tag %s could not be converted to an integer", + values[i]); + return; + } + if (id < 0 || id >= data->n_columns) + { + g_set_error (error, data->error_quark, 0, + "id value %d out of range", id); + return; + } + } + else if (strcmp (names[i], "translatable") == 0) + { + if (!_gtk_builder_boolean_from_string (values[i], &translatable, + error)) + return; + } + else if (strcmp (names[i], "comments") == 0) + { + /* do nothing, comments are for translators */ + } + else if (strcmp (names[i], "context") == 0) + { + context = g_strdup (values[i]); + } + + if (id == -1) + { + g_set_error (error, data->error_quark, 0, + " needs an id attribute"); + return; + } + + info = g_slice_new0 (ColInfo); + info->translatable = translatable; + info->context = context; + info->id = id; + + data->colids[data->row_column] = id; + data->columns[data->row_column] = info; + data->row_column++; + data->is_data = TRUE; + } + else if (strcmp (element_name, "row") == 0) + ; + else if (strcmp (element_name, "column") == 0) + { + for (i = 0; names[i]; i++) + if (strcmp (names[i], "type") == 0) + data->column_type_names = g_slist_prepend (data->column_type_names, + g_strdup (values[i])); + } + else if (strcmp (element_name, "columns") == 0) + ; + else if (strcmp (element_name, "data") == 0) + ; + else + g_set_error (error, data->error_quark, 0, + "Unknown start tag: %s", element_name); +} + +static void +list_store_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + + g_assert (data->builder); + + if (strcmp (element_name, "row") == 0) + { + GtkTreeIter iter; + int i; + + gtk_list_store_insert_with_valuesv (GTK_LIST_STORE (data->object), + &iter, + data->last_row, + data->colids, + data->values, + data->row_column); + for (i = 0; i < data->row_column; i++) + { + ColInfo *info = data->columns[i]; + g_free (info->context); + g_slice_free (ColInfo, info); + data->columns[i] = NULL; + g_value_unset (&data->values[i]); + } + g_free (data->values); + data->values = g_new0 (GValue, data->n_columns); + data->last_row++; + data->row_column = 0; + } + else if (strcmp (element_name, "columns") == 0) + { + GType *column_types; + GSList *l; + int i; + GType type; + + data->column_type_names = g_slist_reverse (data->column_type_names); + column_types = g_new0 (GType, g_slist_length (data->column_type_names)); + + for (l = data->column_type_names, 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; + } + column_types[i] = type; + + g_free (l->data); + } + + gtk_list_store_set_column_types (GTK_LIST_STORE (data->object), i, + column_types); + + g_free (column_types); + } + else if (strcmp (element_name, "col") == 0) + data->is_data = FALSE; + else if (strcmp (element_name, "data") == 0) + ; + else if (strcmp (element_name, "column") == 0) + ; + else + g_set_error (error, data->error_quark, 0, + "Unknown end tag: %s", element_name); +} + +static void +list_store_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + gint i; + GError *tmp_error = NULL; + gchar *string; + ColInfo *info; + + if (!data->is_data) + return; + + i = data->row_column - 1; + info = data->columns[i]; + + string = g_strndup (text, text_len); + if (info->translatable && text_len) + { + gchar *translated; + + /* FIXME: This will not use the domain set in the .ui file, + * since the parser is not telling the builder about the domain. + * However, it will work for gtk_builder_set_translation_domain() calls. + */ + translated = _gtk_builder_parser_translate (data->domain, + info->context, + string); + g_free (string); + string = translated; + } + + if (!gtk_builder_value_from_string_type (data->builder, + data->column_types[info->id], + string, + &data->values[i], + &tmp_error)) + { + g_set_error (error, + tmp_error->domain, + tmp_error->code, + "Could not convert '%s' to type %s: %s\n", + text, g_type_name (data->column_types[info->id]), + tmp_error->message); + g_error_free (tmp_error); + } + g_free (string); +} + +static const GMarkupParser list_store_parser = + { + list_store_start_element, + list_store_end_element, + list_store_text + }; + +static gboolean +gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + SubParserData *parser_data; + + if (child) + return FALSE; + + if (strcmp (tagname, "columns") == 0) + { + + parser_data = g_slice_new0 (SubParserData); + parser_data->builder = builder; + parser_data->object = G_OBJECT (buildable); + parser_data->column_type_names = NULL; + + *parser = list_store_parser; + *data = parser_data; + return TRUE; + } + else if (strcmp (tagname, "data") == 0) + { + gint n_columns = gtk_list_store_get_n_columns (GTK_TREE_MODEL (buildable)); + if (n_columns == 0) + g_error ("Cannot append data to an empty model"); + + parser_data = g_slice_new0 (SubParserData); + parser_data->builder = builder; + parser_data->object = G_OBJECT (buildable); + parser_data->values = g_new0 (GValue, n_columns); + parser_data->colids = g_new0 (gint, n_columns); + parser_data->columns = g_new0 (ColInfo*, n_columns); + parser_data->column_types = GTK_LIST_STORE (buildable)->priv->column_headers; + parser_data->n_columns = n_columns; + parser_data->last_row = 0; + parser_data->error_quark = g_quark_from_static_string ("GtkListStore"); + parser_data->domain = gtk_builder_get_translation_domain (builder); + + *parser = list_store_parser; + *data = parser_data; + return TRUE; + } + else + g_warning ("Unknown custom list store tag: %s", tagname); + + return FALSE; +} + +static void +gtk_list_store_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + SubParserData *sub = (SubParserData*)data; + + if (strcmp (tagname, "columns") == 0) + { + g_slist_free (sub->column_type_names); + g_slice_free (SubParserData, sub); + } + else if (strcmp (tagname, "data") == 0) + { + int i; + for (i = 0; i < sub->n_columns; i++) + { + ColInfo *info = sub->columns[i]; + if (info) + { + g_free (info->context); + g_slice_free (ColInfo, info); + } + } + g_free (sub->colids); + g_free (sub->columns); + g_free (sub->values); + g_slice_free (SubParserData, sub); + } + else + g_warning ("Unknown custom list store tag: %s", tagname); +}