X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkliststore.c;h=467bac0f15dc4da7b0a98336a01fe22f63952e7c;hb=aaedf5a35219b034a244730564b8fdf2b7d32540;hp=9e9d48cf9443e93c498c598e2d50fed1ba517a28;hpb=38df3fec7759a655a820d9df6b2a08317cb1c933;p=~andy%2Fgtk diff --git a/gtk/gtkliststore.c b/gtk/gtkliststore.c index 9e9d48cf9..467bac0f1 100644 --- a/gtk/gtkliststore.c +++ b/gtk/gtkliststore.c @@ -12,30 +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 "gtkalias.h" #include "gtktreemodel.h" #include "gtkliststore.h" #include "gtktreedatalist.h" #include "gtktreednd.h" -#include "gtksequence.h" +#include "gtkintl.h" +#include "gtkbuildable.h" +#include "gtkbuilderprivate.h" -#define GTK_LIST_STORE_IS_SORTED(list) (GTK_LIST_STORE (list)->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) -#define VALID_ITER(iter, list_store) ((iter)!= NULL && (iter)->user_data != NULL && list_store->stamp == (iter)->stamp && !_gtk_sequence_ptr_is_end ((iter)->user_data) && _gtk_sequence_ptr_get_sequence ((iter)->user_data) == list_store->seq) -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); @@ -52,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); @@ -74,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, @@ -106,93 +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 GObjectClass *parent_class = NULL; +/* 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)) -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); - } - - 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 @@ -205,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; @@ -237,32 +373,57 @@ gtk_list_store_sortable_init (GtkTreeSortableIface *iface) iface->has_default_sort_func = gtk_list_store_has_default_sort_func; } +void +gtk_list_store_buildable_init (GtkBuildableIface *iface) +{ + iface->custom_tag_start = gtk_list_store_buildable_custom_tag_start; + iface->custom_tag_end = gtk_list_store_buildable_custom_tag_end; +} + static void gtk_list_store_init (GtkListStore *list_store) { - list_store->seq = _gtk_sequence_new (NULL); - list_store->sort_list = NULL; - list_store->stamp = g_random_int (); - list_store->sort_column_id = -2; - list_store->columns_dirty = FALSE; - list_store->length = 0; + GtkListStorePrivate *priv; + + list_store->priv = G_TYPE_INSTANCE_GET_PRIVATE (list_store, + GTK_TYPE_LIST_STORE, + GtkListStorePrivate); + priv = list_store->priv; + + priv->seq = g_sequence_new (NULL); + priv->sort_list = NULL; + priv->stamp = g_random_int (); + priv->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID; + priv->columns_dirty = FALSE; + priv->length = 0; +} + +static gboolean +iter_is_valid (GtkTreeIter *iter, + GtkListStore *list_store) +{ + return iter != NULL && + iter->user_data != NULL && + list_store->priv->stamp == iter->stamp && + !g_sequence_iter_is_end (iter->user_data) && + g_sequence_iter_get_sequence (iter->user_data) == list_store->priv->seq; } /** * gtk_list_store_new: * @n_columns: number of columns in the list store - * @Varargs: all #GType types for the columns, from first to last + * @...: all #GType types for the columns, from first to last * * Creates a new list store as with @n_columns columns each of the types passed - * in. Note that only types derived from standard GObject fundamental types - * are supported. + * in. Note that only types derived from standard GObject fundamental types + * are supported. * - * As an example, gtk_tree_store_new (3, G_TYPE_INT, G_TYPE_STRING, + * As an example, gtk_list_store_new (3, G_TYPE_INT, G_TYPE_STRING, * GDK_TYPE_PIXBUF); will create a new #GtkListStore with three columns, of type * int, string and #GdkPixbuf respectively. * * Return value: a new #GtkListStore - **/ + */ GtkListStore * gtk_list_store_new (gint n_columns, ...) @@ -282,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); } @@ -301,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, @@ -323,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; } @@ -339,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 @@ -351,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]); @@ -372,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); - - list_store->sort_list = _gtk_tree_data_list_header_new (n_columns, list_store->column_headers); + 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->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 @@ -406,78 +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; - _gtk_sequence_foreach (list_store->seq, - (GFunc) _gtk_tree_data_list_free, list_store->column_headers); + g_sequence_foreach (priv->seq, + (GFunc) _gtk_tree_data_list_free, priv->column_headers); - _gtk_sequence_free (list_store->seq); + g_sequence_free (priv->seq); - _gtk_tree_data_list_header_free (list_store->sort_list); - g_free (list_store->column_headers); - - if (list_store->default_sort_destroy) + _gtk_tree_data_list_header_free (priv->sort_list); + g_free (priv->column_headers); + + if (priv->default_sort_destroy) { - GtkDestroyNotify d = list_store->default_sort_destroy; + GDestroyNotify d = priv->default_sort_destroy; - list_store->default_sort_destroy = NULL; - d (list_store->default_sort_data); - list_store->default_sort_data = NULL; + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); + priv->default_sort_data = NULL; } - /* must chain up */ - (* 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; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; - g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), 0); + priv->columns_dirty = TRUE; - list_store->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 @@ -485,24 +633,25 @@ gtk_list_store_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) { - GtkListStore *list_store = (GtkListStore *) tree_model; - GtkSequence *seq; + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + GSequence *seq; gint i; - 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; - seq = list_store->seq; - i = gtk_tree_path_get_indices (path)[0]; - if (i >= _gtk_sequence_get_length (seq)) - return FALSE; + if (i >= g_sequence_get_length (seq)) + { + iter->stamp = 0; + return FALSE; + } - iter->stamp = list_store->stamp; - iter->user_data = _gtk_sequence_get_ptr_at_pos (seq, i); + iter->stamp = priv->stamp; + iter->user_data = g_sequence_get_iter_at_pos (seq, i); return TRUE; } @@ -511,16 +660,17 @@ static GtkTreePath * gtk_list_store_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter) { + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; GtkTreePath *path; - g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), NULL); - g_return_val_if_fail (iter->stamp == GTK_LIST_STORE (tree_model)->stamp, NULL); + g_return_val_if_fail (iter->stamp == priv->stamp, NULL); - if (_gtk_sequence_ptr_is_end (iter->user_data)) + if (g_sequence_iter_is_end (iter->user_data)) return NULL; path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, _gtk_sequence_ptr_get_position (iter->user_data)); + gtk_tree_path_append_index (path, g_sequence_iter_get_position (iter->user_data)); return path; } @@ -531,24 +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); - g_return_if_fail (VALID_ITER (iter, GTK_LIST_STORE(tree_model))); + g_return_if_fail (column < priv->n_columns); + g_return_if_fail (iter_is_valid (iter, list_store)); - list = _gtk_sequence_ptr_get_data (iter->user_data); + list = g_sequence_get (iter->user_data); while (tmp_column-- > 0 && list) list = list->next; if (list == NULL) - g_value_init (value, 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); } @@ -556,11 +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); - iter->user_data = _gtk_sequence_ptr_next (iter->user_data); + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + gboolean retval; + + g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); + iter->user_data = g_sequence_iter_next (iter->user_data); + + retval = g_sequence_iter_is_end (iter->user_data); + if (retval) + iter->stamp = 0; + + return !retval; +} + +static gboolean +gtk_list_store_iter_previous (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkListStore *list_store = GTK_LIST_STORE (tree_model); + GtkListStorePrivate *priv = list_store->priv; + + g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); + + if (g_sequence_iter_is_begin (iter->user_data)) + { + iter->stamp = 0; + return FALSE; + } - return !_gtk_sequence_ptr_is_end (iter->user_data); + iter->user_data = g_sequence_iter_prev (iter->user_data); + + return TRUE; } static gboolean @@ -568,22 +745,27 @@ gtk_list_store_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) { - GtkListStore *list_store; - + GtkListStore *list_store = (GtkListStore *) tree_model; + GtkListStorePrivate *priv = list_store->priv; + /* this is a list, nodes have no children */ if (parent) - return FALSE; - - list_store = GTK_LIST_STORE (tree_model); + { + iter->stamp = 0; + return FALSE; + } - if (_gtk_sequence_get_length (list_store->seq) == 0) + if (g_sequence_get_length (priv->seq) > 0) { - iter->stamp = list_store->stamp; - iter->user_data = _gtk_sequence_get_begin_ptr (list_store->seq); + iter->stamp = priv->stamp; + iter->user_data = g_sequence_get_begin_iter (priv->seq); return TRUE; } else - return FALSE; + { + iter->stamp = 0; + return FALSE; + } } static gboolean @@ -597,16 +779,14 @@ static gint gtk_list_store_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter) { - GtkListStore *store; - - 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; - store = GTK_LIST_STORE (tree_model); - if (iter == NULL) - return _gtk_sequence_get_length (store->seq); + return g_sequence_get_length (priv->seq); + + g_return_val_if_fail (priv->stamp == iter->stamp, -1); - g_return_val_if_fail (store->stamp == iter->stamp, -1); return 0; } @@ -616,23 +796,23 @@ gtk_list_store_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *parent, gint n) { - GtkSequencePtr child; - GtkListStore *store; + 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; - store = GTK_LIST_STORE (tree_model); - if (parent) return FALSE; - child = _gtk_sequence_get_ptr_at_pos (store->seq, n); + child = g_sequence_get_iter_at_pos (priv->seq, n); - if (_gtk_sequence_ptr_is_end (child)) + if (g_sequence_iter_is_end (child)) return FALSE; - iter->stamp = store->stamp; + iter->stamp = priv->stamp; iter->user_data = child; + return TRUE; } @@ -641,6 +821,7 @@ gtk_list_store_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) { + iter->stamp = 0; return FALSE; } @@ -651,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 = _gtk_sequence_ptr_get_data (iter->user_data); + prev = list = g_sequence_get (iter->user_data); while (list != NULL) { @@ -709,10 +887,10 @@ gtk_list_store_real_set_value (GtkListStore *list_store, list = list->next; } - if (_gtk_sequence_ptr_get_data (iter->user_data) == NULL) + if (g_sequence_get (iter->user_data) == NULL) { list = _gtk_tree_data_list_alloc(); - _gtk_sequence_set (iter->user_data, list); + g_sequence_set (iter->user_data, list); list->next = NULL; } else @@ -763,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; } } + 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; + *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); @@ -848,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; - + *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; + 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); } @@ -880,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, @@ -888,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, @@ -896,10 +1190,6 @@ 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); @@ -920,33 +1210,36 @@ gboolean gtk_list_store_remove (GtkListStore *list_store, GtkTreeIter *iter) { + GtkListStorePrivate *priv; GtkTreePath *path; - GtkSequencePtr ptr, next; + GSequenceIter *ptr, *next; g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE); - g_return_val_if_fail (VALID_ITER (iter, list_store), FALSE); + g_return_val_if_fail (iter_is_valid (iter, list_store), FALSE); + + priv = list_store->priv; path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); ptr = iter->user_data; - next = _gtk_sequence_ptr_next (ptr); + next = g_sequence_iter_next (ptr); - _gtk_tree_data_list_free (_gtk_sequence_ptr_get_data (ptr), list_store->column_headers); - _gtk_sequence_remove (iter->user_data); + _gtk_tree_data_list_free (g_sequence_get (ptr), priv->column_headers); + g_sequence_remove (iter->user_data); - list_store->length--; + priv->length--; gtk_tree_model_row_deleted (GTK_TREE_MODEL (list_store), path); gtk_tree_path_free (path); - if (_gtk_sequence_ptr_is_end (next)) + if (g_sequence_iter_is_end (next)) { iter->stamp = 0; return FALSE; } else { - iter->stamp = list_store->stamp; + iter->stamp = priv->stamp; iter->user_data = next; return TRUE; } @@ -955,14 +1248,14 @@ gtk_list_store_remove (GtkListStore *list_store, /** * gtk_list_store_insert: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the new row - * @position: position to insert the new row + * @iter: (out): An unset #GtkTreeIter to set to the new row + * @position: position to insert the new row, or -1 for last * * Creates a new row at @position. @iter will be changed to point to this new - * row. If @position is larger than the number of rows on the list, then the - * new row will be appended to the list. The row will be empty 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 @@ -970,32 +1263,34 @@ gtk_list_store_insert (GtkListStore *list_store, GtkTreeIter *iter, gint position) { + GtkListStorePrivate *priv; GtkTreePath *path; - GtkSequence *seq; - GtkSequencePtr ptr; + GSequence *seq; + GSequenceIter *ptr; gint length; g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); - g_return_if_fail (position >= 0); - list_store->columns_dirty = TRUE; + priv = list_store->priv; + + priv->columns_dirty = TRUE; - seq = list_store->seq; + seq = priv->seq; - length = _gtk_sequence_get_length (seq); - if (position > length) + length = g_sequence_get_length (seq); + if (position > length || position < 0) position = length; - ptr = _gtk_sequence_get_ptr_at_pos (seq, position); - ptr = _gtk_sequence_insert (ptr, NULL); + ptr = g_sequence_get_iter_at_pos (seq, position); + ptr = g_sequence_insert_before (ptr, NULL); - iter->stamp = list_store->stamp; + iter->stamp = priv->stamp; iter->user_data = ptr; - g_assert (VALID_ITER (iter, list_store)); + g_assert (iter_is_valid (iter, list_store)); - list_store->length++; + priv->length++; path = gtk_tree_path_new (); gtk_tree_path_append_index (path, position); @@ -1006,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 @@ -1020,26 +1315,30 @@ gtk_list_store_insert_before (GtkListStore *list_store, GtkTreeIter *iter, GtkTreeIter *sibling) { - GtkSequencePtr after; + GtkListStorePrivate *priv; + GSequenceIter *after; g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); + + priv = list_store->priv; + if (sibling) - g_return_if_fail (VALID_ITER (sibling, list_store)); + g_return_if_fail (iter_is_valid (sibling, list_store)); if (!sibling) - after = _gtk_sequence_get_end_ptr (list_store->seq); + after = g_sequence_get_end_iter (priv->seq); else after = sibling->user_data; - gtk_list_store_insert (list_store, iter, _gtk_sequence_ptr_get_position (after)); + gtk_list_store_insert (list_store, iter, g_sequence_iter_get_position (after)); } /** * gtk_list_store_insert_after: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the new row - * @sibling: A valid #GtkTreeIter, or %NULL + * @iter: (out): An unset #GtkTreeIter to set to the new row + * @sibling: (allow-none): A valid #GtkTreeIter, or %NULL * * Inserts a new row after @sibling. If @sibling is %NULL, then the row will be * prepended to the beginning of the list. @iter will be changed to point to @@ -1052,25 +1351,29 @@ gtk_list_store_insert_after (GtkListStore *list_store, GtkTreeIter *iter, GtkTreeIter *sibling) { - GtkSequencePtr after; + GtkListStorePrivate *priv; + GSequenceIter *after; g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); + + priv = list_store->priv; + if (sibling) - g_return_if_fail (VALID_ITER (sibling, list_store)); + g_return_if_fail (iter_is_valid (sibling, list_store)); if (!sibling) - after = _gtk_sequence_get_begin_ptr (list_store->seq); + after = g_sequence_get_begin_iter (priv->seq); else - after = _gtk_sequence_ptr_next (sibling->user_data); + after = g_sequence_iter_next (sibling->user_data); - gtk_list_store_insert (list_store, iter, _gtk_sequence_ptr_get_position (after)); + gtk_list_store_insert (list_store, iter, g_sequence_iter_get_position (after)); } /** * gtk_list_store_prepend: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the prepend row + * @iter: (out): An unset #GtkTreeIter to set to the prepend row * * Prepends a new row to @list_store. @iter will be changed to point to this new * row. The row will be empty after this function is called. To fill in @@ -1090,7 +1393,7 @@ gtk_list_store_prepend (GtkListStore *list_store, /** * gtk_list_store_append: * @list_store: A #GtkListStore - * @iter: An unset #GtkTreeIter to set to the appended row + * @iter: (out): An unset #GtkTreeIter to set to the appended row * * Appends a new row to @list_store. @iter will be changed to point to this new * row. The row will be empty after this function is called. To fill in @@ -1104,7 +1407,19 @@ gtk_list_store_append (GtkListStore *list_store, g_return_if_fail (GTK_IS_LIST_STORE (list_store)); g_return_if_fail (iter != NULL); - gtk_list_store_insert (list_store, iter, _gtk_sequence_get_length (list_store->seq)); + gtk_list_store_insert (list_store, iter, -1); +} + +static void +gtk_list_store_increment_stamp (GtkListStore *list_store) +{ + GtkListStorePrivate *priv = list_store->priv; + + do + { + priv->stamp++; + } + while (priv->stamp == 0); } /** @@ -1117,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 (_gtk_sequence_get_length (list_store->seq) > 0) + priv = list_store->priv; + + while (g_sequence_get_length (priv->seq) > 0) { - iter.stamp = list_store->stamp; - iter.user_data = _gtk_sequence_get_begin_ptr (list_store->seq); + iter.stamp = priv->stamp; + iter.user_data = g_sequence_get_begin_iter (priv->seq); gtk_list_store_remove (list_store, &iter); } + + gtk_list_store_increment_stamp (list_store); } /** @@ -1133,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. * @@ -1149,13 +1470,7 @@ gtk_list_store_iter_is_valid (GtkListStore *list_store, g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE); g_return_val_if_fail (iter != NULL, FALSE); - if (!VALID_ITER (iter, list_store)) - return FALSE; - - if (_gtk_sequence_ptr_get_sequence (iter->user_data) != list_store->seq) - return FALSE; - - return TRUE; + return iter_is_valid (iter, list_store); } static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source, @@ -1169,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)) { @@ -1186,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 @@ -1213,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) && @@ -1234,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)) { @@ -1255,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; @@ -1271,7 +1579,7 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, */ if (retval) { - GtkTreeDataList *dl = _gtk_sequence_ptr_get_data (src_iter.user_data); + GtkTreeDataList *dl = g_sequence_get (src_iter.user_data); GtkTreeDataList *copy_head = NULL; GtkTreeDataList *copy_prev = NULL; GtkTreeDataList *copy_iter = NULL; @@ -1282,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; @@ -1296,8 +1604,8 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, ++col; } - dest_iter.stamp = list_store->stamp; - _gtk_sequence_set (dest_iter.user_data, copy_head); + dest_iter.stamp = priv->stamp; + g_sequence_set (dest_iter.user_data, copy_head); path = gtk_list_store_get_path (tree_model, &dest_iter); gtk_tree_model_row_changed (tree_model, path, &dest_iter); @@ -1329,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; @@ -1350,7 +1656,7 @@ gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, indices = gtk_tree_path_get_indices (dest_path); - if (indices[0] <= _gtk_sequence_get_length (GTK_LIST_STORE (drag_dest)->seq)) + if (indices[0] <= g_sequence_get_length (GTK_LIST_STORE (drag_dest)->priv->seq)) retval = TRUE; out: @@ -1364,9 +1670,9 @@ gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, /* Reordering */ static gint -gtk_list_store_reorder_func (gconstpointer a, - gconstpointer b, - gpointer user_data) +gtk_list_store_reorder_func (GSequenceIter *a, + GSequenceIter *b, + gpointer user_data) { GHashTable *new_positions = user_data; gint apos = GPOINTER_TO_INT (g_hash_table_lookup (new_positions, a)); @@ -1382,9 +1688,10 @@ gtk_list_store_reorder_func (gconstpointer a, /** * gtk_list_store_reorder: * @store: A #GtkListStore. - * @new_order: an array of integers mapping the new position of each child - * to its old position before the re-ordering, - * i.e. @new_order[newpos] = oldpos. + * @new_order: (array zero-terminated=1): an array of integers mapping the new + * position of each child to its old position before the re-ordering, + * i.e. @new_order[newpos] = oldpos. It must have + * exactly as many items as the list store's length. * * Reorders @store to follow the order indicated by @new_order. Note that * this function only works with unsorted stores. @@ -1395,27 +1702,36 @@ void gtk_list_store_reorder (GtkListStore *store, gint *new_order) { + GtkListStorePrivate *priv; gint i; GtkTreePath *path; GHashTable *new_positions; - GtkSequencePtr ptr; - + GSequenceIter *ptr; + gint *order; + g_return_if_fail (GTK_IS_LIST_STORE (store)); g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); g_return_if_fail (new_order != NULL); + priv = store->priv; + + order = g_new (gint, g_sequence_get_length (priv->seq)); + for (i = 0; i < g_sequence_get_length (priv->seq); i++) + order[new_order[i]] = i; + new_positions = g_hash_table_new (g_direct_hash, g_direct_equal); - ptr = _gtk_sequence_get_begin_ptr (store->seq); + ptr = g_sequence_get_begin_iter (priv->seq); i = 0; - while (ptr) + while (!g_sequence_iter_is_end (ptr)) { - g_hash_table_insert (new_positions, ptr, GINT_TO_POINTER (new_order[i++])); + g_hash_table_insert (new_positions, ptr, GINT_TO_POINTER (order[i++])); - ptr = _gtk_sequence_ptr_next (ptr); + ptr = g_sequence_iter_next (ptr); } + g_free (order); - _gtk_sequence_sort (store->seq, gtk_list_store_reorder_func, new_positions); + g_sequence_sort_iter (priv->seq, gtk_list_store_reorder_func, new_positions); g_hash_table_destroy (new_positions); @@ -1427,37 +1743,37 @@ gtk_list_store_reorder (GtkListStore *store, } static GHashTable * -save_positions (GtkSequence *seq) +save_positions (GSequence *seq) { GHashTable *positions = g_hash_table_new (g_direct_hash, g_direct_equal); - GtkSequencePtr ptr; + GSequenceIter *ptr; - ptr = _gtk_sequence_get_begin_ptr (seq); - while (!_gtk_sequence_ptr_is_end (ptr)) + ptr = g_sequence_get_begin_iter (seq); + while (!g_sequence_iter_is_end (ptr)) { g_hash_table_insert (positions, ptr, - GINT_TO_POINTER (_gtk_sequence_ptr_get_position (ptr))); - ptr = _gtk_sequence_ptr_next (ptr); + GINT_TO_POINTER (g_sequence_iter_get_position (ptr))); + ptr = g_sequence_iter_next (ptr); } return positions; } static int * -generate_order (GtkSequence *seq, +generate_order (GSequence *seq, GHashTable *old_positions) { - GtkSequencePtr ptr; - int *order = g_new (int, _gtk_sequence_get_length (seq)); + GSequenceIter *ptr; + int *order = g_new (int, g_sequence_get_length (seq)); int i; i = 0; - ptr = _gtk_sequence_get_begin_ptr (seq); - while (!_gtk_sequence_ptr_is_end (ptr)) + ptr = g_sequence_get_begin_iter (seq); + while (!g_sequence_iter_is_end (ptr)) { int old_pos = GPOINTER_TO_INT (g_hash_table_lookup (old_positions, ptr)); - order[old_pos] = i++; - ptr = _gtk_sequence_ptr_next (ptr); + order[i++] = old_pos; + ptr = g_sequence_iter_next (ptr); } g_hash_table_destroy (old_positions); @@ -1481,23 +1797,26 @@ gtk_list_store_swap (GtkListStore *store, GtkTreeIter *a, GtkTreeIter *b) { + GtkListStorePrivate *priv; GHashTable *old_positions; gint *order; GtkTreePath *path; g_return_if_fail (GTK_IS_LIST_STORE (store)); g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); - g_return_if_fail (VALID_ITER (a, store)); - g_return_if_fail (VALID_ITER (b, store)); + g_return_if_fail (iter_is_valid (a, store)); + g_return_if_fail (iter_is_valid (b, store)); + + priv = store->priv; if (a->user_data == b->user_data) return; - old_positions = save_positions (store->seq); + old_positions = save_positions (priv->seq); - _gtk_sequence_swap (a->user_data, b->user_data); + g_sequence_swap (a->user_data, b->user_data); - order = generate_order (store->seq, old_positions); + order = generate_order (priv->seq, old_positions); path = gtk_tree_path_new (); gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), @@ -1512,16 +1831,17 @@ gtk_list_store_move_to (GtkListStore *store, GtkTreeIter *iter, gint new_pos) { + GtkListStorePrivate *priv = store->priv; GHashTable *old_positions; GtkTreePath *path; gint *order; - - old_positions = save_positions (store->seq); - - _gtk_sequence_move (iter->user_data, _gtk_sequence_get_ptr_at_pos (store->seq, new_pos)); - order = generate_order (store->seq, old_positions); - + old_positions = save_positions (priv->seq); + + g_sequence_move (iter->user_data, g_sequence_get_iter_at_pos (priv->seq, new_pos)); + + order = generate_order (priv->seq, old_positions); + path = gtk_tree_path_new (); gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store), path, NULL, order); @@ -1533,7 +1853,7 @@ gtk_list_store_move_to (GtkListStore *store, * gtk_list_store_move_before: * @store: A #GtkListStore. * @iter: A #GtkTreeIter. - * @position: A #GtkTreeIter, or %NULL. + * @position: (allow-none): A #GtkTreeIter, or %NULL. * * Moves @iter in @store to the position before @position. Note that this * function only works with unsorted stores. If @position is %NULL, @iter @@ -1550,12 +1870,12 @@ gtk_list_store_move_before (GtkListStore *store, g_return_if_fail (GTK_IS_LIST_STORE (store)); g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); - g_return_if_fail (VALID_ITER (iter, store)); + g_return_if_fail (iter_is_valid (iter, store)); if (position) - g_return_if_fail (VALID_ITER (position, store)); + g_return_if_fail (iter_is_valid (position, store)); if (position) - pos = _gtk_sequence_ptr_get_position (position->user_data); + pos = g_sequence_iter_get_position (position->user_data); else pos = -1; @@ -1566,7 +1886,7 @@ gtk_list_store_move_before (GtkListStore *store, * gtk_list_store_move_after: * @store: A #GtkListStore. * @iter: A #GtkTreeIter. - * @position: A #GtkTreeIter or %NULL. + * @position: (allow-none): A #GtkTreeIter or %NULL. * * Moves @iter in @store to the position after @position. Note that this * function only works with unsorted stores. If @position is %NULL, @iter @@ -1583,12 +1903,12 @@ gtk_list_store_move_after (GtkListStore *store, g_return_if_fail (GTK_IS_LIST_STORE (store)); g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store)); - g_return_if_fail (VALID_ITER (iter, store)); + g_return_if_fail (iter_is_valid (iter, store)); if (position) - g_return_if_fail (VALID_ITER (position, store)); + g_return_if_fail (iter_is_valid (position, store)); if (position) - pos = _gtk_sequence_ptr_get_position (position->user_data) + 1; + pos = g_sequence_iter_get_position (position->user_data) + 1; else pos = 0; @@ -1597,23 +1917,24 @@ gtk_list_store_move_after (GtkListStore *store, /* Sorting */ static gint -gtk_list_store_compare_func (gconstpointer a, - gconstpointer b, +gtk_list_store_compare_func (GSequenceIter *a, + GSequenceIter *b, gpointer user_data) { GtkListStore *list_store = user_data; + GtkListStorePrivate *priv = list_store->priv; GtkTreeIter iter_a; GtkTreeIter iter_b; gint retval; GtkTreeIterCompareFunc func; gpointer data; - if (list_store->sort_column_id != -1) + if (priv->sort_column_id != -1) { GtkTreeDataSortHeader *header; - header = _gtk_tree_data_list_get_header (list_store->sort_list, - list_store->sort_column_id); + header = _gtk_tree_data_list_get_header (priv->sort_list, + priv->sort_column_id); g_return_val_if_fail (header != NULL, 0); g_return_val_if_fail (header->func != NULL, 0); @@ -1622,27 +1943,27 @@ gtk_list_store_compare_func (gconstpointer a, } else { - g_return_val_if_fail (list_store->default_sort_func != NULL, 0); - func = list_store->default_sort_func; - data = list_store->default_sort_data; + g_return_val_if_fail (priv->default_sort_func != NULL, 0); + func = priv->default_sort_func; + data = priv->default_sort_data; } - iter_a.stamp = list_store->stamp; + iter_a.stamp = priv->stamp; iter_a.user_data = (gpointer)a; - iter_b.stamp = list_store->stamp; + iter_b.stamp = priv->stamp; iter_b.user_data = (gpointer)b; - g_assert (VALID_ITER (&iter_a, list_store)); - g_assert (VALID_ITER (&iter_b, list_store)); - + g_assert (iter_is_valid (&iter_a, list_store)); + g_assert (iter_is_valid (&iter_b, list_store)); + retval = (* func) (GTK_TREE_MODEL (list_store), &iter_a, &iter_b, data); - if (list_store->order == GTK_SORT_DESCENDING) + if (priv->order == GTK_SORT_DESCENDING) { if (retval > 0) - retval = -1; + retval = -1; else if (retval < 0) - retval = 1; + retval = 1; } return retval; @@ -1651,20 +1972,21 @@ gtk_list_store_compare_func (gconstpointer a, static void gtk_list_store_sort (GtkListStore *list_store) { + GtkListStorePrivate *priv = list_store->priv; gint *new_order; GtkTreePath *path; GHashTable *old_positions; if (!GTK_LIST_STORE_IS_SORTED (list_store) || - _gtk_sequence_get_length (list_store->seq) <= 1) + g_sequence_get_length (priv->seq) <= 1) return; - old_positions = save_positions (list_store->seq); + old_positions = save_positions (priv->seq); - _gtk_sequence_sort (list_store->seq, gtk_list_store_compare_func, list_store); + g_sequence_sort_iter (priv->seq, gtk_list_store_compare_func, list_store); /* Let the world know about our new order */ - new_order = generate_order (list_store->seq, old_positions); + new_order = generate_order (priv->seq, old_positions); path = gtk_tree_path_new (); gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store), @@ -1673,21 +1995,58 @@ gtk_list_store_sort (GtkListStore *list_store) g_free (new_order); } +static gboolean +iter_is_sorted (GtkListStore *list_store, + GtkTreeIter *iter) +{ + GSequenceIter *cmp; + + if (!g_sequence_iter_is_begin (iter->user_data)) + { + cmp = g_sequence_iter_prev (iter->user_data); + if (gtk_list_store_compare_func (cmp, iter->user_data, list_store) > 0) + return FALSE; + } + + cmp = g_sequence_iter_next (iter->user_data); + if (!g_sequence_iter_is_end (cmp)) + { + if (gtk_list_store_compare_func (iter->user_data, cmp, list_store) > 0) + return FALSE; + } + + return TRUE; +} + static void gtk_list_store_sort_iter_changed (GtkListStore *list_store, GtkTreeIter *iter, gint column) { - GtkTreePath *tmp_path; + GtkListStorePrivate *priv = list_store->priv; + GtkTreePath *path; - _gtk_sequence_sort_changed (iter->user_data, - gtk_list_store_compare_func, - list_store); + 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); - tmp_path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_store), iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), tmp_path, iter); - gtk_tree_path_free (tmp_path); + if (!iter_is_sorted (list_store, iter)) + { + GHashTable *old_positions; + gint *order; + + 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); + } } static gboolean @@ -1695,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; } @@ -1714,12 +2074,11 @@ 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_UNSORTED_SORT_COLUMN_ID) @@ -1728,7 +2087,8 @@ gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable, { 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); @@ -1736,13 +2096,13 @@ gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable, } else { - g_return_if_fail (list_store->default_sort_func != NULL); + g_return_if_fail (priv->default_sort_func != NULL); } } - list_store->sort_column_id = sort_column_id; - list_store->order = order; + priv->sort_column_id = sort_column_id; + priv->order = order; gtk_tree_sortable_sort_column_changed (sortable); @@ -1754,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; + GtkListStore *list_store = GTK_LIST_STORE (sortable); + GtkListStorePrivate *priv = list_store->priv; - g_return_if_fail (GTK_IS_LIST_STORE (sortable)); - g_return_if_fail (func != NULL); + priv->sort_list = _gtk_tree_data_list_set_header (priv->sort_list, + sort_column_id, + func, data, destroy); + + if (priv->sort_column_id == sort_column_id) + gtk_list_store_sort (list_store); +} + +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; - for (list = list_store->sort_list; list; list = list->next) + if (priv->default_sort_destroy) { - GtkTreeDataSortHeader *list_header; + GDestroyNotify d = priv->default_sort_destroy; - list_header = (GtkTreeDataSortHeader*) list->data; - if (list_header->sort_column_id == sort_column_id) - { - header = list_header; - break; - } + priv->default_sort_destroy = NULL; + d (priv->default_sort_data); } - if (header == NULL) + priv->default_sort_func = func; + priv->default_sort_data = data; + priv->default_sort_destroy = destroy; + + 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 +list_store_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + guint i; + SubParserData *data = (SubParserData*)user_data; + + if (strcmp (element_name, "col") == 0) { - header = g_new0 (GtkTreeDataSortHeader, 1); - header->sort_column_id = sort_column_id; - list_store->sort_list = g_list_append (list_store->sort_list, header); - } + int i, id = -1; + gchar *context = NULL; + gboolean translatable = FALSE; + ColInfo *info; + + if (data->row_column >= data->n_columns) + { + g_set_error (error, data->error_quark, 0, + "Too many columns, maximum is %d\n", data->n_columns - 1); + return; + } - if (header->destroy) + 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) { - GtkDestroyNotify d = header->destroy; + 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); +} - header->destroy = NULL; - d (header->data); +static void +list_store_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + + g_assert (data->builder); + + if (strcmp (element_name, "row") == 0) + { + GtkTreeIter iter; + int i; + + gtk_list_store_insert_with_valuesv (GTK_LIST_STORE (data->object), + &iter, + data->last_row, + data->colids, + data->values, + data->row_column); + for (i = 0; i < data->row_column; i++) + { + ColInfo *info = data->columns[i]; + g_free (info->context); + g_slice_free (ColInfo, info); + data->columns[i] = NULL; + g_value_unset (&data->values[i]); + } + g_free (data->values); + data->values = g_new0 (GValue, data->n_columns); + data->last_row++; + data->row_column = 0; } + else if (strcmp (element_name, "columns") == 0) + { + GType *column_types; + GSList *l; + int i; + GType type; - header->func = func; - header->data = data; - header->destroy = destroy; + data->column_type_names = g_slist_reverse (data->column_type_names); + column_types = g_new0 (GType, g_slist_length (data->column_type_names)); - if (list_store->sort_column_id == sort_column_id) - gtk_list_store_sort (list_store); + 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 -gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GtkDestroyNotify destroy) +list_store_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) { - GtkListStore *list_store = (GtkListStore *) sortable; + SubParserData *data = (SubParserData*)user_data; + gint i; + GError *tmp_error = NULL; + gchar *string; + ColInfo *info; + + if (!data->is_data) + return; - g_return_if_fail (GTK_IS_LIST_STORE (sortable)); + i = data->row_column - 1; + info = data->columns[i]; - if (list_store->default_sort_destroy) + string = g_strndup (text, text_len); + if (info->translatable && text_len) { - GtkDestroyNotify d = list_store->default_sort_destroy; + gchar *translated; - list_store->default_sort_destroy = NULL; - d (list_store->default_sort_data); + /* 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; } - list_store->default_sort_func = func; - list_store->default_sort_data = data; - list_store->default_sort_destroy = destroy; - - if (list_store->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) - gtk_list_store_sort (list_store); + 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; + + if (child) + return FALSE; + + if (strcmp (tagname, "columns") == 0) + { - g_return_val_if_fail (GTK_IS_LIST_STORE (sortable), FALSE); + parser_data = g_slice_new0 (SubParserData); + parser_data->builder = builder; + parser_data->object = G_OBJECT (buildable); + parser_data->column_type_names = NULL; - return (list_store->default_sort_func != 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); }