1/* gtkliststore.c
2 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19#include <errno.h>
20#include <stdlib.h>
21#include <string.h>
22#include <gobject/gvaluecollector.h>
23#include "gtktreemodel.h"
24#include "gtkliststore.h"
25#include "gtktreedatalist.h"
26#include "gtktreednd.h"
27#include "gtkintl.h"
28#include "gtkbuildable.h"
29#include "gtkbuilderprivate.h"
30
31
32/**
33 * GtkListStore:
34 *
35 * A list-like data structure that can be used with the [class@Gtk.TreeView].
36 *
37 * The `GtkListStore` object is a list model for use with a `GtkTreeView`
38 * widget. It implements the `GtkTreeModel` interface, and consequentialy,
39 * can use all of the methods available there. It also implements the
40 * `GtkTreeSortable` interface so it can be sorted by the view.
41 * Finally, it also implements the tree
42 * [drag](iface.TreeDragSource.html) and [drop](iface.TreeDragDest.html)
43 * interfaces.
44 *
45 * The `GtkListStore` can accept most `GType`s as a column type, though
46 * it can’t accept all custom types. Internally, it will keep a copy of
47 * data passed in (such as a string or a boxed pointer). Columns that
48 * accept `GObject`s are handled a little differently. The
49 * `GtkListStore` will keep a reference to the object instead of copying the
50 * value. As a result, if the object is modified, it is up to the
51 * application writer to call [method@Gtk.TreeModel.row_changed] to emit the
52 * [signal@Gtk.TreeModel::row_changed] signal. This most commonly affects lists
53 * with [class@Gdk.Texture]s stored.
54 *
55 * An example for creating a simple list store:
56 *
57 * ```c
58 * enum {
59 * COLUMN_STRING,
60 * COLUMN_INT,
61 * COLUMN_BOOLEAN,
62 * N_COLUMNS
63 * };
64 *
65 * {
66 * GtkListStore *list_store;
67 * GtkTreePath *path;
68 * GtkTreeIter iter;
69 * int i;
70 *
71 * list_store = gtk_list_store_new (N_COLUMNS,
72 * G_TYPE_STRING,
73 * G_TYPE_INT,
74 * G_TYPE_BOOLEAN);
75 *
76 * for (i = 0; i < 10; i++)
77 * {
78 * char *some_data;
79 *
80 * some_data = get_some_data (i);
81 *
82 * // Add a new row to the model
83 * gtk_list_store_append (list_store, &iter);
84 * gtk_list_store_set (list_store, &iter,
85 * COLUMN_STRING, some_data,
86 * COLUMN_INT, i,
87 * COLUMN_BOOLEAN, FALSE,
88 * -1);
89 *
90 * // As the store will keep a copy of the string internally,
91 * // we free some_data.
92 * g_free (some_data);
93 * }
94 *
95 * // Modify a particular row
96 * path = gtk_tree_path_new_from_string ("4");
97 * gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store),
98 * &iter,
99 * path);
100 * gtk_tree_path_free (path);
101 * gtk_list_store_set (list_store, &iter,
102 * COLUMN_BOOLEAN, TRUE,
103 * -1);
104 * }
105 * ```
106 *
107 * # Performance Considerations
108 *
109 * Internally, the `GtkListStore` was originally implemented with a linked list
110 * with a tail pointer. As a result, it was fast at data insertion and deletion,
111 * and not fast at random data access. The `GtkListStore` sets the
112 * `GTK_TREE_MODEL_ITERS_PERSIST` flag, which means that `GtkTreeIter`s can be
113 * cached while the row exists. Thus, if access to a particular row is needed
114 * often and your code is expected to run on older versions of GTK, it is worth
115 * keeping the iter around.
116 *
117 * # Atomic Operations
118 *
119 * It is important to note that only the methods
120 * gtk_list_store_insert_with_values() and gtk_list_store_insert_with_valuesv()
121 * are atomic, in the sense that the row is being appended to the store and the
122 * values filled in in a single operation with regard to `GtkTreeModel` signaling.
123 * In contrast, using e.g. gtk_list_store_append() and then gtk_list_store_set()
124 * will first create a row, which triggers the `GtkTreeModel::row-inserted` signal
125 * on `GtkListStore`. The row, however, is still empty, and any signal handler
126 * connecting to `GtkTreeModel::row-inserted` on this particular store should be prepared
127 * for the situation that the row might be empty. This is especially important
128 * if you are wrapping the `GtkListStore` inside a `GtkTreeModel`Filter and are
129 * using a `GtkTreeModel`FilterVisibleFunc. Using any of the non-atomic operations
130 * to append rows to the `GtkListStore` will cause the
131 * `GtkTreeModel`FilterVisibleFunc to be visited with an empty row first; the
132 * function must be prepared for that.
133 *
134 * # GtkListStore as GtkBuildable
135 *
136 * The GtkListStore implementation of the [iface@Gtk.Buildable] interface allows
137 * to specify the model columns with a `<columns>` element that may contain
138 * multiple `<column>` elements, each specifying one model column. The “type”
139 * attribute specifies the data type for the column.
140 *
141 * Additionally, it is possible to specify content for the list store
142 * in the UI definition, with the `<data>` element. It can contain multiple
143 * `<row>` elements, each specifying to content for one row of the list model.
144 * Inside a `<row>`, the `<col>` elements specify the content for individual cells.
145 *
146 * Note that it is probably more common to define your models in the code,
147 * and one might consider it a layering violation to specify the content of
148 * a list store in a UI definition, data, not presentation, and common wisdom
149 * is to separate the two, as far as possible.
150 *
151 * An example of a UI Definition fragment for a list store:
152 *
153 * ```xml
154 * <object class="GtkListStore">
155 * <columns>
156 * <column type="gchararray"/>
157 * <column type="gchararray"/>
158 * <column type="gint"/>
159 * </columns>
160 * <data>
161 * <row>
162 * <col id="0">John</col>
163 * <col id="1">Doe</col>
164 * <col id="2">25</col>
165 * </row>
166 * <row>
167 * <col id="0">Johan</col>
168 * <col id="1">Dahlin</col>
169 * <col id="2">50</col>
170 * </row>
171 * </data>
172 * </object>
173 * ```
174 */
175
176
177struct _GtkListStorePrivate
178{
179 GtkTreeIterCompareFunc default_sort_func;
180
181 GDestroyNotify default_sort_destroy;
182 GList *sort_list;
183 GType *column_headers;
184
185 int stamp;
186 int n_columns;
187 int sort_column_id;
188 int length;
189
190 GtkSortType order;
191
192 guint columns_dirty : 1;
193
194 gpointer default_sort_data;
195 gpointer seq; /* head of the list */
196};
197
198#define GTK_LIST_STORE_IS_SORTED(list) (((GtkListStore*)(list))->priv->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
199static void gtk_list_store_tree_model_init (GtkTreeModelIface *iface);
200static void gtk_list_store_drag_source_init(GtkTreeDragSourceIface *iface);
201static void gtk_list_store_drag_dest_init (GtkTreeDragDestIface *iface);
202static void gtk_list_store_sortable_init (GtkTreeSortableIface *iface);
203static void gtk_list_store_buildable_init (GtkBuildableIface *iface);
204static void gtk_list_store_finalize (GObject *object);
205static GtkTreeModelFlags gtk_list_store_get_flags (GtkTreeModel *tree_model);
206static int gtk_list_store_get_n_columns (GtkTreeModel *tree_model);
207static GType gtk_list_store_get_column_type (GtkTreeModel *tree_model,
208 int index);
209static gboolean gtk_list_store_get_iter (GtkTreeModel *tree_model,
210 GtkTreeIter *iter,
211 GtkTreePath *path);
212static GtkTreePath *gtk_list_store_get_path (GtkTreeModel *tree_model,
213 GtkTreeIter *iter);
214static void gtk_list_store_get_value (GtkTreeModel *tree_model,
215 GtkTreeIter *iter,
216 int column,
217 GValue *value);
218static gboolean gtk_list_store_iter_next (GtkTreeModel *tree_model,
219 GtkTreeIter *iter);
220static gboolean gtk_list_store_iter_previous (GtkTreeModel *tree_model,
221 GtkTreeIter *iter);
222static gboolean gtk_list_store_iter_children (GtkTreeModel *tree_model,
223 GtkTreeIter *iter,
224 GtkTreeIter *parent);
225static gboolean gtk_list_store_iter_has_child (GtkTreeModel *tree_model,
226 GtkTreeIter *iter);
227static int gtk_list_store_iter_n_children (GtkTreeModel *tree_model,
228 GtkTreeIter *iter);
229static gboolean gtk_list_store_iter_nth_child (GtkTreeModel *tree_model,
230 GtkTreeIter *iter,
231 GtkTreeIter *parent,
232 int n);
233static gboolean gtk_list_store_iter_parent (GtkTreeModel *tree_model,
234 GtkTreeIter *iter,
235 GtkTreeIter *child);
236
237
238static void gtk_list_store_set_n_columns (GtkListStore *list_store,
239 int n_columns);
240static void gtk_list_store_set_column_type (GtkListStore *list_store,
241 int column,
242 GType type);
243
244static void gtk_list_store_increment_stamp (GtkListStore *list_store);
245
246
247/* Drag and Drop */
248static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source,
249 GtkTreePath *path);
250static gboolean gtk_list_store_drag_data_delete (GtkTreeDragSource *drag_source,
251 GtkTreePath *path);
252static GdkContentProvider *
253 gtk_list_store_drag_data_get (GtkTreeDragSource *drag_source,
254 GtkTreePath *path);
255static gboolean gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest,
256 GtkTreePath *dest,
257 const GValue *value);
258static gboolean gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest,
259 GtkTreePath *dest_path,
260 const GValue *value);
261
262
263/* sortable */
264static void gtk_list_store_sort (GtkListStore *list_store);
265static void gtk_list_store_sort_iter_changed (GtkListStore *list_store,
266 GtkTreeIter *iter,
267 int column);
268static gboolean gtk_list_store_get_sort_column_id (GtkTreeSortable *sortable,
269 int *sort_column_id,
270 GtkSortType *order);
271static void gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable,
272 int sort_column_id,
273 GtkSortType order);
274static void gtk_list_store_set_sort_func (GtkTreeSortable *sortable,
275 int sort_column_id,
276 GtkTreeIterCompareFunc func,
277 gpointer data,
278 GDestroyNotify destroy);
279static void gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable,
280 GtkTreeIterCompareFunc func,
281 gpointer data,
282 GDestroyNotify destroy);
283static gboolean gtk_list_store_has_default_sort_func (GtkTreeSortable *sortable);
284
285
286/* buildable */
287static gboolean gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable,
288 GtkBuilder *builder,
289 GObject *child,
290 const char *tagname,
291 GtkBuildableParser *parser,
292 gpointer *data);
293static void gtk_list_store_buildable_custom_tag_end (GtkBuildable *buildable,
294 GtkBuilder *builder,
295 GObject *child,
296 const char *tagname,
297 gpointer data);
298
299G_DEFINE_TYPE_WITH_CODE (GtkListStore, gtk_list_store, G_TYPE_OBJECT,
300 G_ADD_PRIVATE (GtkListStore)
301 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
302 gtk_list_store_tree_model_init)
303 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
304 gtk_list_store_drag_source_init)
305 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
306 gtk_list_store_drag_dest_init)
307 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
308 gtk_list_store_sortable_init)
309 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
310 gtk_list_store_buildable_init))
311
312
313static void
314gtk_list_store_class_init (GtkListStoreClass *class)
315{
316 GObjectClass *object_class;
317
318 object_class = (GObjectClass*) class;
319
320 object_class->finalize = gtk_list_store_finalize;
321}
322
323static void
324gtk_list_store_tree_model_init (GtkTreeModelIface *iface)
325{
326 iface->get_flags = gtk_list_store_get_flags;
327 iface->get_n_columns = gtk_list_store_get_n_columns;
328 iface->get_column_type = gtk_list_store_get_column_type;
329 iface->get_iter = gtk_list_store_get_iter;
330 iface->get_path = gtk_list_store_get_path;
331 iface->get_value = gtk_list_store_get_value;
332 iface->iter_next = gtk_list_store_iter_next;
333 iface->iter_previous = gtk_list_store_iter_previous;
334 iface->iter_children = gtk_list_store_iter_children;
335 iface->iter_has_child = gtk_list_store_iter_has_child;
336 iface->iter_n_children = gtk_list_store_iter_n_children;
337 iface->iter_nth_child = gtk_list_store_iter_nth_child;
338 iface->iter_parent = gtk_list_store_iter_parent;
339}
340
341static void
342gtk_list_store_drag_source_init (GtkTreeDragSourceIface *iface)
343{
344 iface->row_draggable = real_gtk_list_store_row_draggable;
345 iface->drag_data_delete = gtk_list_store_drag_data_delete;
346 iface->drag_data_get = gtk_list_store_drag_data_get;
347}
348
349static void
350gtk_list_store_drag_dest_init (GtkTreeDragDestIface *iface)
351{
352 iface->drag_data_received = gtk_list_store_drag_data_received;
353 iface->row_drop_possible = gtk_list_store_row_drop_possible;
354}
355
356static void
357gtk_list_store_sortable_init (GtkTreeSortableIface *iface)
358{
359 iface->get_sort_column_id = gtk_list_store_get_sort_column_id;
360 iface->set_sort_column_id = gtk_list_store_set_sort_column_id;
361 iface->set_sort_func = gtk_list_store_set_sort_func;
362 iface->set_default_sort_func = gtk_list_store_set_default_sort_func;
363 iface->has_default_sort_func = gtk_list_store_has_default_sort_func;
364}
365
366void
367gtk_list_store_buildable_init (GtkBuildableIface *iface)
368{
369 iface->custom_tag_start = gtk_list_store_buildable_custom_tag_start;
370 iface->custom_tag_end = gtk_list_store_buildable_custom_tag_end;
371}
372
373static void
374gtk_list_store_init (GtkListStore *list_store)
375{
376 GtkListStorePrivate *priv;
377
378 list_store->priv = gtk_list_store_get_instance_private (self: list_store);
379 priv = list_store->priv;
380
381 priv->seq = g_sequence_new (NULL);
382 priv->sort_list = NULL;
383 priv->stamp = g_random_int ();
384 priv->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
385 priv->columns_dirty = FALSE;
386 priv->length = 0;
387}
388
389static gboolean
390iter_is_valid (GtkTreeIter *iter,
391 GtkListStore *list_store)
392{
393 return iter != NULL &&
394 iter->user_data != NULL &&
395 list_store->priv->stamp == iter->stamp &&
396 !g_sequence_iter_is_end (iter: iter->user_data) &&
397 g_sequence_iter_get_sequence (iter: iter->user_data) == list_store->priv->seq;
398}
399
400/**
401 * gtk_list_store_new:
402 * @n_columns: number of columns in the list store
403 * @...: all `GType` types for the columns, from first to last
404 *
405 * Creates a new list store as with @n_columns columns each of the types passed
406 * in. Note that only types derived from standard GObject fundamental types
407 * are supported.
408 *
409 * As an example, `gtk_list_store_new (3, G_TYPE_INT, G_TYPE_STRING,
410 * GDK_TYPE_TEXTURE);` will create a new `GtkListStore` with three columns, of type
411 * int, string and `GdkTexture`, respectively.
412 *
413 * Returns: a new `GtkListStore`
414 */
415GtkListStore *
416gtk_list_store_new (int n_columns,
417 ...)
418{
419 GtkListStore *retval;
420 va_list args;
421 int i;
422
423 g_return_val_if_fail (n_columns > 0, NULL);
424
425 retval = g_object_new (GTK_TYPE_LIST_STORE, NULL);
426 gtk_list_store_set_n_columns (list_store: retval, n_columns);
427
428 va_start (args, n_columns);
429
430 for (i = 0; i < n_columns; i++)
431 {
432 GType type = va_arg (args, GType);
433 if (! _gtk_tree_data_list_check_type (type))
434 {
435 g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (type));
436 g_object_unref (object: retval);
437 va_end (args);
438
439 return NULL;
440 }
441
442 gtk_list_store_set_column_type (list_store: retval, column: i, type);
443 }
444
445 va_end (args);
446
447 return retval;
448}
449
450
451/**
452 * gtk_list_store_newv: (rename-to gtk_list_store_new)
453 * @n_columns: number of columns in the list store
454 * @types: (array length=n_columns): an array of `GType` types for the columns, from first to last
455 *
456 * Non-vararg creation function. Used primarily by language bindings.
457 *
458 * Returns: (transfer full): a new `GtkListStore`
459 **/
460GtkListStore *
461gtk_list_store_newv (int n_columns,
462 GType *types)
463{
464 GtkListStore *retval;
465 int i;
466
467 g_return_val_if_fail (n_columns > 0, NULL);
468
469 retval = g_object_new (GTK_TYPE_LIST_STORE, NULL);
470 gtk_list_store_set_n_columns (list_store: retval, n_columns);
471
472 for (i = 0; i < n_columns; i++)
473 {
474 if (! _gtk_tree_data_list_check_type (type: types[i]))
475 {
476 g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (types[i]));
477 g_object_unref (object: retval);
478 return NULL;
479 }
480
481 gtk_list_store_set_column_type (list_store: retval, column: i, type: types[i]);
482 }
483
484 return retval;
485}
486
487/**
488 * gtk_list_store_set_column_types:
489 * @list_store: A `GtkListStore`
490 * @n_columns: Number of columns for the list store
491 * @types: (array length=n_columns): An array length n of `GType`s
492 *
493 * This function is meant primarily for `GObject`s that inherit from `GtkListStore`,
494 * and should only be used when constructing a new `GtkListStore`. It will not
495 * function after a row has been added, or a method on the `GtkTreeModel`
496 * interface is called.
497 **/
498void
499gtk_list_store_set_column_types (GtkListStore *list_store,
500 int n_columns,
501 GType *types)
502{
503 GtkListStorePrivate *priv;
504 int i;
505
506 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
507
508 priv = list_store->priv;
509
510 g_return_if_fail (priv->columns_dirty == 0);
511
512 gtk_list_store_set_n_columns (list_store, n_columns);
513 for (i = 0; i < n_columns; i++)
514 {
515 if (! _gtk_tree_data_list_check_type (type: types[i]))
516 {
517 g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (types[i]));
518 continue;
519 }
520 gtk_list_store_set_column_type (list_store, column: i, type: types[i]);
521 }
522}
523
524static void
525gtk_list_store_set_n_columns (GtkListStore *list_store,
526 int n_columns)
527{
528 GtkListStorePrivate *priv = list_store->priv;
529 int i;
530
531 if (priv->n_columns == n_columns)
532 return;
533
534 priv->column_headers = g_renew (GType, priv->column_headers, n_columns);
535 for (i = priv->n_columns; i < n_columns; i++)
536 priv->column_headers[i] = G_TYPE_INVALID;
537 priv->n_columns = n_columns;
538
539 if (priv->sort_list)
540 _gtk_tree_data_list_header_free (header_list: priv->sort_list);
541 priv->sort_list = _gtk_tree_data_list_header_new (n_columns, types: priv->column_headers);
542}
543
544static void
545gtk_list_store_set_column_type (GtkListStore *list_store,
546 int column,
547 GType type)
548{
549 GtkListStorePrivate *priv = list_store->priv;
550
551 if (!_gtk_tree_data_list_check_type (type))
552 {
553 g_warning ("%s: Invalid type %s", G_STRLOC, g_type_name (type));
554 return;
555 }
556
557 priv->column_headers[column] = type;
558}
559
560static void
561gtk_list_store_finalize (GObject *object)
562{
563 GtkListStore *list_store = GTK_LIST_STORE (object);
564 GtkListStorePrivate *priv = list_store->priv;
565
566 g_sequence_foreach (seq: priv->seq,
567 func: (GFunc) _gtk_tree_data_list_free, user_data: priv->column_headers);
568
569 g_sequence_free (seq: priv->seq);
570
571 _gtk_tree_data_list_header_free (header_list: priv->sort_list);
572 g_free (mem: priv->column_headers);
573
574 if (priv->default_sort_destroy)
575 {
576 GDestroyNotify d = priv->default_sort_destroy;
577
578 priv->default_sort_destroy = NULL;
579 d (priv->default_sort_data);
580 priv->default_sort_data = NULL;
581 }
582
583 G_OBJECT_CLASS (gtk_list_store_parent_class)->finalize (object);
584}
585
586/* Fulfill the GtkTreeModel requirements */
587static GtkTreeModelFlags
588gtk_list_store_get_flags (GtkTreeModel *tree_model)
589{
590 return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
591}
592
593static int
594gtk_list_store_get_n_columns (GtkTreeModel *tree_model)
595{
596 GtkListStore *list_store = GTK_LIST_STORE (tree_model);
597 GtkListStorePrivate *priv = list_store->priv;
598
599 priv->columns_dirty = TRUE;
600
601 return priv->n_columns;
602}
603
604static GType
605gtk_list_store_get_column_type (GtkTreeModel *tree_model,
606 int index)
607{
608 GtkListStore *list_store = GTK_LIST_STORE (tree_model);
609 GtkListStorePrivate *priv = list_store->priv;
610
611 g_return_val_if_fail (index < priv->n_columns, G_TYPE_INVALID);
612
613 priv->columns_dirty = TRUE;
614
615 return priv->column_headers[index];
616}
617
618static gboolean
619gtk_list_store_get_iter (GtkTreeModel *tree_model,
620 GtkTreeIter *iter,
621 GtkTreePath *path)
622{
623 GtkListStore *list_store = GTK_LIST_STORE (tree_model);
624 GtkListStorePrivate *priv = list_store->priv;
625 GSequence *seq;
626 int i;
627
628 priv->columns_dirty = TRUE;
629
630 seq = priv->seq;
631
632 i = gtk_tree_path_get_indices (path)[0];
633
634 if (i >= g_sequence_get_length (seq))
635 {
636 iter->stamp = 0;
637 return FALSE;
638 }
639
640 iter->stamp = priv->stamp;
641 iter->user_data = g_sequence_get_iter_at_pos (seq, pos: i);
642
643 return TRUE;
644}
645
646static GtkTreePath *
647gtk_list_store_get_path (GtkTreeModel *tree_model,
648 GtkTreeIter *iter)
649{
650 GtkListStore *list_store = GTK_LIST_STORE (tree_model);
651 GtkListStorePrivate *priv = list_store->priv;
652 GtkTreePath *path;
653
654 g_return_val_if_fail (iter->stamp == priv->stamp, NULL);
655
656 if (g_sequence_iter_is_end (iter: iter->user_data))
657 return NULL;
658
659 path = gtk_tree_path_new ();
660 gtk_tree_path_append_index (path, index_: g_sequence_iter_get_position (iter: iter->user_data));
661
662 return path;
663}
664
665static void
666gtk_list_store_get_value (GtkTreeModel *tree_model,
667 GtkTreeIter *iter,
668 int column,
669 GValue *value)
670{
671 GtkListStore *list_store = GTK_LIST_STORE (tree_model);
672 GtkListStorePrivate *priv = list_store->priv;
673 GtkTreeDataList *list;
674 int tmp_column = column;
675
676 g_return_if_fail (column < priv->n_columns);
677 g_return_if_fail (iter_is_valid (iter, list_store));
678
679 list = g_sequence_get (iter: iter->user_data);
680
681 while (tmp_column-- > 0 && list)
682 list = list->next;
683
684 if (list == NULL)
685 g_value_init (value, g_type: priv->column_headers[column]);
686 else
687 _gtk_tree_data_list_node_to_value (list,
688 type: priv->column_headers[column],
689 value);
690}
691
692static gboolean
693gtk_list_store_iter_next (GtkTreeModel *tree_model,
694 GtkTreeIter *iter)
695{
696 GtkListStore *list_store = GTK_LIST_STORE (tree_model);
697 GtkListStorePrivate *priv = list_store->priv;
698 gboolean retval;
699
700 g_return_val_if_fail (priv->stamp == iter->stamp, FALSE);
701 iter->user_data = g_sequence_iter_next (iter: iter->user_data);
702
703 retval = g_sequence_iter_is_end (iter: iter->user_data);
704 if (retval)
705 iter->stamp = 0;
706
707 return !retval;
708}
709
710static gboolean
711gtk_list_store_iter_previous (GtkTreeModel *tree_model,
712 GtkTreeIter *iter)
713{
714 GtkListStore *list_store = GTK_LIST_STORE (tree_model);
715 GtkListStorePrivate *priv = list_store->priv;
716
717 g_return_val_if_fail (priv->stamp == iter->stamp, FALSE);
718
719 if (g_sequence_iter_is_begin (iter: iter->user_data))
720 {
721 iter->stamp = 0;
722 return FALSE;
723 }
724
725 iter->user_data = g_sequence_iter_prev (iter: iter->user_data);
726
727 return TRUE;
728}
729
730static gboolean
731gtk_list_store_iter_children (GtkTreeModel *tree_model,
732 GtkTreeIter *iter,
733 GtkTreeIter *parent)
734{
735 GtkListStore *list_store = (GtkListStore *) tree_model;
736 GtkListStorePrivate *priv = list_store->priv;
737
738 /* this is a list, nodes have no children */
739 if (parent)
740 {
741 iter->stamp = 0;
742 return FALSE;
743 }
744
745 if (g_sequence_get_length (seq: priv->seq) > 0)
746 {
747 iter->stamp = priv->stamp;
748 iter->user_data = g_sequence_get_begin_iter (seq: priv->seq);
749 return TRUE;
750 }
751 else
752 {
753 iter->stamp = 0;
754 return FALSE;
755 }
756}
757
758static gboolean
759gtk_list_store_iter_has_child (GtkTreeModel *tree_model,
760 GtkTreeIter *iter)
761{
762 return FALSE;
763}
764
765static int
766gtk_list_store_iter_n_children (GtkTreeModel *tree_model,
767 GtkTreeIter *iter)
768{
769 GtkListStore *list_store = GTK_LIST_STORE (tree_model);
770 GtkListStorePrivate *priv = list_store->priv;
771
772 if (iter == NULL)
773 return g_sequence_get_length (seq: priv->seq);
774
775 g_return_val_if_fail (priv->stamp == iter->stamp, -1);
776
777 return 0;
778}
779
780static gboolean
781gtk_list_store_iter_nth_child (GtkTreeModel *tree_model,
782 GtkTreeIter *iter,
783 GtkTreeIter *parent,
784 int n)
785{
786 GtkListStore *list_store = GTK_LIST_STORE (tree_model);
787 GtkListStorePrivate *priv = list_store->priv;
788 GSequenceIter *child;
789
790 iter->stamp = 0;
791
792 if (parent)
793 return FALSE;
794
795 child = g_sequence_get_iter_at_pos (seq: priv->seq, pos: n);
796
797 if (g_sequence_iter_is_end (iter: child))
798 return FALSE;
799
800 iter->stamp = priv->stamp;
801 iter->user_data = child;
802
803 return TRUE;
804}
805
806static gboolean
807gtk_list_store_iter_parent (GtkTreeModel *tree_model,
808 GtkTreeIter *iter,
809 GtkTreeIter *child)
810{
811 iter->stamp = 0;
812 return FALSE;
813}
814
815static gboolean
816gtk_list_store_real_set_value (GtkListStore *list_store,
817 GtkTreeIter *iter,
818 int column,
819 GValue *value,
820 gboolean sort)
821{
822 GtkListStorePrivate *priv = list_store->priv;
823 GtkTreeDataList *list;
824 GtkTreeDataList *prev;
825 int old_column = column;
826 GValue real_value = G_VALUE_INIT;
827 gboolean converted = FALSE;
828 gboolean retval = FALSE;
829
830 if (! g_type_is_a (G_VALUE_TYPE (value), is_a_type: priv->column_headers[column]))
831 {
832 if (! (g_value_type_transformable (G_VALUE_TYPE (value), dest_type: priv->column_headers[column])))
833 {
834 g_warning ("%s: Unable to convert from %s to %s",
835 G_STRLOC,
836 g_type_name (G_VALUE_TYPE (value)),
837 g_type_name (priv->column_headers[column]));
838 return retval;
839 }
840
841 g_value_init (value: &real_value, g_type: priv->column_headers[column]);
842 if (!g_value_transform (src_value: value, dest_value: &real_value))
843 {
844 g_warning ("%s: Unable to make conversion from %s to %s",
845 G_STRLOC,
846 g_type_name (G_VALUE_TYPE (value)),
847 g_type_name (priv->column_headers[column]));
848 g_value_unset (value: &real_value);
849 return retval;
850 }
851 converted = TRUE;
852 }
853
854 prev = list = g_sequence_get (iter: iter->user_data);
855
856 while (list != NULL)
857 {
858 if (column == 0)
859 {
860 if (converted)
861 _gtk_tree_data_list_value_to_node (list, value: &real_value);
862 else
863 _gtk_tree_data_list_value_to_node (list, value);
864 retval = TRUE;
865 if (converted)
866 g_value_unset (value: &real_value);
867 if (sort && GTK_LIST_STORE_IS_SORTED (list_store))
868 gtk_list_store_sort_iter_changed (list_store, iter, column: old_column);
869 return retval;
870 }
871
872 column--;
873 prev = list;
874 list = list->next;
875 }
876
877 if (g_sequence_get (iter: iter->user_data) == NULL)
878 {
879 list = _gtk_tree_data_list_alloc();
880 g_sequence_set (iter: iter->user_data, data: list);
881 list->next = NULL;
882 }
883 else
884 {
885 list = prev->next = _gtk_tree_data_list_alloc ();
886 list->next = NULL;
887 }
888
889 while (column != 0)
890 {
891 list->next = _gtk_tree_data_list_alloc ();
892 list = list->next;
893 list->next = NULL;
894 column --;
895 }
896
897 if (converted)
898 _gtk_tree_data_list_value_to_node (list, value: &real_value);
899 else
900 _gtk_tree_data_list_value_to_node (list, value);
901
902 retval = TRUE;
903 if (converted)
904 g_value_unset (value: &real_value);
905
906 if (sort && GTK_LIST_STORE_IS_SORTED (list_store))
907 gtk_list_store_sort_iter_changed (list_store, iter, column: old_column);
908
909 return retval;
910}
911
912
913/**
914 * gtk_list_store_set_value:
915 * @list_store: A `GtkListStore`
916 * @iter: A valid `GtkTreeIter` for the row being modified
917 * @column: column number to modify
918 * @value: new value for the cell
919 *
920 * Sets the data in the cell specified by @iter and @column.
921 * The type of @value must be convertible to the type of the
922 * column.
923 *
924 **/
925void
926gtk_list_store_set_value (GtkListStore *list_store,
927 GtkTreeIter *iter,
928 int column,
929 GValue *value)
930{
931 GtkListStorePrivate *priv;
932
933 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
934 g_return_if_fail (iter_is_valid (iter, list_store));
935 g_return_if_fail (G_IS_VALUE (value));
936 priv = list_store->priv;
937 g_return_if_fail (column >= 0 && column < priv->n_columns);
938
939 if (gtk_list_store_real_set_value (list_store, iter, column, value, TRUE))
940 {
941 GtkTreePath *path;
942
943 path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter);
944 gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter);
945 gtk_tree_path_free (path);
946 }
947}
948
949static GtkTreeIterCompareFunc
950gtk_list_store_get_compare_func (GtkListStore *list_store)
951{
952 GtkListStorePrivate *priv = list_store->priv;
953 GtkTreeIterCompareFunc func = NULL;
954
955 if (GTK_LIST_STORE_IS_SORTED (list_store))
956 {
957 if (priv->sort_column_id != -1)
958 {
959 GtkTreeDataSortHeader *header;
960 header = _gtk_tree_data_list_get_header (header_list: priv->sort_list,
961 sort_column_id: priv->sort_column_id);
962 g_return_val_if_fail (header != NULL, NULL);
963 g_return_val_if_fail (header->func != NULL, NULL);
964 func = header->func;
965 }
966 else
967 {
968 func = priv->default_sort_func;
969 }
970 }
971
972 return func;
973}
974
975static void
976gtk_list_store_set_vector_internal (GtkListStore *list_store,
977 GtkTreeIter *iter,
978 gboolean *emit_signal,
979 gboolean *maybe_need_sort,
980 int *columns,
981 GValue *values,
982 int n_values)
983{
984 GtkListStorePrivate *priv = list_store->priv;
985 int i;
986 GtkTreeIterCompareFunc func = NULL;
987
988 func = gtk_list_store_get_compare_func (list_store);
989 if (func != _gtk_tree_data_list_compare_func)
990 *maybe_need_sort = TRUE;
991
992 for (i = 0; i < n_values; i++)
993 {
994 *emit_signal = gtk_list_store_real_set_value (list_store,
995 iter,
996 column: columns[i],
997 value: &values[i],
998 FALSE) || *emit_signal;
999
1000 if (func == _gtk_tree_data_list_compare_func &&
1001 columns[i] == priv->sort_column_id)
1002 *maybe_need_sort = TRUE;
1003 }
1004}
1005
1006static void
1007gtk_list_store_set_valist_internal (GtkListStore *list_store,
1008 GtkTreeIter *iter,
1009 gboolean *emit_signal,
1010 gboolean *maybe_need_sort,
1011 va_list var_args)
1012{
1013 GtkListStorePrivate *priv = list_store->priv;
1014 int column;
1015 GtkTreeIterCompareFunc func = NULL;
1016
1017 column = va_arg (var_args, int);
1018
1019 func = gtk_list_store_get_compare_func (list_store);
1020 if (func != _gtk_tree_data_list_compare_func)
1021 *maybe_need_sort = TRUE;
1022
1023 while (column != -1)
1024 {
1025 GValue value = G_VALUE_INIT;
1026 char *error = NULL;
1027
1028 if (column < 0 || column >= priv->n_columns)
1029 {
1030 g_warning ("%s: Invalid column number %d added to iter (remember to end your list of columns with a -1)", G_STRLOC, column);
1031 break;
1032 }
1033
1034 G_VALUE_COLLECT_INIT (&value, priv->column_headers[column],
1035 var_args, 0, &error);
1036 if (error)
1037 {
1038 g_warning ("%s: %s", G_STRLOC, error);
1039 g_free (mem: error);
1040
1041 /* we purposely leak the value here, it might not be
1042 * in a sane state if an error condition occurred
1043 */
1044 break;
1045 }
1046
1047 /* FIXME: instead of calling this n times, refactor with above */
1048 *emit_signal = gtk_list_store_real_set_value (list_store,
1049 iter,
1050 column,
1051 value: &value,
1052 FALSE) || *emit_signal;
1053
1054 if (func == _gtk_tree_data_list_compare_func &&
1055 column == priv->sort_column_id)
1056 *maybe_need_sort = TRUE;
1057
1058 g_value_unset (value: &value);
1059
1060 column = va_arg (var_args, int);
1061 }
1062}
1063
1064/**
1065 * gtk_list_store_set_valuesv: (rename-to gtk_list_store_set)
1066 * @list_store: A `GtkListStore`
1067 * @iter: A valid `GtkTreeIter` for the row being modified
1068 * @columns: (array length=n_values): an array of column numbers
1069 * @values: (array length=n_values): an array of GValues
1070 * @n_values: the length of the @columns and @values arrays
1071 *
1072 * A variant of gtk_list_store_set_valist() which
1073 * takes the columns and values as two arrays, instead of
1074 * varargs. This function is mainly intended for
1075 * language-bindings and in case the number of columns to
1076 * change is not known until run-time.
1077 */
1078void
1079gtk_list_store_set_valuesv (GtkListStore *list_store,
1080 GtkTreeIter *iter,
1081 int *columns,
1082 GValue *values,
1083 int n_values)
1084{
1085 GtkListStorePrivate *priv;
1086 gboolean emit_signal = FALSE;
1087 gboolean maybe_need_sort = FALSE;
1088
1089 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
1090 g_return_if_fail (iter_is_valid (iter, list_store));
1091
1092 priv = list_store->priv;
1093
1094 gtk_list_store_set_vector_internal (list_store, iter,
1095 emit_signal: &emit_signal,
1096 maybe_need_sort: &maybe_need_sort,
1097 columns, values, n_values);
1098
1099 if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store))
1100 gtk_list_store_sort_iter_changed (list_store, iter, column: priv->sort_column_id);
1101
1102 if (emit_signal)
1103 {
1104 GtkTreePath *path;
1105
1106 path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter);
1107 gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter);
1108 gtk_tree_path_free (path);
1109 }
1110}
1111
1112/**
1113 * gtk_list_store_set_valist:
1114 * @list_store: A `GtkListStore`
1115 * @iter: A valid `GtkTreeIter` for the row being modified
1116 * @var_args: va_list of column/value pairs
1117 *
1118 * See gtk_list_store_set(); this version takes a va_list for use by language
1119 * bindings.
1120 *
1121 **/
1122void
1123gtk_list_store_set_valist (GtkListStore *list_store,
1124 GtkTreeIter *iter,
1125 va_list var_args)
1126{
1127 GtkListStorePrivate *priv;
1128 gboolean emit_signal = FALSE;
1129 gboolean maybe_need_sort = FALSE;
1130
1131 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
1132 g_return_if_fail (iter_is_valid (iter, list_store));
1133
1134 priv = list_store->priv;
1135
1136 gtk_list_store_set_valist_internal (list_store, iter,
1137 emit_signal: &emit_signal,
1138 maybe_need_sort: &maybe_need_sort,
1139 var_args);
1140
1141 if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store))
1142 gtk_list_store_sort_iter_changed (list_store, iter, column: priv->sort_column_id);
1143
1144 if (emit_signal)
1145 {
1146 GtkTreePath *path;
1147
1148 path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter);
1149 gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter);
1150 gtk_tree_path_free (path);
1151 }
1152}
1153
1154/**
1155 * gtk_list_store_set:
1156 * @list_store: a `GtkListStore`
1157 * @iter: row iterator
1158 * @...: pairs of column number and value, terminated with -1
1159 *
1160 * Sets the value of one or more cells in the row referenced by @iter.
1161 * The variable argument list should contain integer column numbers,
1162 * each column number followed by the value to be set.
1163 * The list is terminated by a -1. For example, to set column 0 with type
1164 * %G_TYPE_STRING to “Foo”, you would write `gtk_list_store_set (store, iter,
1165 * 0, "Foo", -1)`.
1166 *
1167 * The value will be referenced by the store if it is a %G_TYPE_OBJECT, and it
1168 * will be copied if it is a %G_TYPE_STRING or %G_TYPE_BOXED.
1169 */
1170void
1171gtk_list_store_set (GtkListStore *list_store,
1172 GtkTreeIter *iter,
1173 ...)
1174{
1175 va_list var_args;
1176
1177 va_start (var_args, iter);
1178 gtk_list_store_set_valist (list_store, iter, var_args);
1179 va_end (var_args);
1180}
1181
1182/**
1183 * gtk_list_store_remove:
1184 * @list_store: A `GtkListStore`
1185 * @iter: A valid `GtkTreeIter`
1186 *
1187 * Removes the given row from the list store. After being removed,
1188 * @iter is set to be the next valid row, or invalidated if it pointed
1189 * to the last row in @list_store.
1190 *
1191 * Returns: %TRUE if @iter is valid, %FALSE if not.
1192 **/
1193gboolean
1194gtk_list_store_remove (GtkListStore *list_store,
1195 GtkTreeIter *iter)
1196{
1197 GtkListStorePrivate *priv;
1198 GtkTreePath *path;
1199 GSequenceIter *ptr, *next;
1200
1201 g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE);
1202 g_return_val_if_fail (iter_is_valid (iter, list_store), FALSE);
1203
1204 priv = list_store->priv;
1205
1206 path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter);
1207
1208 ptr = iter->user_data;
1209 next = g_sequence_iter_next (iter: ptr);
1210
1211 _gtk_tree_data_list_free (list: g_sequence_get (iter: ptr), column_headers: priv->column_headers);
1212 g_sequence_remove (iter: iter->user_data);
1213
1214 priv->length--;
1215
1216 gtk_tree_model_row_deleted (GTK_TREE_MODEL (list_store), path);
1217 gtk_tree_path_free (path);
1218
1219 if (g_sequence_iter_is_end (iter: next))
1220 {
1221 iter->stamp = 0;
1222 return FALSE;
1223 }
1224 else
1225 {
1226 iter->stamp = priv->stamp;
1227 iter->user_data = next;
1228 return TRUE;
1229 }
1230}
1231
1232/**
1233 * gtk_list_store_insert:
1234 * @list_store: A `GtkListStore`
1235 * @iter: (out): An unset `GtkTreeIter` to set to the new row
1236 * @position: position to insert the new row, or -1 for last
1237 *
1238 * Creates a new row at @position. @iter will be changed to point to this new
1239 * row. If @position is -1 or is larger than the number of rows on the list,
1240 * then the new row will be appended to the list. The row will be empty after
1241 * this function is called. To fill in values, you need to call
1242 * gtk_list_store_set() or gtk_list_store_set_value().
1243 *
1244 **/
1245void
1246gtk_list_store_insert (GtkListStore *list_store,
1247 GtkTreeIter *iter,
1248 int position)
1249{
1250 GtkListStorePrivate *priv;
1251 GtkTreePath *path;
1252 GSequence *seq;
1253 GSequenceIter *ptr;
1254 int length;
1255
1256 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
1257 g_return_if_fail (iter != NULL);
1258
1259 priv = list_store->priv;
1260
1261 priv->columns_dirty = TRUE;
1262
1263 seq = priv->seq;
1264
1265 length = g_sequence_get_length (seq);
1266 if (position > length || position < 0)
1267 position = length;
1268
1269 ptr = g_sequence_get_iter_at_pos (seq, pos: position);
1270 ptr = g_sequence_insert_before (iter: ptr, NULL);
1271
1272 iter->stamp = priv->stamp;
1273 iter->user_data = ptr;
1274
1275 g_assert (iter_is_valid (iter, list_store));
1276
1277 priv->length++;
1278
1279 path = gtk_tree_path_new ();
1280 gtk_tree_path_append_index (path, index_: position);
1281 gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter);
1282 gtk_tree_path_free (path);
1283}
1284
1285/**
1286 * gtk_list_store_insert_before:
1287 * @list_store: A `GtkListStore`
1288 * @iter: (out): An unset `GtkTreeIter` to set to the new row
1289 * @sibling: (nullable): A valid `GtkTreeIter`
1290 *
1291 * Inserts a new row before @sibling. If @sibling is %NULL, then the row will
1292 * be appended to the end of the list. @iter will be changed to point to this
1293 * new row. The row will be empty after this function is called. To fill in
1294 * values, you need to call gtk_list_store_set() or gtk_list_store_set_value().
1295 *
1296 **/
1297void
1298gtk_list_store_insert_before (GtkListStore *list_store,
1299 GtkTreeIter *iter,
1300 GtkTreeIter *sibling)
1301{
1302 GtkListStorePrivate *priv;
1303 GSequenceIter *after;
1304
1305 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
1306 g_return_if_fail (iter != NULL);
1307
1308 priv = list_store->priv;
1309
1310 if (sibling)
1311 g_return_if_fail (iter_is_valid (sibling, list_store));
1312
1313 if (!sibling)
1314 after = g_sequence_get_end_iter (seq: priv->seq);
1315 else
1316 after = sibling->user_data;
1317
1318 gtk_list_store_insert (list_store, iter, position: g_sequence_iter_get_position (iter: after));
1319}
1320
1321/**
1322 * gtk_list_store_insert_after:
1323 * @list_store: A `GtkListStore`
1324 * @iter: (out): An unset `GtkTreeIter` to set to the new row
1325 * @sibling: (nullable): A valid `GtkTreeIter`
1326 *
1327 * Inserts a new row after @sibling. If @sibling is %NULL, then the row will be
1328 * prepended to the beginning of the list. @iter will be changed to point to
1329 * this new row. The row will be empty after this function is called. To fill
1330 * in values, you need to call gtk_list_store_set() or gtk_list_store_set_value().
1331 *
1332 **/
1333void
1334gtk_list_store_insert_after (GtkListStore *list_store,
1335 GtkTreeIter *iter,
1336 GtkTreeIter *sibling)
1337{
1338 GtkListStorePrivate *priv;
1339 GSequenceIter *after;
1340
1341 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
1342 g_return_if_fail (iter != NULL);
1343
1344 priv = list_store->priv;
1345
1346 if (sibling)
1347 g_return_if_fail (iter_is_valid (sibling, list_store));
1348
1349 if (!sibling)
1350 after = g_sequence_get_begin_iter (seq: priv->seq);
1351 else
1352 after = g_sequence_iter_next (iter: sibling->user_data);
1353
1354 gtk_list_store_insert (list_store, iter, position: g_sequence_iter_get_position (iter: after));
1355}
1356
1357/**
1358 * gtk_list_store_prepend:
1359 * @list_store: A `GtkListStore`
1360 * @iter: (out): An unset `GtkTreeIter` to set to the prepend row
1361 *
1362 * Prepends a new row to @list_store. @iter will be changed to point to this new
1363 * row. The row will be empty after this function is called. To fill in
1364 * values, you need to call gtk_list_store_set() or gtk_list_store_set_value().
1365 *
1366 **/
1367void
1368gtk_list_store_prepend (GtkListStore *list_store,
1369 GtkTreeIter *iter)
1370{
1371 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
1372 g_return_if_fail (iter != NULL);
1373
1374 gtk_list_store_insert (list_store, iter, position: 0);
1375}
1376
1377/**
1378 * gtk_list_store_append:
1379 * @list_store: A `GtkListStore`
1380 * @iter: (out): An unset `GtkTreeIter` to set to the appended row
1381 *
1382 * Appends a new row to @list_store. @iter will be changed to point to this new
1383 * row. The row will be empty after this function is called. To fill in
1384 * values, you need to call gtk_list_store_set() or gtk_list_store_set_value().
1385 *
1386 **/
1387void
1388gtk_list_store_append (GtkListStore *list_store,
1389 GtkTreeIter *iter)
1390{
1391 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
1392 g_return_if_fail (iter != NULL);
1393
1394 gtk_list_store_insert (list_store, iter, position: -1);
1395}
1396
1397static void
1398gtk_list_store_increment_stamp (GtkListStore *list_store)
1399{
1400 GtkListStorePrivate *priv = list_store->priv;
1401
1402 do
1403 {
1404 priv->stamp++;
1405 }
1406 while (priv->stamp == 0);
1407}
1408
1409/**
1410 * gtk_list_store_clear:
1411 * @list_store: a `GtkListStore`.
1412 *
1413 * Removes all rows from the list store.
1414 *
1415 **/
1416void
1417gtk_list_store_clear (GtkListStore *list_store)
1418{
1419 GtkListStorePrivate *priv;
1420 GtkTreeIter iter;
1421
1422 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
1423
1424 priv = list_store->priv;
1425
1426 while (g_sequence_get_length (seq: priv->seq) > 0)
1427 {
1428 iter.stamp = priv->stamp;
1429 iter.user_data = g_sequence_get_begin_iter (seq: priv->seq);
1430 gtk_list_store_remove (list_store, iter: &iter);
1431 }
1432
1433 gtk_list_store_increment_stamp (list_store);
1434}
1435
1436/**
1437 * gtk_list_store_iter_is_valid:
1438 * @list_store: a list store
1439 * @iter: the iterator to check
1440 *
1441 * Checks if the given iter is a valid iter for this `GtkListStore`.
1442 *
1443 * This function is slow. Only use it for debugging and/or testing
1444 * purposes.
1445 *
1446 * Returns: %TRUE if the iter is valid, %FALSE if the iter is invalid.
1447 **/
1448gboolean
1449gtk_list_store_iter_is_valid (GtkListStore *list_store,
1450 GtkTreeIter *iter)
1451{
1452 GtkListStorePrivate *priv;
1453 GSequenceIter *seq_iter;
1454
1455 g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), FALSE);
1456 g_return_val_if_fail (iter != NULL, FALSE);
1457
1458 /* can't use iter_is_valid() here, because iter might point
1459 * to random memory.
1460 *
1461 * We MUST NOT dereference it.
1462 */
1463
1464 priv = list_store->priv;
1465
1466 if (iter == NULL ||
1467 iter->user_data == NULL ||
1468 priv->stamp != iter->stamp)
1469 return FALSE;
1470
1471 for (seq_iter = g_sequence_get_begin_iter (seq: priv->seq);
1472 !g_sequence_iter_is_end (iter: seq_iter);
1473 seq_iter = g_sequence_iter_next (iter: seq_iter))
1474 {
1475 if (seq_iter == iter->user_data)
1476 return TRUE;
1477 }
1478
1479 return FALSE;
1480}
1481
1482static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source,
1483 GtkTreePath *path)
1484{
1485 return TRUE;
1486}
1487
1488static gboolean
1489gtk_list_store_drag_data_delete (GtkTreeDragSource *drag_source,
1490 GtkTreePath *path)
1491{
1492 GtkTreeIter iter;
1493
1494 if (gtk_list_store_get_iter (GTK_TREE_MODEL (drag_source),
1495 iter: &iter,
1496 path))
1497 {
1498 gtk_list_store_remove (GTK_LIST_STORE (drag_source), iter: &iter);
1499 return TRUE;
1500 }
1501 return FALSE;
1502}
1503
1504static GdkContentProvider *
1505gtk_list_store_drag_data_get (GtkTreeDragSource *drag_source,
1506 GtkTreePath *path)
1507{
1508 /* Note that we don't need to handle the GTK_TREE_MODEL_ROW
1509 * target, because the default handler does it for us, but
1510 * we do anyway for the convenience of someone maybe overriding the
1511 * default handler.
1512 */
1513 return gtk_tree_create_row_drag_content (GTK_TREE_MODEL (drag_source), path);
1514}
1515
1516static gboolean
1517gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest,
1518 GtkTreePath *dest,
1519 const GValue *value)
1520{
1521 GtkTreeModel *tree_model = GTK_TREE_MODEL (drag_dest);
1522 GtkListStore *list_store = GTK_LIST_STORE (tree_model);
1523 GtkListStorePrivate *priv = list_store->priv;
1524 GtkTreeModel *src_model = NULL;
1525 GtkTreePath *src_path = NULL;
1526 gboolean retval = FALSE;
1527
1528 if (gtk_tree_get_row_drag_data (value,
1529 tree_model: &src_model,
1530 path: &src_path) &&
1531 src_model == tree_model)
1532 {
1533 /* Copy the given row to a new position */
1534 GtkTreeIter src_iter;
1535 GtkTreeIter dest_iter;
1536 GtkTreePath *prev;
1537
1538 if (!gtk_list_store_get_iter (tree_model: src_model,
1539 iter: &src_iter,
1540 path: src_path))
1541 {
1542 goto out;
1543 }
1544
1545 /* Get the path to insert _after_ (dest is the path to insert _before_) */
1546 prev = gtk_tree_path_copy (path: dest);
1547
1548 if (!gtk_tree_path_prev (path: prev))
1549 {
1550 /* dest was the first spot in the list; which means we are supposed
1551 * to prepend.
1552 */
1553 gtk_list_store_prepend (list_store, iter: &dest_iter);
1554
1555 retval = TRUE;
1556 }
1557 else
1558 {
1559 if (gtk_list_store_get_iter (tree_model, iter: &dest_iter, path: prev))
1560 {
1561 GtkTreeIter tmp_iter = dest_iter;
1562
1563 gtk_list_store_insert_after (list_store, iter: &dest_iter, sibling: &tmp_iter);
1564
1565 retval = TRUE;
1566 }
1567 }
1568
1569 gtk_tree_path_free (path: prev);
1570
1571 /* If we succeeded in creating dest_iter, copy data from src
1572 */
1573 if (retval)
1574 {
1575 GtkTreeDataList *dl = g_sequence_get (iter: src_iter.user_data);
1576 GtkTreeDataList *copy_head = NULL;
1577 GtkTreeDataList *copy_prev = NULL;
1578 GtkTreeDataList *copy_iter = NULL;
1579 GtkTreePath *path;
1580 int col;
1581
1582 col = 0;
1583 while (dl)
1584 {
1585 copy_iter = _gtk_tree_data_list_node_copy (list: dl,
1586 type: priv->column_headers[col]);
1587
1588 if (copy_head == NULL)
1589 copy_head = copy_iter;
1590
1591 if (copy_prev)
1592 copy_prev->next = copy_iter;
1593
1594 copy_prev = copy_iter;
1595
1596 dl = dl->next;
1597 ++col;
1598 }
1599
1600 dest_iter.stamp = priv->stamp;
1601 g_sequence_set (iter: dest_iter.user_data, data: copy_head);
1602
1603 path = gtk_list_store_get_path (tree_model, iter: &dest_iter);
1604 gtk_tree_model_row_changed (tree_model, path, iter: &dest_iter);
1605 gtk_tree_path_free (path);
1606 }
1607 }
1608 else
1609 {
1610 /* FIXME maybe add some data targets eventually, or handle text
1611 * targets in the simple case.
1612 */
1613 }
1614
1615 out:
1616
1617 if (src_path)
1618 gtk_tree_path_free (path: src_path);
1619
1620 return retval;
1621}
1622
1623static gboolean
1624gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest,
1625 GtkTreePath *dest_path,
1626 const GValue *value)
1627{
1628 int *indices;
1629 GtkTreeModel *src_model = NULL;
1630 GtkTreePath *src_path = NULL;
1631 gboolean retval = FALSE;
1632
1633 /* don't accept drops if the list has been sorted */
1634 if (GTK_LIST_STORE_IS_SORTED (drag_dest))
1635 return FALSE;
1636
1637 if (!gtk_tree_get_row_drag_data (value,
1638 tree_model: &src_model,
1639 path: &src_path))
1640 goto out;
1641
1642 if (src_model != GTK_TREE_MODEL (drag_dest))
1643 goto out;
1644
1645 if (gtk_tree_path_get_depth (path: dest_path) != 1)
1646 goto out;
1647
1648 /* can drop before any existing node, or before one past any existing. */
1649
1650 indices = gtk_tree_path_get_indices (path: dest_path);
1651
1652 if (indices[0] <= g_sequence_get_length (GTK_LIST_STORE (drag_dest)->priv->seq))
1653 retval = TRUE;
1654
1655 out:
1656 if (src_path)
1657 gtk_tree_path_free (path: src_path);
1658
1659 return retval;
1660}
1661
1662/* Sorting and reordering */
1663
1664/* Reordering */
1665static int
1666gtk_list_store_reorder_func (GSequenceIter *a,
1667 GSequenceIter *b,
1668 gpointer user_data)
1669{
1670 GHashTable *new_positions = user_data;
1671 int apos = GPOINTER_TO_INT (g_hash_table_lookup (new_positions, a));
1672 int bpos = GPOINTER_TO_INT (g_hash_table_lookup (new_positions, b));
1673
1674 if (apos < bpos)
1675 return -1;
1676 if (apos > bpos)
1677 return 1;
1678 return 0;
1679}
1680
1681/**
1682 * gtk_list_store_reorder:
1683 * @store: A `GtkListStore`.
1684 * @new_order: (array zero-terminated=1): an array of integers mapping the new
1685 * position of each child to its old position before the re-ordering,
1686 * i.e. @new_order`[newpos] = oldpos`. It must have
1687 * exactly as many items as the list store’s length.
1688 *
1689 * Reorders @store to follow the order indicated by @new_order. Note that
1690 * this function only works with unsorted stores.
1691 **/
1692void
1693gtk_list_store_reorder (GtkListStore *store,
1694 int *new_order)
1695{
1696 GtkListStorePrivate *priv;
1697 int i;
1698 GtkTreePath *path;
1699 GHashTable *new_positions;
1700 GSequenceIter *ptr;
1701 int *order;
1702
1703 g_return_if_fail (GTK_IS_LIST_STORE (store));
1704 g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store));
1705 g_return_if_fail (new_order != NULL);
1706
1707 priv = store->priv;
1708
1709 order = g_new (int, g_sequence_get_length (priv->seq));
1710 for (i = 0; i < g_sequence_get_length (seq: priv->seq); i++)
1711 order[new_order[i]] = i;
1712
1713 new_positions = g_hash_table_new (hash_func: g_direct_hash, key_equal_func: g_direct_equal);
1714
1715 ptr = g_sequence_get_begin_iter (seq: priv->seq);
1716 i = 0;
1717 while (!g_sequence_iter_is_end (iter: ptr))
1718 {
1719 g_hash_table_insert (hash_table: new_positions, key: ptr, GINT_TO_POINTER (order[i++]));
1720
1721 ptr = g_sequence_iter_next (iter: ptr);
1722 }
1723 g_free (mem: order);
1724
1725 g_sequence_sort_iter (seq: priv->seq, cmp_func: gtk_list_store_reorder_func, cmp_data: new_positions);
1726
1727 g_hash_table_destroy (hash_table: new_positions);
1728
1729 /* emit signal */
1730 path = gtk_tree_path_new ();
1731 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store),
1732 path, NULL, new_order);
1733 gtk_tree_path_free (path);
1734}
1735
1736static GHashTable *
1737save_positions (GSequence *seq)
1738{
1739 GHashTable *positions = g_hash_table_new (hash_func: g_direct_hash, key_equal_func: g_direct_equal);
1740 GSequenceIter *ptr;
1741
1742 ptr = g_sequence_get_begin_iter (seq);
1743 while (!g_sequence_iter_is_end (iter: ptr))
1744 {
1745 g_hash_table_insert (hash_table: positions, key: ptr,
1746 GINT_TO_POINTER (g_sequence_iter_get_position (ptr)));
1747 ptr = g_sequence_iter_next (iter: ptr);
1748 }
1749
1750 return positions;
1751}
1752
1753static int *
1754generate_order (GSequence *seq,
1755 GHashTable *old_positions)
1756{
1757 GSequenceIter *ptr;
1758 int *order = g_new (int, g_sequence_get_length (seq));
1759 int i;
1760
1761 i = 0;
1762 ptr = g_sequence_get_begin_iter (seq);
1763 while (!g_sequence_iter_is_end (iter: ptr))
1764 {
1765 int old_pos = GPOINTER_TO_INT (g_hash_table_lookup (old_positions, ptr));
1766 order[i++] = old_pos;
1767 ptr = g_sequence_iter_next (iter: ptr);
1768 }
1769
1770 g_hash_table_destroy (hash_table: old_positions);
1771
1772 return order;
1773}
1774
1775/**
1776 * gtk_list_store_swap:
1777 * @store: A `GtkListStore`.
1778 * @a: A `GtkTreeIter`
1779 * @b: Another `GtkTreeIter`
1780 *
1781 * Swaps @a and @b in @store. Note that this function only works with
1782 * unsorted stores.
1783 **/
1784void
1785gtk_list_store_swap (GtkListStore *store,
1786 GtkTreeIter *a,
1787 GtkTreeIter *b)
1788{
1789 GtkListStorePrivate *priv;
1790 GHashTable *old_positions;
1791 int *order;
1792 GtkTreePath *path;
1793
1794 g_return_if_fail (GTK_IS_LIST_STORE (store));
1795 g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store));
1796 g_return_if_fail (iter_is_valid (a, store));
1797 g_return_if_fail (iter_is_valid (b, store));
1798
1799 priv = store->priv;
1800
1801 if (a->user_data == b->user_data)
1802 return;
1803
1804 old_positions = save_positions (seq: priv->seq);
1805
1806 g_sequence_swap (a: a->user_data, b: b->user_data);
1807
1808 order = generate_order (seq: priv->seq, old_positions);
1809 path = gtk_tree_path_new ();
1810
1811 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store),
1812 path, NULL, new_order: order);
1813
1814 gtk_tree_path_free (path);
1815 g_free (mem: order);
1816}
1817
1818static void
1819gtk_list_store_move_to (GtkListStore *store,
1820 GtkTreeIter *iter,
1821 int new_pos)
1822{
1823 GtkListStorePrivate *priv = store->priv;
1824 GHashTable *old_positions;
1825 GtkTreePath *path;
1826 int *order;
1827
1828 old_positions = save_positions (seq: priv->seq);
1829
1830 g_sequence_move (src: iter->user_data, dest: g_sequence_get_iter_at_pos (seq: priv->seq, pos: new_pos));
1831
1832 order = generate_order (seq: priv->seq, old_positions);
1833
1834 path = gtk_tree_path_new ();
1835 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (store),
1836 path, NULL, new_order: order);
1837 gtk_tree_path_free (path);
1838 g_free (mem: order);
1839}
1840
1841/**
1842 * gtk_list_store_move_before:
1843 * @store: A `GtkListStore`.
1844 * @iter: A `GtkTreeIter`
1845 * @position: (nullable): A `GtkTreeIter`
1846 *
1847 * Moves @iter in @store to the position before @position. Note that this
1848 * function only works with unsorted stores. If @position is %NULL, @iter
1849 * will be moved to the end of the list.
1850 **/
1851void
1852gtk_list_store_move_before (GtkListStore *store,
1853 GtkTreeIter *iter,
1854 GtkTreeIter *position)
1855{
1856 int pos;
1857
1858 g_return_if_fail (GTK_IS_LIST_STORE (store));
1859 g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store));
1860 g_return_if_fail (iter_is_valid (iter, store));
1861 if (position)
1862 g_return_if_fail (iter_is_valid (position, store));
1863
1864 if (position)
1865 pos = g_sequence_iter_get_position (iter: position->user_data);
1866 else
1867 pos = -1;
1868
1869 gtk_list_store_move_to (store, iter, new_pos: pos);
1870}
1871
1872/**
1873 * gtk_list_store_move_after:
1874 * @store: A `GtkListStore`.
1875 * @iter: A `GtkTreeIter`
1876 * @position: (nullable): A `GtkTreeIter`
1877 *
1878 * Moves @iter in @store to the position after @position. Note that this
1879 * function only works with unsorted stores. If @position is %NULL, @iter
1880 * will be moved to the start of the list.
1881 **/
1882void
1883gtk_list_store_move_after (GtkListStore *store,
1884 GtkTreeIter *iter,
1885 GtkTreeIter *position)
1886{
1887 int pos;
1888
1889 g_return_if_fail (GTK_IS_LIST_STORE (store));
1890 g_return_if_fail (!GTK_LIST_STORE_IS_SORTED (store));
1891 g_return_if_fail (iter_is_valid (iter, store));
1892 if (position)
1893 g_return_if_fail (iter_is_valid (position, store));
1894
1895 if (position)
1896 pos = g_sequence_iter_get_position (iter: position->user_data) + 1;
1897 else
1898 pos = 0;
1899
1900 gtk_list_store_move_to (store, iter, new_pos: pos);
1901}
1902
1903/* Sorting */
1904static int
1905gtk_list_store_compare_func (GSequenceIter *a,
1906 GSequenceIter *b,
1907 gpointer user_data)
1908{
1909 GtkListStore *list_store = user_data;
1910 GtkListStorePrivate *priv = list_store->priv;
1911 GtkTreeIter iter_a;
1912 GtkTreeIter iter_b;
1913 int retval;
1914 GtkTreeIterCompareFunc func;
1915 gpointer data;
1916
1917 if (priv->sort_column_id != -1)
1918 {
1919 GtkTreeDataSortHeader *header;
1920
1921 header = _gtk_tree_data_list_get_header (header_list: priv->sort_list,
1922 sort_column_id: priv->sort_column_id);
1923 g_return_val_if_fail (header != NULL, 0);
1924 g_return_val_if_fail (header->func != NULL, 0);
1925
1926 func = header->func;
1927 data = header->data;
1928 }
1929 else
1930 {
1931 g_return_val_if_fail (priv->default_sort_func != NULL, 0);
1932 func = priv->default_sort_func;
1933 data = priv->default_sort_data;
1934 }
1935
1936 iter_a.stamp = priv->stamp;
1937 iter_a.user_data = (gpointer)a;
1938 iter_b.stamp = priv->stamp;
1939 iter_b.user_data = (gpointer)b;
1940
1941 g_assert (iter_is_valid (&iter_a, list_store));
1942 g_assert (iter_is_valid (&iter_b, list_store));
1943
1944 retval = (* func) (GTK_TREE_MODEL (list_store), &iter_a, &iter_b, data);
1945
1946 if (priv->order == GTK_SORT_DESCENDING)
1947 {
1948 if (retval > 0)
1949 retval = -1;
1950 else if (retval < 0)
1951 retval = 1;
1952 }
1953
1954 return retval;
1955}
1956
1957static void
1958gtk_list_store_sort (GtkListStore *list_store)
1959{
1960 GtkListStorePrivate *priv = list_store->priv;
1961 int *new_order;
1962 GtkTreePath *path;
1963 GHashTable *old_positions;
1964
1965 if (!GTK_LIST_STORE_IS_SORTED (list_store) ||
1966 g_sequence_get_length (seq: priv->seq) <= 1)
1967 return;
1968
1969 old_positions = save_positions (seq: priv->seq);
1970
1971 g_sequence_sort_iter (seq: priv->seq, cmp_func: gtk_list_store_compare_func, cmp_data: list_store);
1972
1973 /* Let the world know about our new order */
1974 new_order = generate_order (seq: priv->seq, old_positions);
1975
1976 path = gtk_tree_path_new ();
1977 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store),
1978 path, NULL, new_order);
1979 gtk_tree_path_free (path);
1980 g_free (mem: new_order);
1981}
1982
1983static gboolean
1984iter_is_sorted (GtkListStore *list_store,
1985 GtkTreeIter *iter)
1986{
1987 GSequenceIter *cmp;
1988
1989 if (!g_sequence_iter_is_begin (iter: iter->user_data))
1990 {
1991 cmp = g_sequence_iter_prev (iter: iter->user_data);
1992 if (gtk_list_store_compare_func (a: cmp, b: iter->user_data, user_data: list_store) > 0)
1993 return FALSE;
1994 }
1995
1996 cmp = g_sequence_iter_next (iter: iter->user_data);
1997 if (!g_sequence_iter_is_end (iter: cmp))
1998 {
1999 if (gtk_list_store_compare_func (a: iter->user_data, b: cmp, user_data: list_store) > 0)
2000 return FALSE;
2001 }
2002
2003 return TRUE;
2004}
2005
2006static void
2007gtk_list_store_sort_iter_changed (GtkListStore *list_store,
2008 GtkTreeIter *iter,
2009 int column)
2010
2011{
2012 GtkListStorePrivate *priv = list_store->priv;
2013 GtkTreePath *path;
2014
2015 path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter);
2016 gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter);
2017 gtk_tree_path_free (path);
2018
2019 if (!iter_is_sorted (list_store, iter))
2020 {
2021 GHashTable *old_positions;
2022 int *order;
2023
2024 old_positions = save_positions (seq: priv->seq);
2025 g_sequence_sort_changed_iter (iter: iter->user_data,
2026 iter_cmp: gtk_list_store_compare_func,
2027 cmp_data: list_store);
2028 order = generate_order (seq: priv->seq, old_positions);
2029 path = gtk_tree_path_new ();
2030 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (list_store),
2031 path, NULL, new_order: order);
2032 gtk_tree_path_free (path);
2033 g_free (mem: order);
2034 }
2035}
2036
2037static gboolean
2038gtk_list_store_get_sort_column_id (GtkTreeSortable *sortable,
2039 int *sort_column_id,
2040 GtkSortType *order)
2041{
2042 GtkListStore *list_store = GTK_LIST_STORE (sortable);
2043 GtkListStorePrivate *priv = list_store->priv;
2044
2045 if (sort_column_id)
2046 * sort_column_id = priv->sort_column_id;
2047 if (order)
2048 * order = priv->order;
2049
2050 if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID ||
2051 priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
2052 return FALSE;
2053
2054 return TRUE;
2055}
2056
2057static void
2058gtk_list_store_set_sort_column_id (GtkTreeSortable *sortable,
2059 int sort_column_id,
2060 GtkSortType order)
2061{
2062 GtkListStore *list_store = GTK_LIST_STORE (sortable);
2063 GtkListStorePrivate *priv = list_store->priv;
2064
2065 if ((priv->sort_column_id == sort_column_id) &&
2066 (priv->order == order))
2067 return;
2068
2069 if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
2070 {
2071 if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
2072 {
2073 GtkTreeDataSortHeader *header = NULL;
2074
2075 header = _gtk_tree_data_list_get_header (header_list: priv->sort_list,
2076 sort_column_id);
2077
2078 /* We want to make sure that we have a function */
2079 g_return_if_fail (header != NULL);
2080 g_return_if_fail (header->func != NULL);
2081 }
2082 else
2083 {
2084 g_return_if_fail (priv->default_sort_func != NULL);
2085 }
2086 }
2087
2088
2089 priv->sort_column_id = sort_column_id;
2090 priv->order = order;
2091
2092 gtk_tree_sortable_sort_column_changed (sortable);
2093
2094 gtk_list_store_sort (list_store);
2095}
2096
2097static void
2098gtk_list_store_set_sort_func (GtkTreeSortable *sortable,
2099 int sort_column_id,
2100 GtkTreeIterCompareFunc func,
2101 gpointer data,
2102 GDestroyNotify destroy)
2103{
2104 GtkListStore *list_store = GTK_LIST_STORE (sortable);
2105 GtkListStorePrivate *priv = list_store->priv;
2106
2107 priv->sort_list = _gtk_tree_data_list_set_header (header_list: priv->sort_list,
2108 sort_column_id,
2109 func, data, destroy);
2110
2111 if (priv->sort_column_id == sort_column_id)
2112 gtk_list_store_sort (list_store);
2113}
2114
2115static void
2116gtk_list_store_set_default_sort_func (GtkTreeSortable *sortable,
2117 GtkTreeIterCompareFunc func,
2118 gpointer data,
2119 GDestroyNotify destroy)
2120{
2121 GtkListStore *list_store = GTK_LIST_STORE (sortable);
2122 GtkListStorePrivate *priv = list_store->priv;
2123
2124 if (priv->default_sort_destroy)
2125 {
2126 GDestroyNotify d = priv->default_sort_destroy;
2127
2128 priv->default_sort_destroy = NULL;
2129 d (priv->default_sort_data);
2130 }
2131
2132 priv->default_sort_func = func;
2133 priv->default_sort_data = data;
2134 priv->default_sort_destroy = destroy;
2135
2136 if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
2137 gtk_list_store_sort (list_store);
2138}
2139
2140static gboolean
2141gtk_list_store_has_default_sort_func (GtkTreeSortable *sortable)
2142{
2143 GtkListStore *list_store = GTK_LIST_STORE (sortable);
2144 GtkListStorePrivate *priv = list_store->priv;
2145
2146 return (priv->default_sort_func != NULL);
2147}
2148
2149
2150/**
2151 * gtk_list_store_insert_with_values:
2152 * @list_store: A `GtkListStore`
2153 * @iter: (out) (optional): An unset `GtkTreeIter` to set to the new row
2154 * @position: position to insert the new row, or -1 to append after existing
2155 * rows
2156 * @...: pairs of column number and value, terminated with -1
2157 *
2158 * Creates a new row at @position. @iter will be changed to point to this new
2159 * row. If @position is -1, or larger than the number of rows in the list, then
2160 * the new row will be appended to the list. The row will be filled with the
2161 * values given to this function.
2162 *
2163 * Calling
2164 * `gtk_list_store_insert_with_values (list_store, iter, position...)`
2165 * has the same effect as calling:
2166 *
2167 * |[<!-- language="C" -->
2168 * static void
2169 * insert_value (GtkListStore *list_store,
2170 * GtkTreeIter *iter,
2171 * int position)
2172 * {
2173 * gtk_list_store_insert (list_store, iter, position);
2174 * gtk_list_store_set (list_store,
2175 * iter
2176 * // ...
2177 * );
2178 * }
2179 * ]|
2180 *
2181 * with the difference that the former will only emit `GtkTreeModel`::row-inserted
2182 * once, while the latter will emit `GtkTreeModel`::row-inserted,
2183 * `GtkTreeModel`::row-changed and, if the list store is sorted,
2184 * `GtkTreeModel`::rows-reordered for every inserted value.
2185 *
2186 * Since emitting the `GtkTreeModel::rows-reordered` signal repeatedly can
2187 * affect the performance of the program, gtk_list_store_insert_with_values()
2188 * should generally be preferred when inserting rows in a sorted list store.
2189 */
2190void
2191gtk_list_store_insert_with_values (GtkListStore *list_store,
2192 GtkTreeIter *iter,
2193 int position,
2194 ...)
2195{
2196 GtkListStorePrivate *priv;
2197 GtkTreePath *path;
2198 GSequence *seq;
2199 GSequenceIter *ptr;
2200 GtkTreeIter tmp_iter;
2201 int length;
2202 gboolean changed = FALSE;
2203 gboolean maybe_need_sort = FALSE;
2204 va_list var_args;
2205
2206 /* FIXME: refactor to reduce overlap with gtk_list_store_set() */
2207 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
2208
2209 priv = list_store->priv;
2210
2211 if (!iter)
2212 iter = &tmp_iter;
2213
2214 priv->columns_dirty = TRUE;
2215
2216 seq = priv->seq;
2217
2218 length = g_sequence_get_length (seq);
2219 if (position > length || position < 0)
2220 position = length;
2221
2222 ptr = g_sequence_get_iter_at_pos (seq, pos: position);
2223 ptr = g_sequence_insert_before (iter: ptr, NULL);
2224
2225 iter->stamp = priv->stamp;
2226 iter->user_data = ptr;
2227
2228 g_assert (iter_is_valid (iter, list_store));
2229
2230 priv->length++;
2231
2232 va_start (var_args, position);
2233 gtk_list_store_set_valist_internal (list_store, iter,
2234 emit_signal: &changed, maybe_need_sort: &maybe_need_sort,
2235 var_args);
2236 va_end (var_args);
2237
2238 /* Don't emit rows_reordered here */
2239 if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store))
2240 g_sequence_sort_changed_iter (iter: iter->user_data,
2241 iter_cmp: gtk_list_store_compare_func,
2242 cmp_data: list_store);
2243
2244 /* Just emit row_inserted */
2245 path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter);
2246 gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter);
2247 gtk_tree_path_free (path);
2248}
2249
2250
2251/**
2252 * gtk_list_store_insert_with_valuesv: (rename-to gtk_list_store_insert_with_values)
2253 * @list_store: A `GtkListStore`
2254 * @iter: (out) (optional): An unset `GtkTreeIter` to set to the new row
2255 * @position: position to insert the new row, or -1 for last
2256 * @columns: (array length=n_values): an array of column numbers
2257 * @values: (array length=n_values): an array of GValues
2258 * @n_values: the length of the @columns and @values arrays
2259 *
2260 * A variant of gtk_list_store_insert_with_values() which
2261 * takes the columns and values as two arrays, instead of
2262 * varargs.
2263 *
2264 * This function is mainly intended for language-bindings.
2265 */
2266void
2267gtk_list_store_insert_with_valuesv (GtkListStore *list_store,
2268 GtkTreeIter *iter,
2269 int position,
2270 int *columns,
2271 GValue *values,
2272 int n_values)
2273{
2274 GtkListStorePrivate *priv;
2275 GtkTreePath *path;
2276 GSequence *seq;
2277 GSequenceIter *ptr;
2278 GtkTreeIter tmp_iter;
2279 int length;
2280 gboolean changed = FALSE;
2281 gboolean maybe_need_sort = FALSE;
2282
2283 /* FIXME refactor to reduce overlap with
2284 * gtk_list_store_insert_with_values()
2285 */
2286 g_return_if_fail (GTK_IS_LIST_STORE (list_store));
2287
2288 priv = list_store->priv;
2289
2290 if (!iter)
2291 iter = &tmp_iter;
2292
2293 priv->columns_dirty = TRUE;
2294
2295 seq = priv->seq;
2296
2297 length = g_sequence_get_length (seq);
2298 if (position > length || position < 0)
2299 position = length;
2300
2301 ptr = g_sequence_get_iter_at_pos (seq, pos: position);
2302 ptr = g_sequence_insert_before (iter: ptr, NULL);
2303
2304 iter->stamp = priv->stamp;
2305 iter->user_data = ptr;
2306
2307 g_assert (iter_is_valid (iter, list_store));
2308
2309 priv->length++;
2310
2311 gtk_list_store_set_vector_internal (list_store, iter,
2312 emit_signal: &changed, maybe_need_sort: &maybe_need_sort,
2313 columns, values, n_values);
2314
2315 /* Don't emit rows_reordered here */
2316 if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store))
2317 g_sequence_sort_changed_iter (iter: iter->user_data,
2318 iter_cmp: gtk_list_store_compare_func,
2319 cmp_data: list_store);
2320
2321 /* Just emit row_inserted */
2322 path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter);
2323 gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter);
2324 gtk_tree_path_free (path);
2325}
2326
2327/* GtkBuildable custom tag implementation
2328 *
2329 * <columns>
2330 * <column type="..."/>
2331 * <column type="..."/>
2332 * </columns>
2333 */
2334typedef struct {
2335 gboolean translatable;
2336 char *context;
2337 int id;
2338} ColInfo;
2339
2340typedef struct {
2341 GtkBuilder *builder;
2342 GObject *object;
2343 GSList *column_type_names;
2344 GType *column_types;
2345 GValue *values;
2346 int *colids;
2347 ColInfo **columns;
2348 int last_row;
2349 int n_columns;
2350 int row_column;
2351 gboolean is_data;
2352 const char *domain;
2353} SubParserData;
2354
2355static void
2356list_store_start_element (GtkBuildableParseContext *context,
2357 const char *element_name,
2358 const char **names,
2359 const char **values,
2360 gpointer user_data,
2361 GError **error)
2362{
2363 SubParserData *data = (SubParserData*)user_data;
2364
2365 if (strcmp (s1: element_name, s2: "col") == 0)
2366 {
2367 int id = -1;
2368 const char *id_str;
2369 const char *msg_context = NULL;
2370 gboolean translatable = FALSE;
2371 ColInfo *info;
2372 GValue val = G_VALUE_INIT;
2373
2374 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "row", error))
2375 return;
2376
2377 if (data->row_column >= data->n_columns)
2378 {
2379 g_set_error (err: error,
2380 GTK_BUILDER_ERROR, code: GTK_BUILDER_ERROR_INVALID_VALUE,
2381 format: "Too many columns, maximum is %d", data->n_columns - 1);
2382 _gtk_builder_prefix_error (builder: data->builder, context, error);
2383 return;
2384 }
2385
2386 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
2387 first_type: G_MARKUP_COLLECT_STRING, first_attr: "id", &id_str,
2388 G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable,
2389 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL,
2390 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &msg_context,
2391 G_MARKUP_COLLECT_INVALID))
2392 {
2393 _gtk_builder_prefix_error (builder: data->builder, context, error);
2394 return;
2395 }
2396
2397 if (!gtk_builder_value_from_string_type (builder: data->builder, G_TYPE_INT, string: id_str, value: &val, error))
2398 {
2399 _gtk_builder_prefix_error (builder: data->builder, context, error);
2400 return;
2401 }
2402
2403 id = g_value_get_int (value: &val);
2404 if (id < 0 || id >= data->n_columns)
2405 {
2406 g_set_error (err: error,
2407 GTK_BUILDER_ERROR, code: GTK_BUILDER_ERROR_INVALID_VALUE,
2408 format: "id value %d out of range", id);
2409 _gtk_builder_prefix_error (builder: data->builder, context, error);
2410 return;
2411 }
2412
2413 info = g_slice_new0 (ColInfo);
2414 info->translatable = translatable;
2415 info->context = g_strdup (str: msg_context);
2416 info->id = id;
2417
2418 data->colids[data->row_column] = id;
2419 data->columns[data->row_column] = info;
2420 data->row_column++;
2421 data->is_data = TRUE;
2422 }
2423 else if (strcmp (s1: element_name, s2: "row") == 0)
2424 {
2425 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "data", error))
2426 return;
2427
2428 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
2429 first_type: G_MARKUP_COLLECT_INVALID, NULL, NULL,
2430 G_MARKUP_COLLECT_INVALID))
2431 _gtk_builder_prefix_error (builder: data->builder, context, error);
2432 }
2433 else if (strcmp (s1: element_name, s2: "columns") == 0 ||
2434 strcmp (s1: element_name, s2: "data") == 0)
2435 {
2436 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "object", error))
2437 return;
2438
2439 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
2440 first_type: G_MARKUP_COLLECT_INVALID, NULL, NULL,
2441 G_MARKUP_COLLECT_INVALID))
2442 _gtk_builder_prefix_error (builder: data->builder, context, error);
2443 }
2444 else if (strcmp (s1: element_name, s2: "column") == 0)
2445 {
2446 const char *type;
2447
2448 if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "columns", error))
2449 return;
2450
2451 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
2452 first_type: G_MARKUP_COLLECT_STRING, first_attr: "type", &type,
2453 G_MARKUP_COLLECT_INVALID))
2454 {
2455 _gtk_builder_prefix_error (builder: data->builder, context, error);
2456 return;
2457 }
2458
2459 data->column_type_names = g_slist_prepend (list: data->column_type_names, data: g_strdup (str: type));
2460 }
2461 else
2462 {
2463 _gtk_builder_error_unhandled_tag (builder: data->builder, context,
2464 object: "GtkListStore", element_name,
2465 error);
2466 }
2467}
2468
2469static void
2470list_store_end_element (GtkBuildableParseContext *context,
2471 const char *element_name,
2472 gpointer user_data,
2473 GError **error)
2474{
2475 SubParserData *data = (SubParserData*)user_data;
2476
2477 g_assert (data->builder);
2478
2479 if (strcmp (s1: element_name, s2: "row") == 0)
2480 {
2481 GtkTreeIter iter;
2482 int i;
2483
2484 gtk_list_store_insert_with_valuesv (GTK_LIST_STORE (data->object),
2485 iter: &iter,
2486 position: data->last_row,
2487 columns: data->colids,
2488 values: data->values,
2489 n_values: data->row_column);
2490 for (i = 0; i < data->row_column; i++)
2491 {
2492 ColInfo *info = data->columns[i];
2493 g_free (mem: info->context);
2494 g_slice_free (ColInfo, info);
2495 data->columns[i] = NULL;
2496 g_value_unset (value: &data->values[i]);
2497 }
2498 g_free (mem: data->values);
2499 data->values = g_new0 (GValue, data->n_columns);
2500 data->last_row++;
2501 data->row_column = 0;
2502 }
2503 else if (strcmp (s1: element_name, s2: "columns") == 0)
2504 {
2505 GType *column_types;
2506 GSList *l;
2507 int i;
2508 GType type;
2509
2510 data->column_type_names = g_slist_reverse (list: data->column_type_names);
2511 column_types = g_new0 (GType, g_slist_length (data->column_type_names));
2512
2513 for (l = data->column_type_names, i = 0; l; l = l->next, i++)
2514 {
2515 type = gtk_builder_get_type_from_name (builder: data->builder, type_name: l->data);
2516 if (type == G_TYPE_INVALID)
2517 {
2518 g_warning ("Unknown type %s specified in treemodel %s",
2519 (const char *)l->data,
2520 gtk_buildable_get_buildable_id (GTK_BUILDABLE (data->object)));
2521 continue;
2522 }
2523 column_types[i] = type;
2524
2525 g_free (mem: l->data);
2526 }
2527
2528 gtk_list_store_set_column_types (GTK_LIST_STORE (data->object), n_columns: i, types: column_types);
2529
2530 g_free (mem: column_types);
2531 }
2532 else if (strcmp (s1: element_name, s2: "col") == 0)
2533 {
2534 data->is_data = FALSE;
2535 }
2536}
2537
2538static void
2539list_store_text (GtkBuildableParseContext *context,
2540 const char *text,
2541 gsize text_len,
2542 gpointer user_data,
2543 GError **error)
2544{
2545 SubParserData *data = (SubParserData*)user_data;
2546 int i;
2547 char *string;
2548 ColInfo *info;
2549
2550 if (!data->is_data)
2551 return;
2552
2553 i = data->row_column - 1;
2554 info = data->columns[i];
2555
2556 string = g_strndup (str: text, n: text_len);
2557 if (info->translatable && text_len)
2558 {
2559 char *translated;
2560
2561 /* FIXME: This will not use the domain set in the .ui file,
2562 * since the parser is not telling the builder about the domain.
2563 * However, it will work for gtk_builder_set_translation_domain() calls.
2564 */
2565 translated = g_strdup (str: _gtk_builder_parser_translate (domain: data->domain,
2566 context: info->context,
2567 text: string));
2568 g_free (mem: string);
2569 string = translated;
2570 }
2571
2572 if (!gtk_builder_value_from_string_type (builder: data->builder,
2573 type: data->column_types[info->id],
2574 string,
2575 value: &data->values[i],
2576 error))
2577 {
2578 _gtk_builder_prefix_error (builder: data->builder, context, error);
2579 }
2580 g_free (mem: string);
2581}
2582
2583static const GtkBuildableParser list_store_parser =
2584 {
2585 list_store_start_element,
2586 list_store_end_element,
2587 list_store_text
2588 };
2589
2590static gboolean
2591gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable,
2592 GtkBuilder *builder,
2593 GObject *child,
2594 const char *tagname,
2595 GtkBuildableParser *parser,
2596 gpointer *parser_data)
2597{
2598 SubParserData *data;
2599
2600 if (child)
2601 return FALSE;
2602
2603 if (strcmp (s1: tagname, s2: "columns") == 0)
2604 {
2605 data = g_slice_new0 (SubParserData);
2606 data->builder = builder;
2607 data->object = G_OBJECT (buildable);
2608 data->column_type_names = NULL;
2609
2610 *parser = list_store_parser;
2611 *parser_data = data;
2612
2613 return TRUE;
2614 }
2615 else if (strcmp (s1: tagname, s2: "data") == 0)
2616 {
2617 int n_columns = gtk_list_store_get_n_columns (GTK_TREE_MODEL (buildable));
2618 if (n_columns == 0)
2619 g_error ("Cannot append data to an empty model");
2620
2621 data = g_slice_new0 (SubParserData);
2622 data->builder = builder;
2623 data->object = G_OBJECT (buildable);
2624 data->values = g_new0 (GValue, n_columns);
2625 data->colids = g_new0 (int, n_columns);
2626 data->columns = g_new0 (ColInfo*, n_columns);
2627 data->column_types = GTK_LIST_STORE (buildable)->priv->column_headers;
2628 data->n_columns = n_columns;
2629 data->last_row = 0;
2630 data->domain = gtk_builder_get_translation_domain (builder);
2631
2632 *parser = list_store_parser;
2633 *parser_data = data;
2634
2635 return TRUE;
2636 }
2637
2638 return FALSE;
2639}
2640
2641static void
2642gtk_list_store_buildable_custom_tag_end (GtkBuildable *buildable,
2643 GtkBuilder *builder,
2644 GObject *child,
2645 const char *tagname,
2646 gpointer parser_data)
2647{
2648 SubParserData *data = (SubParserData*)parser_data;
2649
2650 if (strcmp (s1: tagname, s2: "columns") == 0)
2651 {
2652 g_slist_free (list: data->column_type_names);
2653 g_slice_free (SubParserData, data);
2654 }
2655 else if (strcmp (s1: tagname, s2: "data") == 0)
2656 {
2657 int i;
2658 for (i = 0; i < data->n_columns; i++)
2659 {
2660 ColInfo *info = data->columns[i];
2661 if (info)
2662 {
2663 g_free (mem: info->context);
2664 g_slice_free (ColInfo, info);
2665 }
2666 }
2667 g_free (mem: data->colids);
2668 g_free (mem: data->columns);
2669 g_free (mem: data->values);
2670 g_slice_free (SubParserData, data);
2671 }
2672}
2673

source code of gtk/gtk/gtkliststore.c