X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkliststore.c;h=467bac0f15dc4da7b0a98336a01fe22f63952e7c;hb=HEAD;hp=43e3af78056f75539a484fe369a580a0a201f44e;hpb=80581c3011871fa000433a881554ffc1e9363468;p=~andy%2Fgtk diff --git a/gtk/gtkliststore.c b/gtk/gtkliststore.c index 43e3af780..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 "gtkintl.h" +#include "gtkbuildable.h" +#include "gtkbuilderprivate.h" -#define G_SLIST(x) ((GSList *) x) -#define GTK_LIST_STORE_IS_SORTED(list) (GTK_LIST_STORE (list)->sort_column_id != -2) -#define VALID_ITER(iter, list_store) (iter!= NULL && iter->user_data != NULL && list_store->stamp == iter->stamp) -static void gtk_list_store_init (GtkListStore *list_store); -static void gtk_list_store_class_init (GtkListStoreClass *class); +/** + * 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); @@ -73,6 +250,8 @@ static void gtk_list_store_set_column_type (GtkListStore *list_store, gint column, GType type); +static void gtk_list_store_increment_stamp (GtkListStore *list_store); + /* Drag and Drop */ static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source, @@ -105,110 +284,50 @@ 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); -static void gtk_list_store_move (GtkListStore *store, - GtkTreeIter *iter, - GtkTreeIter *path, - gboolean before); - - -static GObjectClass *parent_class = NULL; - - -static void -validate_list_store (GtkListStore *list_store) -{ - if (gtk_debug_flags & GTK_DEBUG_TREE) - { - g_assert (g_slist_length (list_store->root) == list_store->length); - - g_assert (g_slist_last (list_store->root) == list_store->tail); - } -} - -GType -gtk_list_store_get_type (void) -{ - static GType list_store_type = 0; - if (!list_store_type) - { - static const GTypeInfo list_store_info = - { - sizeof (GtkListStoreClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_list_store_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkListStore), - 0, - (GInstanceInitFunc) gtk_list_store_init, - }; - - static const GInterfaceInfo tree_model_info = - { - (GInterfaceInitFunc) gtk_list_store_tree_model_init, - NULL, - NULL - }; - - static const GInterfaceInfo drag_source_info = - { - (GInterfaceInitFunc) gtk_list_store_drag_source_init, - NULL, - NULL - }; - - static const GInterfaceInfo drag_dest_info = - { - (GInterfaceInitFunc) gtk_list_store_drag_dest_init, - NULL, - NULL - }; - - static const GInterfaceInfo sortable_info = - { - (GInterfaceInitFunc) gtk_list_store_sortable_init, - NULL, - NULL - }; - - list_store_type = g_type_register_static (G_TYPE_OBJECT, "GtkListStore", - &list_store_info, 0); - - g_type_add_interface_static (list_store_type, - GTK_TYPE_TREE_MODEL, - &tree_model_info); - g_type_add_interface_static (list_store_type, - GTK_TYPE_TREE_DRAG_SOURCE, - &drag_source_info); - g_type_add_interface_static (list_store_type, - GTK_TYPE_TREE_DRAG_DEST, - &drag_dest_info); - g_type_add_interface_static (list_store_type, - GTK_TYPE_TREE_SORTABLE, - &sortable_info); - } +/* 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) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + gtk_list_store_drag_source_init) + 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) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_list_store_buildable_init)) - return list_store_type; -} static void gtk_list_store_class_init (GtkListStoreClass *class) { GObjectClass *object_class; - parent_class = g_type_class_peek_parent (class); object_class = (GObjectClass*) class; object_class->finalize = gtk_list_store_finalize; + + g_type_class_add_private (class, sizeof (GtkListStorePrivate)); } static void @@ -221,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; @@ -253,36 +373,60 @@ 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->root = NULL; - list_store->tail = NULL; - list_store->sort_list = NULL; - list_store->stamp = g_random_int (); - list_store->length = 0; - list_store->sort_column_id = -2; - list_store->columns_dirty = FALSE; + 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, - ...) + ...) { GtkListStore *retval; va_list args; @@ -299,12 +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 passed to gtk_list_store_new\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); } @@ -318,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, @@ -340,8 +486,7 @@ gtk_list_store_newv (gint n_columns, { if (! _gtk_tree_data_list_check_type (types[i])) { - g_warning ("%s: Invalid type %s passed to gtk_list_store_newv\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; } @@ -356,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 @@ -368,17 +513,21 @@ 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])) { - g_warning ("%s: Invalid type %s passed to gtk_list_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_list_store_set_column_type (list_store, i, types[i]); @@ -389,33 +538,20 @@ static void gtk_list_store_set_n_columns (GtkListStore *list_store, gint n_columns) { - GType *new_columns; - - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (n_columns > 0); + 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)); - - g_free (list_store->column_headers); - } - - if (list_store->sort_list) - _gtk_tree_data_list_header_free (list_store->sort_list); + 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; - 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 @@ -423,76 +559,73 @@ gtk_list_store_set_column_type (GtkListStore *list_store, gint column, GType type) { - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (column >=0 && column < list_store->n_columns); + GtkListStorePrivate *priv = list_store->priv; if (!_gtk_tree_data_list_check_type (type)) { - g_warning ("%s: Invalid type %s passed to gtk_list_store_set_column_type\n", G_STRLOC, g_type_name (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; - g_slist_foreach (list_store->root, (GFunc) _gtk_tree_data_list_free, list_store->column_headers); - g_slist_free (list_store->root); + g_sequence_foreach (priv->seq, + (GFunc) _gtk_tree_data_list_free, priv->column_headers); - _gtk_tree_data_list_header_free (list_store->sort_list); - g_free (list_store->column_headers); - - if (list_store->default_sort_destroy) + g_sequence_free (priv->seq); + + _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 */ - (* parent_class->finalize) (object); + G_OBJECT_CLASS (gtk_list_store_parent_class)->finalize (object); } /* Fulfill the GtkTreeModel requirements */ static GtkTreeModelFlags gtk_list_store_get_flags (GtkTreeModel *tree_model) { - g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), 0); - return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; } static gint gtk_list_store_get_n_columns (GtkTreeModel *tree_model) { - GtkListStore *list_store = (GtkListStore *) tree_model; - - g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), 0); + 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 (GTK_IS_LIST_STORE (tree_model), G_TYPE_INVALID); - g_return_val_if_fail (index < GTK_LIST_STORE (tree_model)->n_columns && - index >= 0, 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 @@ -500,27 +633,25 @@ gtk_list_store_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) { - GtkListStore *list_store = (GtkListStore *) tree_model; - GSList *list; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GSequence *seq; gint i; - g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), FALSE); - g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); + priv->columns_dirty = TRUE; - list_store->columns_dirty = TRUE; + seq = priv->seq; i = gtk_tree_path_get_indices (path)[0]; - if (i >= list_store->length) - return FALSE; - - list = g_slist_nth (G_SLIST (list_store->root), i); - - /* If this fails, list_store->length has gotten mangled. */ - g_assert (list); + if (i >= g_sequence_get_length (seq)) + { + iter->stamp = 0; + return FALSE; + } - iter->stamp = list_store->stamp; - iter->user_data = list; + iter->stamp = priv->stamp; + iter->user_data = g_sequence_get_iter_at_pos (seq, i); return TRUE; } @@ -529,31 +660,19 @@ static GtkTreePath * gtk_list_store_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter) { - GtkTreePath *retval; - GSList *list; - gint i = 0; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GtkTreePath *path; - g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), NULL); - g_return_val_if_fail (iter->stamp == GTK_LIST_STORE (tree_model)->stamp, NULL); - if (G_SLIST (iter->user_data) == G_SLIST (GTK_LIST_STORE (tree_model)->tail)) - { - retval = gtk_tree_path_new (); - gtk_tree_path_append_index (retval, GTK_LIST_STORE (tree_model)->length - 1); - return retval; - } + g_return_val_if_fail (iter->stamp == priv->stamp, NULL); - for (list = G_SLIST (GTK_LIST_STORE (tree_model)->root); list; list = list->next) - { - if (list == G_SLIST (iter->user_data)) - break; - i++; - } - if (list == NULL) + if (g_sequence_iter_is_end (iter->user_data)) return NULL; - - retval = gtk_tree_path_new (); - gtk_tree_path_append_index (retval, i); - return retval; + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, g_sequence_iter_get_position (iter->user_data)); + + return path; } static void @@ -562,23 +681,24 @@ gtk_list_store_get_value (GtkTreeModel *tree_model, gint column, GValue *value) { + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; GtkTreeDataList *list; gint tmp_column = column; - g_return_if_fail (GTK_IS_LIST_STORE (tree_model)); - g_return_if_fail (column < GTK_LIST_STORE (tree_model)->n_columns); - g_return_if_fail (GTK_LIST_STORE (tree_model)->stamp == iter->stamp); - - list = G_SLIST (iter->user_data)->data; + g_return_if_fail (column < priv->n_columns); + g_return_if_fail (iter_is_valid (iter, list_store)); + + list = g_sequence_get (iter->user_data); while (tmp_column-- > 0 && list) list = list->next; if (list == NULL) - g_value_init (value, GTK_LIST_STORE (tree_model)->column_headers[column]); + g_value_init (value, priv->column_headers[column]); else _gtk_tree_data_list_node_to_value (list, - GTK_LIST_STORE (tree_model)->column_headers[column], + priv->column_headers[column], value); } @@ -586,12 +706,38 @@ static gboolean gtk_list_store_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter) { - g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), FALSE); - g_return_val_if_fail (GTK_LIST_STORE (tree_model)->stamp == iter->stamp, FALSE); + 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); + + if (g_sequence_iter_is_begin (iter->user_data)) + { + iter->stamp = 0; + return FALSE; + } - iter->user_data = G_SLIST (iter->user_data)->next; + iter->user_data = g_sequence_iter_prev (iter->user_data); - return (iter->user_data != NULL); + return TRUE; } static gboolean @@ -599,22 +745,27 @@ gtk_list_store_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, 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; - - /* but if parent == NULL we return the list itself as children of the - * "root" - */ + { + iter->stamp = 0; + return FALSE; + } - if (GTK_LIST_STORE (tree_model)->root) + if (g_sequence_get_length (priv->seq) > 0) { - iter->stamp = GTK_LIST_STORE (tree_model)->stamp; - iter->user_data = GTK_LIST_STORE (tree_model)->root; + 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 @@ -628,11 +779,14 @@ static gint gtk_list_store_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter) { - g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), -1); + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + if (iter == NULL) - return GTK_LIST_STORE (tree_model)->length; + return g_sequence_get_length (priv->seq); + + g_return_val_if_fail (priv->stamp == iter->stamp, -1); - g_return_val_if_fail (GTK_LIST_STORE (tree_model)->stamp == iter->stamp, -1); return 0; } @@ -642,23 +796,24 @@ gtk_list_store_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *parent, gint n) { - GSList *child; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GSequenceIter *child; - g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), FALSE); + iter->stamp = 0; if (parent) return FALSE; - child = g_slist_nth (G_SLIST (GTK_LIST_STORE (tree_model)->root), n); + child = g_sequence_get_iter_at_pos (priv->seq, n); - if (child) - { - iter->stamp = GTK_LIST_STORE (tree_model)->stamp; - iter->user_data = child; - return TRUE; - } - else + if (g_sequence_iter_is_end (child)) return FALSE; + + iter->stamp = priv->stamp; + iter->user_data = child; + + return TRUE; } static gboolean @@ -666,6 +821,7 @@ gtk_list_store_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) { + iter->stamp = 0; return FALSE; } @@ -676,42 +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; - 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 (column >= 0 && column < list_store->n_columns, FALSE); - g_return_val_if_fail (G_IS_VALUE (value), 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 = G_SLIST (iter->user_data)->data; + prev = list = g_sequence_get (iter->user_data); while (list != NULL) { @@ -734,9 +887,10 @@ gtk_list_store_real_set_value (GtkListStore *list_store, list = list->next; } - if (G_SLIST (iter->user_data)->data == NULL) + if (g_sequence_get (iter->user_data) == NULL) { - G_SLIST (iter->user_data)->data = list = _gtk_tree_data_list_alloc (); + list = _gtk_tree_data_list_alloc(); + g_sequence_set (iter->user_data, list); list->next = NULL; } else @@ -787,79 +941,111 @@ 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)) { GtkTreePath *path; - path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_store), iter); + 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 - * @iter: A valid #GtkTreeIter for the row being modified - * @var_args: va_list of column/value pairs - * - * See gtk_list_store_set(); this version takes a va_list for use by language - * bindings. - * - **/ -void -gtk_list_store_set_valist (GtkListStore *list_store, - GtkTreeIter *iter, - va_list var_args) +static GtkTreeIterCompareFunc +gtk_list_store_get_compare_func (GtkListStore *list_store) { - gint column; - gboolean emit_signal = FALSE; - gboolean maybe_need_sort = FALSE; + GtkListStorePrivate *priv = list_store->priv; GtkTreeIterCompareFunc func = NULL; - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (VALID_ITER (iter, list_store)); - - column = va_arg (var_args, gint); - 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); - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); + 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; } } - if (func != gtk_tree_data_list_compare_func) - maybe_need_sort = TRUE; + 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, + gboolean *emit_signal, + gboolean *maybe_need_sort, + va_list var_args) +{ + GtkListStorePrivate *priv = list_store->priv; + gint column; + GtkTreeIterCompareFunc func = NULL; + + column = va_arg (var_args, gint); + + func = gtk_list_store_get_compare_func (list_store); + if (func != _gtk_tree_data_list_compare_func) + *maybe_need_sort = TRUE; 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); @@ -872,29 +1058,110 @@ gtk_list_store_set_valist (GtkListStore *list_store, } /* FIXME: instead of calling this n times, refactor with above */ - emit_signal = gtk_list_store_real_set_value (list_store, - iter, - column, - &value, - FALSE) || emit_signal; - - if (func == gtk_tree_data_list_compare_func && - column == list_store->sort_column_id) - maybe_need_sort = TRUE; + *emit_signal = gtk_list_store_real_set_value (list_store, + iter, + column, + &value, + FALSE) || *emit_signal; + + if (func == _gtk_tree_data_list_compare_func && + column == priv->sort_column_id) + *maybe_need_sort = TRUE; g_value_unset (&value); column = va_arg (var_args, gint); } +} + +/** + * 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 + * @iter: A valid #GtkTreeIter for the row being modified + * @var_args: va_list of column/value pairs + * + * See gtk_list_store_set(); this version takes a va_list for use by language + * bindings. + * + **/ +void +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 (iter_is_valid (iter, list_store)); + + priv = list_store->priv; + + gtk_list_store_set_valist_internal (list_store, iter, + &emit_signal, + &maybe_need_sort, + 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) { GtkTreePath *path; - path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_store), iter); + 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); } @@ -904,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, @@ -912,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, @@ -920,154 +1190,72 @@ gtk_list_store_set (GtkListStore *list_store, { va_list var_args; - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (iter != NULL); - g_return_if_fail (iter->stamp == list_store->stamp); - va_start (var_args, iter); gtk_list_store_set_valist (list_store, iter, var_args); va_end (var_args); } -static GSList* -remove_link_saving_prev (GSList *list, - GSList *link, - GSList **prevp) +/** + * gtk_list_store_remove: + * @list_store: A #GtkListStore + * @iter: A valid #GtkTreeIter + * + * Removes the given row from the list store. After being removed, + * @iter is set to be the next valid row, or invalidated if it pointed + * to the last row in @list_store. + * + * Return value: %TRUE if @iter is valid, %FALSE if not. + **/ +gboolean +gtk_list_store_remove (GtkListStore *list_store, + GtkTreeIter *iter) { - GSList *tmp; - GSList *prev; - - prev = NULL; - tmp = list; - - while (tmp) - { - if (tmp == link) - { - if (prev) - prev->next = link->next; - - if (list == link) - list = list->next; - - link->next = NULL; - break; - } - - prev = tmp; - tmp = tmp->next; - } - - *prevp = prev; - - return list; -} - -static void -gtk_list_store_remove_silently (GtkListStore *list_store, - GtkTreeIter *iter, - GtkTreePath *path) -{ - if (G_SLIST (iter->user_data)->data) - { - _gtk_tree_data_list_free ((GtkTreeDataList *) G_SLIST (iter->user_data)->data, - list_store->column_headers); - G_SLIST (iter->user_data)->data = NULL; - } - - { - GSList *prev = NULL; - - list_store->root = remove_link_saving_prev (G_SLIST (list_store->root), - G_SLIST (iter->user_data), - &prev); - - list_store->length -= 1; - - if (iter->user_data == list_store->tail) - list_store->tail = prev; - - g_slist_free (G_SLIST (iter->user_data)); - } -} - -/** - * gtk_list_store_remove: - * @list_store: A #GtkListStore - * @iter: A valid #GtkTreeIter - * - * Removes the given row from the list store. After being removed, - * @iter is set to be the next valid row, or invalidated if it pointed - * to the last row in @list_store. - * - * Return value: %TRUE if @iter is valid, %FALSE if not. - **/ -gboolean -gtk_list_store_remove (GtkListStore *list_store, - GtkTreeIter *iter) -{ - GtkTreePath *path; - GSList *next; + GtkListStorePrivate *priv; + GtkTreePath *path; + 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); - - next = G_SLIST (iter->user_data)->next; - path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); + g_return_val_if_fail (iter_is_valid (iter, list_store), FALSE); - validate_list_store (list_store); + priv = list_store->priv; - gtk_list_store_remove_silently (list_store, iter, path); + path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); - validate_list_store (list_store); + ptr = iter->user_data; + next = g_sequence_iter_next (ptr); + + _gtk_tree_data_list_free (g_sequence_get (ptr), priv->column_headers); + g_sequence_remove (iter->user_data); + priv->length--; + gtk_tree_model_row_deleted (GTK_TREE_MODEL (list_store), path); gtk_tree_path_free (path); - if (next) + if (g_sequence_iter_is_end (next)) { - iter->stamp = list_store->stamp; - iter->user_data = next; - return TRUE; + iter->stamp = 0; + return FALSE; } else { - iter->stamp = 0; + iter->stamp = priv->stamp; + iter->user_data = next; + return TRUE; } - - return FALSE; -} - -static void -insert_after (GtkListStore *list_store, - GSList *sibling, - GSList *new_list) -{ - g_return_if_fail (sibling != NULL); - g_return_if_fail (new_list != NULL); - - /* insert new node after list */ - new_list->next = sibling->next; - sibling->next = new_list; - - /* if list was the tail, the new node is the new tail */ - if (sibling == ((GSList *) list_store->tail)) - list_store->tail = new_list; - - list_store->length += 1; } /** * 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 before this - * function is called. To fill in values, you need to call gtk_list_store_set() - * or gtk_list_store_set_value(). + * 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(). * **/ void @@ -1075,42 +1263,35 @@ gtk_list_store_insert (GtkListStore *list_store, GtkTreeIter *iter, gint position) { - GSList *list; + GtkListStorePrivate *priv; GtkTreePath *path; - GSList *new_list; + 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; - - if (position == 0 || - GTK_LIST_STORE_IS_SORTED (list_store)) - { - gtk_list_store_prepend (list_store, iter); - return; - } - list = g_slist_nth (G_SLIST (list_store->root), position - 1); + priv = list_store->priv; - if (list == NULL) - { - /* position if off the end of the list, append it */ - gtk_list_store_append (list_store, iter); + priv->columns_dirty = TRUE; - return; - } + seq = priv->seq; - new_list = g_slist_alloc (); + length = g_sequence_get_length (seq); + if (position > length || position < 0) + position = length; - insert_after (list_store, list, new_list); + ptr = g_sequence_get_iter_at_pos (seq, position); + ptr = g_sequence_insert_before (ptr, NULL); - iter->stamp = list_store->stamp; - iter->user_data = new_list; + iter->stamp = priv->stamp; + iter->user_data = ptr; - validate_list_store (list_store); + g_assert (iter_is_valid (iter, list_store)); + priv->length++; + path = gtk_tree_path_new (); gtk_tree_path_append_index (path, position); gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); @@ -1120,13 +1301,13 @@ 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 new - * row. The row will be empty before this function is called. To fill in values, - * you need to call gtk_list_store_set() or gtk_list_store_set_value(). + * 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 + * new row. 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(). * **/ void @@ -1134,83 +1315,30 @@ gtk_list_store_insert_before (GtkListStore *list_store, GtkTreeIter *iter, GtkTreeIter *sibling) { - GtkTreePath *path; - GSList *list, *prev, *new_list; - gint i = 0; - + GtkListStorePrivate *priv; + GSequenceIter *after; + g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); - if (sibling) - g_return_if_fail (VALID_ITER (sibling, list_store)); - - list_store->columns_dirty = TRUE; - - if (GTK_LIST_STORE_IS_SORTED (list_store)) - { - gtk_list_store_prepend (list_store, iter); - return; - } - - if (sibling == NULL) - { - gtk_list_store_append (list_store, iter); - return; - } - - new_list = g_slist_alloc (); - - prev = NULL; - list = list_store->root; - while (list && list != sibling->user_data) - { - prev = list; - list = list->next; - i++; - } - - if (list != sibling->user_data) - { - g_warning ("%s: sibling iterator invalid? not found in the list", G_STRLOC); - return; - } - /* if there are no nodes, we become the list tail, otherwise we - * are inserting before any existing nodes so we can't change - * the tail - */ + priv = list_store->priv; - if (list_store->root == NULL) - list_store->tail = new_list; + if (sibling) + g_return_if_fail (iter_is_valid (sibling, list_store)); - if (prev) - { - new_list->next = prev->next; - prev->next = new_list; - } + if (!sibling) + after = g_sequence_get_end_iter (priv->seq); else - { - new_list->next = list_store->root; - list_store->root = new_list; - } - - iter->stamp = list_store->stamp; - iter->user_data = new_list; + after = sibling->user_data; - list_store->length += 1; - - validate_list_store (list_store); - - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, i); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); + 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 @@ -1223,48 +1351,29 @@ gtk_list_store_insert_after (GtkListStore *list_store, GtkTreeIter *iter, GtkTreeIter *sibling) { - GtkTreePath *path; - GSList *list, *new_list; - gint i = 0; + GtkListStorePrivate *priv; + GSequenceIter *after; g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); - if (sibling) - g_return_if_fail (VALID_ITER (sibling, list_store)); - - list_store->columns_dirty = TRUE; - if (sibling == NULL || - GTK_LIST_STORE_IS_SORTED (list_store)) - { - gtk_list_store_prepend (list_store, iter); - return; - } - - for (list = list_store->root; list && list != sibling->user_data; list = list->next) - i++; + priv = list_store->priv; - g_return_if_fail (list == sibling->user_data); - - new_list = g_slist_alloc (); - - insert_after (list_store, list, new_list); - - iter->stamp = list_store->stamp; - iter->user_data = new_list; + if (sibling) + g_return_if_fail (iter_is_valid (sibling, list_store)); - validate_list_store (list_store); + if (!sibling) + after = g_sequence_get_begin_iter (priv->seq); + else + after = g_sequence_iter_next (sibling->user_data); - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, i + 1); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); + 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 @@ -1275,36 +1384,16 @@ void gtk_list_store_prepend (GtkListStore *list_store, GtkTreeIter *iter) { - GtkTreePath *path; - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); - iter->stamp = list_store->stamp; - iter->user_data = g_slist_alloc (); - - list_store->columns_dirty = TRUE; - - if (list_store->root == NULL) - list_store->tail = iter->user_data; - - G_SLIST (iter->user_data)->next = G_SLIST (list_store->root); - list_store->root = iter->user_data; - - list_store->length += 1; - - validate_list_store (list_store); - - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, 0); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); + gtk_list_store_insert (list_store, iter, 0); } /** * 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 @@ -1315,37 +1404,22 @@ void gtk_list_store_append (GtkListStore *list_store, GtkTreeIter *iter) { - GtkTreePath *path; - g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); - list_store->columns_dirty = TRUE; + gtk_list_store_insert (list_store, iter, -1); +} - if (GTK_LIST_STORE_IS_SORTED (list_store)) +static void +gtk_list_store_increment_stamp (GtkListStore *list_store) +{ + GtkListStorePrivate *priv = list_store->priv; + + do { - gtk_list_store_prepend (list_store, iter); - return; + priv->stamp++; } - - iter->stamp = list_store->stamp; - iter->user_data = g_slist_alloc (); - - if (list_store->tail) - ((GSList *)list_store->tail)->next = iter->user_data; - else - list_store->root = iter->user_data; - - list_store->tail = iter->user_data; - - list_store->length += 1; - - validate_list_store (list_store); - - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, list_store->length - 1); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); - gtk_tree_path_free (path); + while (priv->stamp == 0); } /** @@ -1358,15 +1432,21 @@ gtk_list_store_append (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 (list_store->root) + priv = list_store->priv; + + while (g_sequence_get_length (priv->seq) > 0) { - iter.stamp = list_store->stamp; - iter.user_data = list_store->root; + iter.stamp = priv->stamp; + iter.user_data = g_sequence_get_begin_iter (priv->seq); gtk_list_store_remove (list_store, &iter); } + + gtk_list_store_increment_stamp (list_store); } /** @@ -1374,8 +1454,8 @@ gtk_list_store_clear (GtkListStore *list_store) * @list_store: A #GtkListStore. * @iter: A #GtkTreeIter. * - * WARNING: This function is slow. Only use it for debugging and/or testing - * purposes. + * This function is slow. Only use it for debugging and/or testing + * purposes. * * Checks if the given iter is a valid iter for this #GtkListStore. * @@ -1387,24 +1467,10 @@ gboolean gtk_list_store_iter_is_valid (GtkListStore *list_store, GtkTreeIter *iter) { - GList *list; - 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 (iter->user_data == list_store->root) - return TRUE; - if (iter->user_data == list_store->tail) - return TRUE; - - for (list = ((GList *)list_store->root)->next; list; list = list->next) - if (list == iter->user_data) - return TRUE; - - return FALSE; + return iter_is_valid (iter, list_store); } static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source, @@ -1418,9 +1484,8 @@ gtk_list_store_drag_data_delete (GtkTreeDragSource *drag_source, GtkTreePath *path) { GtkTreeIter iter; - g_return_val_if_fail (GTK_IS_LIST_STORE (drag_source), FALSE); - if (gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source), + if (gtk_list_store_get_iter (GTK_TREE_MODEL (drag_source), &iter, path)) { @@ -1435,8 +1500,6 @@ gtk_list_store_drag_data_get (GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data) { - g_return_val_if_fail (GTK_IS_LIST_STORE (drag_source), FALSE); - /* Note that we don't need to handle the GTK_TREE_MODEL_ROW * target, because the default handler does it for us, but * we do anyway for the convenience of someone maybe overriding the @@ -1462,17 +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; - g_return_val_if_fail (GTK_IS_LIST_STORE (drag_dest), 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) && @@ -1483,7 +1542,7 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, GtkTreeIter dest_iter; GtkTreePath *prev; - if (!gtk_tree_model_get_iter (src_model, + if (!gtk_list_store_get_iter (src_model, &src_iter, src_path)) { @@ -1504,7 +1563,7 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, } else { - if (gtk_tree_model_get_iter (tree_model, &dest_iter, prev)) + if (gtk_list_store_get_iter (tree_model, &dest_iter, prev)) { GtkTreeIter tmp_iter = dest_iter; @@ -1520,7 +1579,7 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, */ if (retval) { - GtkTreeDataList *dl = G_SLIST (src_iter.user_data)->data; + GtkTreeDataList *dl = g_sequence_get (src_iter.user_data); GtkTreeDataList *copy_head = NULL; GtkTreeDataList *copy_prev = NULL; GtkTreeDataList *copy_iter = NULL; @@ -1531,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; @@ -1545,8 +1604,8 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, ++col; } - dest_iter.stamp = list_store->stamp; - G_SLIST (dest_iter.user_data)->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); @@ -1578,8 +1637,6 @@ gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, GtkTreePath *src_path = NULL; gboolean retval = FALSE; - g_return_val_if_fail (GTK_IS_LIST_STORE (drag_dest), FALSE); - /* don't accept drops if the list has been sorted */ if (GTK_LIST_STORE_IS_SORTED (drag_dest)) return FALSE; @@ -1599,7 +1656,7 @@ gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, indices = gtk_tree_path_get_indices (dest_path); - if (indices[0] <= GTK_LIST_STORE (drag_dest)->length) + if (indices[0] <= g_sequence_get_length (GTK_LIST_STORE (drag_dest)->priv->seq)) retval = TRUE; out: @@ -1610,38 +1667,31 @@ gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, } /* Sorting and reordering */ -typedef struct _SortTuple -{ - gint offset; - GSList *el; -} SortTuple; /* 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) { - SortTuple *a_reorder; - SortTuple *b_reorder; - - a_reorder = (SortTuple *)a; - b_reorder = (SortTuple *)b; + GHashTable *new_positions = user_data; + gint apos = GPOINTER_TO_INT (g_hash_table_lookup (new_positions, a)); + gint bpos = GPOINTER_TO_INT (g_hash_table_lookup (new_positions, b)); - if (a_reorder->offset < b_reorder->offset) + if (apos < bpos) return -1; - if (a_reorder->offset > b_reorder->offset) + if (apos > bpos) return 1; - return 0; } - + /** * 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. @@ -1652,46 +1702,83 @@ void gtk_list_store_reorder (GtkListStore *store, gint *new_order) { + GtkListStorePrivate *priv; gint i; - GSList *current_list; GtkTreePath *path; - SortTuple *sort_array; - + GHashTable *new_positions; + 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); - sort_array = g_new (SortTuple, store->length); + priv = store->priv; - current_list = store->root; + 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); - for (i = 0; i < store->length; i++) + ptr = g_sequence_get_begin_iter (priv->seq); + i = 0; + while (!g_sequence_iter_is_end (ptr)) { - sort_array[new_order[i]].offset = i; - sort_array[i].el = current_list; + g_hash_table_insert (new_positions, ptr, GINT_TO_POINTER (order[i++])); - current_list = current_list->next; + ptr = g_sequence_iter_next (ptr); } + g_free (order); + + g_sequence_sort_iter (priv->seq, gtk_list_store_reorder_func, new_positions); - g_qsort_with_data (sort_array, - store->length, - sizeof (SortTuple), - gtk_list_store_reorder_func, - NULL); - - for (i = 0; i < store->length - 1; i++) - G_SLIST (sort_array[i].el)->next = G_SLIST (sort_array[i+1].el); - - store->root = G_SLIST (sort_array[0].el); - store->tail = G_SLIST (sort_array[store->length-1].el); - G_SLIST (store->tail)->next = NULL; - + g_hash_table_destroy (new_positions); + /* emit signal */ path = gtk_tree_path_new (); gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), path, NULL, new_order); gtk_tree_path_free (path); - g_free (sort_array); +} + +static GHashTable * +save_positions (GSequence *seq) +{ + GHashTable *positions = g_hash_table_new (g_direct_hash, g_direct_equal); + GSequenceIter *ptr; + + ptr = g_sequence_get_begin_iter (seq); + while (!g_sequence_iter_is_end (ptr)) + { + g_hash_table_insert (positions, ptr, + GINT_TO_POINTER (g_sequence_iter_get_position (ptr))); + ptr = g_sequence_iter_next (ptr); + } + + return positions; +} + +static int * +generate_order (GSequence *seq, + GHashTable *old_positions) +{ + GSequenceIter *ptr; + int *order = g_new (int, g_sequence_get_length (seq)); + int i; + + i = 0; + 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 = g_sequence_iter_next (ptr); + } + + g_hash_table_destroy (old_positions); + + return order; } /** @@ -1710,78 +1797,50 @@ gtk_list_store_swap (GtkListStore *store, GtkTreeIter *a, GtkTreeIter *b) { - GSList *i, *prev_a = NULL, *prev_b = NULL; - gint j, a_count = 0, b_count = 0, *order; + 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; - if (a->user_data == store->root) - prev_a = NULL; - else - { - for (i = store->root; i; i = i->next, a_count++) - if (i->next == a->user_data) - { - prev_a = i; - break; - } - - a_count++; - } - - if (b->user_data == store->root) - prev_b = NULL; - else - { - for (i = store->root; i; i = i->next, b_count++) - if (i->next == b->user_data) - { - prev_b = i; - break; - } - - b_count++; - } + old_positions = save_positions (priv->seq); + + g_sequence_swap (a->user_data, b->user_data); - if (!prev_a) - store->root = b->user_data; - else - prev_a->next = b->user_data; + order = generate_order (priv->seq, old_positions); + path = gtk_tree_path_new (); + + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), + path, NULL, order); - if (!prev_b) - store->root = a->user_data; - else - prev_b->next = a->user_data; + gtk_tree_path_free (path); + g_free (order); +} - /* think a_next inspead of a_prev here ... */ - prev_a = G_SLIST (a->user_data)->next; - prev_b = G_SLIST (b->user_data)->next; +static void +gtk_list_store_move_to (GtkListStore *store, + GtkTreeIter *iter, + gint new_pos) +{ + GtkListStorePrivate *priv = store->priv; + GHashTable *old_positions; + GtkTreePath *path; + gint *order; - G_SLIST (a->user_data)->next = prev_b; - G_SLIST (b->user_data)->next = prev_a; + old_positions = save_positions (priv->seq); - /* update tail if needed */ - if (! G_SLIST (a->user_data)->next) - store->tail = G_SLIST (a->user_data); - else if (! G_SLIST (b->user_data)->next) - store->tail = G_SLIST (b->user_data); + g_sequence_move (iter->user_data, g_sequence_get_iter_at_pos (priv->seq, new_pos)); - /* emit signal */ - order = g_new (gint, store->length); - for (j = 0; j < store->length; j++) - if (j == a_count) - order[j] = b_count; - else if (j == b_count) - order[j] = a_count; - else - order[j] = j; + order = generate_order (priv->seq, old_positions); path = gtk_tree_path_new (); gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), @@ -1790,229 +1849,44 @@ gtk_list_store_swap (GtkListStore *store, g_free (order); } -static void -gtk_list_store_move (GtkListStore *store, - GtkTreeIter *iter, - GtkTreeIter *position, - gboolean before) +/** + * gtk_list_store_move_before: + * @store: A #GtkListStore. + * @iter: A #GtkTreeIter. + * @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 + * will be moved to the end of the list. + * + * Since: 2.2 + **/ +void +gtk_list_store_move_before (GtkListStore *store, + GtkTreeIter *iter, + GtkTreeIter *position) { - GtkTreeIter dst_a; - GSList *i, *a, *prev = NULL, *tmp; - gint new_pos = 0, old_pos = 0, j = 0, *order; - GtkTreePath *path = NULL, *pos_path = NULL; - + gint pos; + 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)); - /* lots of sanity checks */ if (position) - { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter); - pos_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), position); - - if (gtk_tree_path_get_depth (pos_path) != 1) - goto free_paths_and_out; - - /* if before: - * moving the iter before path or "path + 1" doesn't make sense - * else - * moving the iter before path or "path - 1" doesn't make sense - */ - if (!gtk_tree_path_compare (path, pos_path)) - goto free_paths_and_out; - - if (before) - gtk_tree_path_next (path); - else - gtk_tree_path_prev (path); - - if (!gtk_tree_path_compare (path, pos_path)) - goto free_paths_and_out; - - gtk_tree_path_free (path); - path = NULL; - } - - /* getting destination iters */ - if (before && position) - { - if (gtk_tree_path_get_indices (pos_path)[0] > 0) - { - gtk_tree_path_prev (pos_path); - if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &dst_a, pos_path)) - a = G_SLIST (dst_a.user_data); - else - a = NULL; - gtk_tree_path_next (pos_path); - } - else - a = NULL; - } - else if (before && !position) - a = NULL; - else /* !before */ - { - if (position) - a = G_SLIST (position->user_data); - else - a = NULL; - } - - /* don't try to reorder the iter to it's own position */ - if (a) - { - if (a == iter->user_data) - goto free_paths_and_out; - } - else if (before) - { - if (iter->user_data == store->tail) - goto free_paths_and_out; - } - else - { - if (iter->user_data == store->root) - goto free_paths_and_out; - } - - /* getting the old prev node */ - if (iter->user_data == store->root) - prev = NULL; - else - { - for (i = store->root; i; i = i->next, old_pos++) - if (i->next == iter->user_data) - { - prev = i; - break; - } - - old_pos++; - } - - /* remove node */ - if (!prev) - store->root = G_SLIST (iter->user_data)->next; - else - { - prev->next = G_SLIST (iter->user_data)->next; - if (!prev->next) - store->tail = prev; - } - - /* and reinsert it */ - if (a) - { - tmp = a->next; - - a->next = G_SLIST (iter->user_data); - a->next->next = tmp; - } - else if (!a && !before) - { - tmp = G_SLIST (store->root); - - store->root = G_SLIST (iter->user_data); - G_SLIST (store->root)->next = tmp; - } - else if (!a && before) - { - G_SLIST (store->tail)->next = G_SLIST (iter->user_data); - G_SLIST (iter->user_data)->next = NULL; - } - - /* update tail if needed */ - if (!G_SLIST (iter->user_data)->next) - store->tail = G_SLIST (iter->user_data); - - /* emit signal */ - if (position) - new_pos = gtk_tree_path_get_indices (pos_path)[0]; - else if (before) - new_pos = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) - 1; - else - new_pos = 0; - - if (new_pos > old_pos) - { - if (before && position) - new_pos--; - } - else - { - if (!before && position) - new_pos++; - } - - order = g_new (gint, store->length); - if (new_pos > old_pos) - { - for (j = 0; j < store->length; j++) - if (j < old_pos) - order[j] = j; - else if (j >= old_pos && j < new_pos) - order[j] = j + 1; - else if (j == new_pos) - order[j] = old_pos; - else - order[j] = j; - } - else - { - for (j = 0; j < store->length; j++) - if (j == new_pos) - order[j] = old_pos; - else if (j > new_pos && j <= old_pos) - order[j] = j - 1; - else - order[j] = j; - } - - path = gtk_tree_path_new (); - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), - path, NULL, order); - gtk_tree_path_free (path); - if (position) - gtk_tree_path_free (pos_path); - g_free (order); - - return; - -free_paths_and_out: - if (path) - gtk_tree_path_free (path); - if (pos_path) - gtk_tree_path_free (pos_path); -} - -/** - * gtk_list_store_move_before: - * @store: A #GtkListStore. - * @iter: A #GtkTreeIter. - * @position: 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 - * will be moved to the end of the list. - * - * Since: 2.2 - **/ -void -gtk_list_store_move_before (GtkListStore *store, - GtkTreeIter *iter, - GtkTreeIter *position) -{ - gtk_list_store_move (store, iter, position, TRUE); -} + pos = g_sequence_iter_get_position (position->user_data); + else + pos = -1; + + gtk_list_store_move_to (store, iter, pos); +} /** * 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 @@ -2022,34 +1896,45 @@ gtk_list_store_move_before (GtkListStore *store, **/ void gtk_list_store_move_after (GtkListStore *store, - GtkTreeIter *iter, + GtkTreeIter *iter, GtkTreeIter *position) { - gtk_list_store_move (store, iter, position, FALSE); -} + gint pos; + + g_return_if_fail (GTK_IS_LIST_STORE (store)); + g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); + g_return_if_fail (iter_is_valid (iter, store)); + if (position) + g_return_if_fail (iter_is_valid (position, store)); + if (position) + pos = g_sequence_iter_get_position (position->user_data) + 1; + else + pos = 0; + + gtk_list_store_move_to (store, iter, pos); +} + /* 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; - GSList *el_a; /* Los Angeles? */ - GSList *el_b; + 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); @@ -2058,267 +1943,110 @@ 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; } - el_a = ((SortTuple *) a)->el; - el_b = ((SortTuple *) b)->el; + iter_a.stamp = priv->stamp; + iter_a.user_data = (gpointer)a; + iter_b.stamp = priv->stamp; + iter_b.user_data = (gpointer)b; - iter_a.stamp = list_store->stamp; - iter_a.user_data = el_a; - iter_b.stamp = list_store->stamp; - iter_b.user_data = el_b; + 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; } static void gtk_list_store_sort (GtkListStore *list_store) { - GArray *sort_array; - gint i; + GtkListStorePrivate *priv = list_store->priv; gint *new_order; - GSList *list; GtkTreePath *path; + GHashTable *old_positions; - if (list_store->length <= 1) + if (!GTK_LIST_STORE_IS_SORTED (list_store) || + g_sequence_get_length (priv->seq) <= 1) return; - g_assert (GTK_LIST_STORE_IS_SORTED (list_store)); - - list = G_SLIST (list_store->root); - - sort_array = g_array_sized_new (FALSE, FALSE, - sizeof (SortTuple), - list_store->length); - - for (i = 0; i < list_store->length; i++) - { - SortTuple tuple = {0,}; - - /* If this fails, we are in an inconsistent state. Bad */ - g_return_if_fail (list != NULL); - - tuple.offset = i; - tuple.el = list; - g_array_append_val (sort_array, tuple); - - list = list->next; - } - - g_array_sort_with_data (sort_array, gtk_list_store_compare_func, list_store); + old_positions = save_positions (priv->seq); - for (i = 0; i < list_store->length - 1; i++) - g_array_index (sort_array, SortTuple, i).el->next = - g_array_index (sort_array, SortTuple, i + 1).el; - g_array_index (sort_array, SortTuple, list_store->length - 1).el->next = NULL; - list_store->root = g_array_index (sort_array, SortTuple, 0).el; - list_store->tail = g_array_index (sort_array, SortTuple, list_store->length - 1).el; + g_sequence_sort_iter (priv->seq, gtk_list_store_compare_func, list_store); /* Let the world know about our new order */ - new_order = g_new (gint, list_store->length); - for (i = 0; i < list_store->length; i++) - new_order[i] = g_array_index (sort_array, SortTuple, i).offset; + new_order = generate_order (priv->seq, old_positions); path = gtk_tree_path_new (); gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store), path, NULL, new_order); gtk_tree_path_free (path); g_free (new_order); - g_array_free (sort_array, TRUE); } -static void -gtk_list_store_sort_iter_changed (GtkListStore *list_store, - GtkTreeIter *iter, - gint column) - +static gboolean +iter_is_sorted (GtkListStore *list_store, + GtkTreeIter *iter) { - GSList *prev = NULL; - GSList *next = NULL; - GSList *list = G_SLIST (list_store->root); - GtkTreePath *tmp_path; - GtkTreeIter tmp_iter; - gint cmp_a = 0; - gint cmp_b = 0; - gint i; - gint old_location; - gint new_location; - gint *new_order; - GtkTreeIterCompareFunc func; - gpointer data; - - if (list_store->length < 2) - return; - - tmp_iter.stamp = list_store->stamp; - - if (list_store->sort_column_id != -1) - { - GtkTreeDataSortHeader *header; - header = _gtk_tree_data_list_get_header (list_store->sort_list, - list_store->sort_column_id); - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - func = header->func; - data = header->data; - } - else - { - g_return_if_fail (list_store->default_sort_func != NULL); - func = list_store->default_sort_func; - data = list_store->default_sort_data; - } - - /* If it's the built in function, we don't sort. */ - if (func == gtk_tree_data_list_compare_func && - list_store->sort_column_id != column) - return; - - old_location = 0; - /* First we find the iter, its prev, and its next */ - while (list) - { - if (list == G_SLIST (iter->user_data)) - break; - prev = list; - list = list->next; - old_location++; - } - g_assert (list != NULL); - - next = list->next; - - /* Check the common case, where we don't need to sort it moved. */ - if (prev != NULL) - { - tmp_iter.user_data = prev; - cmp_a = (* func) (GTK_TREE_MODEL (list_store), &tmp_iter, iter, data); - } + GSequenceIter *cmp; - if (next != NULL) + if (!g_sequence_iter_is_begin (iter->user_data)) { - tmp_iter.user_data = next; - cmp_b = (* func) (GTK_TREE_MODEL (list_store), iter, &tmp_iter, data); + cmp = g_sequence_iter_prev (iter->user_data); + if (gtk_list_store_compare_func (cmp, iter->user_data, list_store) > 0) + return FALSE; } - if (list_store->order == GTK_SORT_DESCENDING) + cmp = g_sequence_iter_next (iter->user_data); + if (!g_sequence_iter_is_end (cmp)) { - if (cmp_a < 0) - cmp_a = 1; - else if (cmp_a > 0) - cmp_a = -1; - - if (cmp_b < 0) - cmp_b = 1; - else if (cmp_b > 0) - cmp_b = -1; + if (gtk_list_store_compare_func (iter->user_data, cmp, list_store) > 0) + return FALSE; } + + return TRUE; +} - if (prev == NULL && cmp_b <= 0) - return; - else if (next == NULL && cmp_a <= 0) - return; - else if (prev != NULL && next != NULL && - cmp_a <= 0 && cmp_b <= 0) - return; +static void +gtk_list_store_sort_iter_changed (GtkListStore *list_store, + GtkTreeIter *iter, + gint column) - /* We actually need to sort it */ - /* First, remove the old link. */ +{ + GtkListStorePrivate *priv = list_store->priv; + GtkTreePath *path; - if (prev == NULL) - list_store->root = next; - else - prev->next = next; - if (next == NULL) - list_store->tail = prev; - list->next = NULL; - - /* FIXME: as an optimization, we can potentially start at next */ - prev = NULL; - list = G_SLIST (list_store->root); - new_location = 0; - tmp_iter.user_data = list; - if (list_store->order == GTK_SORT_DESCENDING) - cmp_a = (* func) (GTK_TREE_MODEL (list_store), &tmp_iter, iter, data); - else - cmp_a = (* func) (GTK_TREE_MODEL (list_store), iter, &tmp_iter, data); + 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); - while ((list->next) && (cmp_a > 0)) + if (!iter_is_sorted (list_store, iter)) { - prev = list; - list = list->next; - new_location++; - tmp_iter.user_data = list; - if (list_store->order == GTK_SORT_DESCENDING) - cmp_a = (* func) (GTK_TREE_MODEL (list_store), &tmp_iter, iter, data); - else - cmp_a = (* func) (GTK_TREE_MODEL (list_store), iter, &tmp_iter, data); - } + GHashTable *old_positions; + gint *order; - if ((!list->next) && (cmp_a > 0)) - { - new_location++; - list->next = G_SLIST (iter->user_data); - list_store->tail = list->next; - } - else if (prev) - { - prev->next = G_SLIST (iter->user_data); - G_SLIST (iter->user_data)->next = list; - } - else - { - G_SLIST (iter->user_data)->next = G_SLIST (list_store->root); - list_store->root = G_SLIST (iter->user_data); + 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); + gtk_tree_path_free (path); + g_free (order); } - - /* Emit the reordered signal. */ - new_order = g_new (int, list_store->length); - if (old_location < new_location) - for (i = 0; i < list_store->length; i++) - { - if (i < old_location || - i > new_location) - new_order[i] = i; - else if (i >= old_location && - i < new_location) - new_order[i] = i + 1; - else if (i == new_location) - new_order[i] = old_location; - } - else - for (i = 0; i < list_store->length; i++) - { - if (i < new_location || - i > old_location) - new_order[i] = i; - else if (i > new_location && - i <= old_location) - new_order[i] = i - 1; - else if (i == new_location) - new_order[i] = old_location; - } - - tmp_path = gtk_tree_path_new (); - tmp_iter.user_data = NULL; - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store), - tmp_path, NULL, - new_order); - gtk_tree_path_free (tmp_path); - g_free (new_order); } static gboolean @@ -2326,17 +2054,18 @@ 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; - g_return_val_if_fail (GTK_IS_LIST_STORE (sortable), FALSE); + if (sort_column_id) + * sort_column_id = priv->sort_column_id; + if (order) + * order = priv->order; - 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 || + priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) return FALSE; - if (sort_column_id) - * sort_column_id = list_store->sort_column_id; - if (order) - * order = list_store->order; return TRUE; } @@ -2345,32 +2074,35 @@ gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable, gint sort_column_id, GtkSortType order) { - GtkListStore *list_store = (GtkListStore *) sortable; - - g_return_if_fail (GTK_IS_LIST_STORE (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_DEFAULT_SORT_COLUMN_ID) + if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) { - GtkTreeDataSortHeader *header = NULL; + if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + { + GtkTreeDataSortHeader *header = NULL; - header = _gtk_tree_data_list_get_header (list_store->sort_list, sort_column_id); + header = _gtk_tree_data_list_get_header (priv->sort_list, + sort_column_id); - /* We want to make sure that we have a function */ - g_return_if_fail (header != NULL); - g_return_if_fail (header->func != NULL); - } - else - { - g_return_if_fail (list_store->default_sort_func != NULL); + /* We want to make sure that we have a function */ + g_return_if_fail (header != NULL); + g_return_if_fail (header->func != NULL); + } + else + { + 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); @@ -2382,82 +2114,562 @@ 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; - GtkTreeDataSortHeader *header = NULL; - GList *list; - - g_return_if_fail (GTK_IS_LIST_STORE (sortable)); - g_return_if_fail (func != NULL); + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; - for (list = list_store->sort_list; list; list = list->next) - { - GtkTreeDataSortHeader *list_header; + priv->sort_list = _gtk_tree_data_list_set_header (priv->sort_list, + sort_column_id, + func, data, destroy); - list_header = (GtkTreeDataSortHeader*) list->data; - if (list_header->sort_column_id == sort_column_id) - { - header = list_header; - break; - } - } + if (priv->sort_column_id == sort_column_id) + gtk_list_store_sort (list_store); +} - if (header == NULL) - { - header = g_new0 (GtkTreeDataSortHeader, 1); - header->sort_column_id = sort_column_id; - list_store->sort_list = g_list_append (list_store->sort_list, header); - } +static void +gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; - if (header->destroy) + if (priv->default_sort_destroy) { - GtkDestroyNotify d = header->destroy; + GDestroyNotify d = priv->default_sort_destroy; - header->destroy = NULL; - d (header->data); + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); } - header->func = func; - header->data = data; - header->destroy = destroy; + priv->default_sort_func = func; + priv->default_sort_data = data; + priv->default_sort_destroy = destroy; - if (list_store->sort_column_id == 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 = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; + + return (priv->default_sort_func != NULL); +} + + +/** + * gtk_list_store_insert_with_values: + * @list_store: A #GtkListStore + * @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. + * + * 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, ...); + * ]| + * 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, + * gtk_list_store_insert_with_values() should generally be preferred when + * inserting rows in a sorted list store. + * + * Since: 2.6 + */ +void +gtk_list_store_insert_with_values (GtkListStore *list_store, + GtkTreeIter *iter, + gint position, + ...) +{ + GtkListStorePrivate *priv; + GtkTreePath *path; + GSequence *seq; + GSequenceIter *ptr; + GtkTreeIter tmp_iter; + gint length; + gboolean changed = FALSE; + gboolean maybe_need_sort = FALSE; + va_list var_args; + + /* 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; + + priv->columns_dirty = TRUE; + + seq = priv->seq; + + length = g_sequence_get_length (seq); + if (position > length || position < 0) + position = length; + + ptr = g_sequence_get_iter_at_pos (seq, position); + ptr = g_sequence_insert_before (ptr, NULL); + + iter->stamp = priv->stamp; + iter->user_data = ptr; + + g_assert (iter_is_valid (iter, list_store)); + + priv->length++; + + va_start (var_args, position); + gtk_list_store_set_valist_internal (list_store, iter, + &changed, &maybe_need_sort, + var_args); + va_end (var_args); + + /* Don't emit rows_reordered here */ + if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (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); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); + gtk_tree_path_free (path); +} + + +/** + * gtk_list_store_insert_with_valuesv: + * @list_store: A #GtkListStore + * @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 + * takes the columns and values as two arrays, instead of + * varargs. This function is mainly intended for + * language-bindings. + * + * Since: 2.6 + */ +void +gtk_list_store_insert_with_valuesv (GtkListStore *list_store, + GtkTreeIter *iter, + gint position, + gint *columns, + GValue *values, + gint n_values) +{ + GtkListStorePrivate *priv; + GtkTreePath *path; + GSequence *seq; + GSequenceIter *ptr; + GtkTreeIter tmp_iter; + gint length; + gboolean changed = FALSE; + gboolean maybe_need_sort = FALSE; + + /* 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; + + priv->columns_dirty = TRUE; + + seq = priv->seq; + + length = g_sequence_get_length (seq); + if (position > length || position < 0) + position = length; + + ptr = g_sequence_get_iter_at_pos (seq, position); + ptr = g_sequence_insert_before (ptr, NULL); + + iter->stamp = priv->stamp; + iter->user_data = ptr; + + g_assert (iter_is_valid (iter, list_store)); + + priv->length++; + + 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)) + 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); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter); + gtk_tree_path_free (path); +} + +/* 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 -gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GtkDestroyNotify destroy) +list_store_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) { - GtkListStore *list_store = (GtkListStore *) sortable; + guint i; + SubParserData *data = (SubParserData*)user_data; - g_return_if_fail (GTK_IS_LIST_STORE (sortable)); + if (strcmp (element_name, "col") == 0) + { + int i, id = -1; + gchar *context = NULL; + gboolean translatable = FALSE; + ColInfo *info; - if (list_store->default_sort_destroy) + 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) { - GtkDestroyNotify d = list_store->default_sort_destroy; + 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)); - list_store->default_sort_destroy = NULL; - d (list_store->default_sort_data); + 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; - list_store->default_sort_func = func; - list_store->default_sort_data = data; - list_store->default_sort_destroy = destroy; + i = data->row_column - 1; + info = data->columns[i]; - if (list_store->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - gtk_list_store_sort (list_store); + 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_has_default_sort_func (GtkTreeSortable *sortable) +gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) { - GtkListStore *list_store = (GtkListStore *) sortable; + SubParserData *parser_data; - g_return_val_if_fail (GTK_IS_LIST_STORE (sortable), FALSE); + if (child) + return FALSE; - return (list_store->default_sort_func != NULL); + 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); }