gtkliststore.c 74 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* gtkliststore.c
 * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * 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.
 */

20
#include "config.h"
Johan Dahlin's avatar
Johan Dahlin committed
21 22
#include <errno.h>
#include <stdlib.h>
23
#include <string.h>
Manish Singh's avatar
Manish Singh committed
24
#include <gobject/gvaluecollector.h>
25 26 27
#include "gtktreemodel.h"
#include "gtkliststore.h"
#include "gtktreedatalist.h"
28
#include "gtktreednd.h"
Matthias Clasen's avatar
Matthias Clasen committed
29
#include "gtkintl.h"
Johan Dahlin's avatar
Johan Dahlin committed
30
#include "gtkbuildable.h"
31
#include "gtkbuilderprivate.h"
32

33

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/**
 * 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 <link linkend="gtktreednd">drag and
 * drop</link> 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 #GObject<!-- -->s 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
 * #GdkPixbuf<!-- -->s stored.
Benjamin Otte's avatar
Benjamin Otte committed
56
 *
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
 * <example>
 * <title>Creating a simple list store.</title>
 * <programlisting>
 * 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);
 * }
 * </programlisting>
 * </example>
Benjamin Otte's avatar
Benjamin Otte committed
110
 *
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
 * <refsect2>
 * <title>Performance Considerations</title>
 * 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 #GtkTreeIter<!-- -->s 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.
 * </refsect2>
 * <refsect2>
 * <title>Atomic Operations</title>
 * 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.
 * </refsect2>
 * <refsect2 id="GtkListStore-BUILDER-UI">
 * <title>GtkListStore as GtkBuildable</title>
 * <para>
 * The GtkListStore implementation of the GtkBuildable interface allows
 * to specify the model columns with a &lt;columns&gt; element that may
 * contain multiple &lt;column&gt; 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 &lt;data&gt; element. It can contain
 * multiple &lt;row&gt; elements, each specifying to content for one
 * row of the list model. Inside a &lt;row&gt;, the &lt;col&gt; 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,
 * <emphasis>data</emphasis>, not <emphasis>presentation</emphasis>,
 * and common wisdom is to separate the two, as far as possible.
 * <!-- FIXME a bit inconclusive -->
 *
 * <example>
 * <title>A UI Definition fragment for a list store</title>
 * <programlisting><![CDATA[
 * <object class="GtkListStore">
 *   <columns>
 *     <column type="gchararray"/>
 *     <column type="gchararray"/>
 *     <column type="gint"/>
 *   </columns>
 *   <data>
 *     <row>
 *       <col id="0">John</col>
 *       <col id="1">Doe</col>
 *       <col id="2">25</col>
 *     </row>
 *     <row>
 *       <col id="0">Johan</col>
 *       <col id="1">Dahlin</col>
 *       <col id="2">50</col>
 *     </row>
 *   </data>
 * </object>
 * ]]></programlisting>
 * </example>
 * </para>
 * </refsect2>
 */


188
struct _GtkListStorePrivate
189 190 191 192 193 194 195 196 197 198 199 200
{
  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;

201 202
  GtkSortType order;

203 204 205 206 207 208 209
  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)
210
static void         gtk_list_store_tree_model_init (GtkTreeModelIface *iface);
211 212
static void         gtk_list_store_drag_source_init(GtkTreeDragSourceIface *iface);
static void         gtk_list_store_drag_dest_init  (GtkTreeDragDestIface   *iface);
213
static void         gtk_list_store_sortable_init   (GtkTreeSortableIface   *iface);
Johan Dahlin's avatar
Johan Dahlin committed
214
static void         gtk_list_store_buildable_init  (GtkBuildableIface      *iface);
215
static void         gtk_list_store_finalize        (GObject           *object);
216
static GtkTreeModelFlags gtk_list_store_get_flags  (GtkTreeModel      *tree_model);
217
static gint         gtk_list_store_get_n_columns   (GtkTreeModel      *tree_model);
218 219
static GType        gtk_list_store_get_column_type (GtkTreeModel      *tree_model,
						    gint               index);
220 221 222 223 224 225 226 227 228 229 230
static gboolean     gtk_list_store_get_iter        (GtkTreeModel      *tree_model,
						    GtkTreeIter       *iter,
						    GtkTreePath       *path);
static GtkTreePath *gtk_list_store_get_path        (GtkTreeModel      *tree_model,
						    GtkTreeIter       *iter);
static void         gtk_list_store_get_value       (GtkTreeModel      *tree_model,
						    GtkTreeIter       *iter,
						    gint               column,
						    GValue            *value);
static gboolean     gtk_list_store_iter_next       (GtkTreeModel      *tree_model,
						    GtkTreeIter       *iter);
231 232
static gboolean     gtk_list_store_iter_previous   (GtkTreeModel      *tree_model,
						    GtkTreeIter       *iter);
233 234 235 236 237 238 239 240 241 242 243 244 245 246
static gboolean     gtk_list_store_iter_children   (GtkTreeModel      *tree_model,
						    GtkTreeIter       *iter,
						    GtkTreeIter       *parent);
static gboolean     gtk_list_store_iter_has_child  (GtkTreeModel      *tree_model,
						    GtkTreeIter       *iter);
static gint         gtk_list_store_iter_n_children (GtkTreeModel      *tree_model,
						    GtkTreeIter       *iter);
static gboolean     gtk_list_store_iter_nth_child  (GtkTreeModel      *tree_model,
						    GtkTreeIter       *iter,
						    GtkTreeIter       *parent,
						    gint               n);
static gboolean     gtk_list_store_iter_parent     (GtkTreeModel      *tree_model,
						    GtkTreeIter       *iter,
						    GtkTreeIter       *child);
247

248

249 250 251 252 253 254
static void gtk_list_store_set_n_columns   (GtkListStore *list_store,
					    gint          n_columns);
static void gtk_list_store_set_column_type (GtkListStore *list_store,
					    gint          column,
					    GType         type);

255 256
static void gtk_list_store_increment_stamp (GtkListStore *list_store);

257

258
/* Drag and Drop */
Murray Cumming's avatar
Murray Cumming committed
259 260
static gboolean real_gtk_list_store_row_draggable (GtkTreeDragSource *drag_source,
                                                   GtkTreePath       *path);
261 262 263 264 265 266 267 268
static gboolean gtk_list_store_drag_data_delete   (GtkTreeDragSource *drag_source,
                                                   GtkTreePath       *path);
static gboolean gtk_list_store_drag_data_get      (GtkTreeDragSource *drag_source,
                                                   GtkTreePath       *path,
                                                   GtkSelectionData  *selection_data);
static gboolean gtk_list_store_drag_data_received (GtkTreeDragDest   *drag_dest,
                                                   GtkTreePath       *dest,
                                                   GtkSelectionData  *selection_data);
269
static gboolean gtk_list_store_row_drop_possible  (GtkTreeDragDest   *drag_dest,
270 271
                                                   GtkTreePath       *dest_path,
						   GtkSelectionData  *selection_data);
272

273

274
/* sortable */
275 276 277 278 279 280 281 282 283 284 285 286 287 288
static void     gtk_list_store_sort                  (GtkListStore           *list_store);
static void     gtk_list_store_sort_iter_changed     (GtkListStore           *list_store,
						      GtkTreeIter            *iter,
						      gint                    column);
static gboolean gtk_list_store_get_sort_column_id    (GtkTreeSortable        *sortable,
						      gint                   *sort_column_id,
						      GtkSortType            *order);
static void     gtk_list_store_set_sort_column_id    (GtkTreeSortable        *sortable,
						      gint                    sort_column_id,
						      GtkSortType             order);
static void     gtk_list_store_set_sort_func         (GtkTreeSortable        *sortable,
						      gint                    sort_column_id,
						      GtkTreeIterCompareFunc  func,
						      gpointer                data,
289
						      GDestroyNotify          destroy);
290 291 292
static void     gtk_list_store_set_default_sort_func (GtkTreeSortable        *sortable,
						      GtkTreeIterCompareFunc  func,
						      gpointer                data,
293
						      GDestroyNotify          destroy);
294 295
static gboolean gtk_list_store_has_default_sort_func (GtkTreeSortable        *sortable);

296

Johan Dahlin's avatar
Johan Dahlin committed
297 298 299 300 301 302 303 304 305 306 307 308 309
/* 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);

Matthias Clasen's avatar
Matthias Clasen committed
310 311 312 313 314 315 316 317
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,
Johan Dahlin's avatar
Johan Dahlin committed
318 319 320 321
						gtk_list_store_sortable_init)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
						gtk_list_store_buildable_init))

322 323 324 325

static void
gtk_list_store_class_init (GtkListStoreClass *class)
{
326 327 328
  GObjectClass *object_class;

  object_class = (GObjectClass*) class;
329 330

  object_class->finalize = gtk_list_store_finalize;
331

332
  g_type_class_add_private (class, sizeof (GtkListStorePrivate));
333 334 335 336 337
}

static void
gtk_list_store_tree_model_init (GtkTreeModelIface *iface)
{
338
  iface->get_flags = gtk_list_store_get_flags;
339
  iface->get_n_columns = gtk_list_store_get_n_columns;
340
  iface->get_column_type = gtk_list_store_get_column_type;
341
  iface->get_iter = gtk_list_store_get_iter;
342
  iface->get_path = gtk_list_store_get_path;
343
  iface->get_value = gtk_list_store_get_value;
344
  iface->iter_next = gtk_list_store_iter_next;
345
  iface->iter_previous = gtk_list_store_iter_previous;
346 347 348 349 350
  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;
  iface->iter_nth_child = gtk_list_store_iter_nth_child;
  iface->iter_parent = gtk_list_store_iter_parent;
351 352
}

353 354 355
static void
gtk_list_store_drag_source_init (GtkTreeDragSourceIface *iface)
{
Murray Cumming's avatar
Murray Cumming committed
356
  iface->row_draggable = real_gtk_list_store_row_draggable;
357 358 359 360 361
  iface->drag_data_delete = gtk_list_store_drag_data_delete;
  iface->drag_data_get = gtk_list_store_drag_data_get;
}

static void
362
gtk_list_store_drag_dest_init (GtkTreeDragDestIface *iface)
363 364
{
  iface->drag_data_received = gtk_list_store_drag_data_received;
365
  iface->row_drop_possible = gtk_list_store_row_drop_possible;
366 367
}

368 369 370 371 372
static void
gtk_list_store_sortable_init (GtkTreeSortableIface *iface)
{
  iface->get_sort_column_id = gtk_list_store_get_sort_column_id;
  iface->set_sort_column_id = gtk_list_store_set_sort_column_id;
373
  iface->set_sort_func = gtk_list_store_set_sort_func;
374 375
  iface->set_default_sort_func = gtk_list_store_set_default_sort_func;
  iface->has_default_sort_func = gtk_list_store_has_default_sort_func;
376 377
}

Johan Dahlin's avatar
Johan Dahlin committed
378 379 380 381 382 383 384
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;
}

385 386 387
static void
gtk_list_store_init (GtkListStore *list_store)
{
388
  GtkListStorePrivate *priv;
389 390 391

  list_store->priv = G_TYPE_INSTANCE_GET_PRIVATE (list_store,
                                                  GTK_TYPE_LIST_STORE,
392
                                                  GtkListStorePrivate);
393 394 395 396 397 398 399 400
  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;
401 402
}

403 404 405 406 407 408 409 410 411 412 413
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;
}

414 415 416
/**
 * gtk_list_store_new:
 * @n_columns: number of columns in the list store
Matthias Clasen's avatar
Matthias Clasen committed
417
 * @...: all #GType types for the columns, from first to last
418
 *
419
 * Creates a new list store as with @n_columns columns each of the types passed
Matthias Clasen's avatar
Matthias Clasen committed
420 421
 * in.  Note that only types derived from standard GObject fundamental types
 * are supported.
422 423
 *
 * As an example, <literal>gtk_tree_store_new (3, G_TYPE_INT, G_TYPE_STRING,
Matthias Clasen's avatar
Matthias Clasen committed
424 425
 * GDK_TYPE_PIXBUF);</literal> will create a new #GtkListStore with three columns, of type
 * int, string and #GdkPixbuf respectively.
426
 *
427
 * Return value: a new #GtkListStore
Matthias Clasen's avatar
Matthias Clasen committed
428
 */
429
GtkListStore *
430
gtk_list_store_new (gint n_columns,
Soeren Sandmann's avatar
Soeren Sandmann committed
431
		    ...)
432
{
433
  GtkListStore *retval;
434 435 436 437 438
  va_list args;
  gint i;

  g_return_val_if_fail (n_columns > 0, NULL);

Manish Singh's avatar
Manish Singh committed
439
  retval = g_object_new (GTK_TYPE_LIST_STORE, NULL);
440
  gtk_list_store_set_n_columns (retval, n_columns);
441 442

  va_start (args, n_columns);
443

444
  for (i = 0; i < n_columns; i++)
445 446 447
    {
      GType type = va_arg (args, GType);
      if (! _gtk_tree_data_list_check_type (type))
448 449 450 451 452 453 454
        {
          g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
          g_object_unref (retval);
          va_end (args);

          return NULL;
        }
455 456 457

      gtk_list_store_set_column_type (retval, i, type);
    }
458 459 460 461 462 463

  va_end (args);

  return retval;
}

464 465 466 467

/**
 * gtk_list_store_newv:
 * @n_columns: number of columns in the list store
468
 * @types: (array length=n_columns): an array of #GType types for the columns, from first to last
469
 *
Matthias Clasen's avatar
Matthias Clasen committed
470
 * Non-vararg creation function.  Used primarily by language bindings.
471
 *
472
 * Return value: (transfer none): a new #GtkListStore
473
 * Rename to: gtk_list_store_new
474 475 476 477 478 479 480 481 482 483
 **/
GtkListStore *
gtk_list_store_newv (gint   n_columns,
		     GType *types)
{
  GtkListStore *retval;
  gint i;

  g_return_val_if_fail (n_columns > 0, NULL);

Manish Singh's avatar
Manish Singh committed
484
  retval = g_object_new (GTK_TYPE_LIST_STORE, NULL);
485 486 487 488 489 490
  gtk_list_store_set_n_columns (retval, n_columns);

  for (i = 0; i < n_columns; i++)
    {
      if (! _gtk_tree_data_list_check_type (types[i]))
	{
491
	  g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
Manish Singh's avatar
Manish Singh committed
492
	  g_object_unref (retval);
493 494 495 496 497 498 499 500 501
	  return NULL;
	}

      gtk_list_store_set_column_type (retval, i, types[i]);
    }

  return retval;
}

502 503 504 505
/**
 * gtk_list_store_set_column_types:
 * @list_store: A #GtkListStore
 * @n_columns: Number of columns for the list store
506 507
 * @types: (array length=n_columns): An array length n of #GTypes
 *
Matthias Clasen's avatar
Matthias Clasen committed
508 509 510
 * 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
511 512 513 514 515 516 517
 * interface is called.
 **/
void
gtk_list_store_set_column_types (GtkListStore *list_store,
				 gint          n_columns,
				 GType        *types)
{
518
  GtkListStorePrivate *priv;
519 520 521
  gint i;

  g_return_if_fail (GTK_IS_LIST_STORE (list_store));
522 523 524 525

  priv = list_store->priv;

  g_return_if_fail (priv->columns_dirty == 0);
526 527

  gtk_list_store_set_n_columns (list_store, n_columns);
528
  for (i = 0; i < n_columns; i++)
529 530 531
    {
      if (! _gtk_tree_data_list_check_type (types[i]))
	{
532
	  g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
533 534 535 536 537 538
	  continue;
	}
      gtk_list_store_set_column_type (list_store, i, types[i]);
    }
}

539
static void
540 541 542
gtk_list_store_set_n_columns (GtkListStore *list_store,
			      gint          n_columns)
{
543
  GtkListStorePrivate *priv = list_store->priv;
544
  int i;
545

546
  if (priv->n_columns == n_columns)
547 548
    return;

549 550 551 552
  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;
553

554 555 556
  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);
557 558
}

559
static void
560 561 562 563
gtk_list_store_set_column_type (GtkListStore *list_store,
				gint          column,
				GType         type)
{
564
  GtkListStorePrivate *priv = list_store->priv;
565

566 567
  if (!_gtk_tree_data_list_check_type (type))
    {
568
      g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
569 570
      return;
    }
571

572
  priv->column_headers[column] = type;
573 574
}

575 576 577 578
static void
gtk_list_store_finalize (GObject *object)
{
  GtkListStore *list_store = GTK_LIST_STORE (object);
579
  GtkListStorePrivate *priv = list_store->priv;
580

581 582
  g_sequence_foreach (priv->seq,
		      (GFunc) _gtk_tree_data_list_free, priv->column_headers);
Soeren Sandmann's avatar
Soeren Sandmann committed
583

584
  g_sequence_free (priv->seq);
585

586 587 588 589
  _gtk_tree_data_list_header_free (priv->sort_list);
  g_free (priv->column_headers);

  if (priv->default_sort_destroy)
590
    {
591
      GDestroyNotify d = priv->default_sort_destroy;
592

593 594 595
      priv->default_sort_destroy = NULL;
      d (priv->default_sort_data);
      priv->default_sort_data = NULL;
596
    }
597

598
  G_OBJECT_CLASS (gtk_list_store_parent_class)->finalize (object);
599 600
}

601
/* Fulfill the GtkTreeModel requirements */
602
static GtkTreeModelFlags
603 604
gtk_list_store_get_flags (GtkTreeModel *tree_model)
{
605
  return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
606 607
}

608 609 610
static gint
gtk_list_store_get_n_columns (GtkTreeModel *tree_model)
{
611
  GtkListStore *list_store = GTK_LIST_STORE (tree_model);
612
  GtkListStorePrivate *priv = list_store->priv;
613

614
  priv->columns_dirty = TRUE;
615

616
  return priv->n_columns;
617 618
}

619 620 621 622
static GType
gtk_list_store_get_column_type (GtkTreeModel *tree_model,
				gint          index)
{
623
  GtkListStore *list_store = GTK_LIST_STORE (tree_model);
624
  GtkListStorePrivate *priv = list_store->priv;
625

626
  g_return_val_if_fail (index < priv->n_columns, G_TYPE_INVALID);
627

628
  priv->columns_dirty = TRUE;
629

630
  return priv->column_headers[index];
631 632
}

633
static gboolean
634
gtk_list_store_get_iter (GtkTreeModel *tree_model,
635
			 GtkTreeIter  *iter,
636 637
			 GtkTreePath  *path)
{
638
  GtkListStore *list_store = GTK_LIST_STORE (tree_model);
639
  GtkListStorePrivate *priv = list_store->priv;
640
  GSequence *seq;
641
  gint i;
642

643 644 645
  priv->columns_dirty = TRUE;

  seq = priv->seq;
646

647
  i = gtk_tree_path_get_indices (path)[0];
648

649
  if (i >= g_sequence_get_length (seq))
650 651 652 653
    {
      iter->stamp = 0;
      return FALSE;
    }
654

655
  iter->stamp = priv->stamp;
656
  iter->user_data = g_sequence_get_iter_at_pos (seq, i);
657

658
  return TRUE;
659 660 661 662
}

static GtkTreePath *
gtk_list_store_get_path (GtkTreeModel *tree_model,
663
			 GtkTreeIter  *iter)
664
{
665
  GtkListStore *list_store = GTK_LIST_STORE (tree_model);
666
  GtkListStorePrivate *priv = list_store->priv;
Soeren Sandmann's avatar
Soeren Sandmann committed
667
  GtkTreePath *path;
668

669
  g_return_val_if_fail (iter->stamp == priv->stamp, NULL);
670

671
  if (g_sequence_iter_is_end (iter->user_data))
672
    return NULL;
Soeren Sandmann's avatar
Soeren Sandmann committed
673 674
	
  path = gtk_tree_path_new ();
675
  gtk_tree_path_append_index (path, g_sequence_iter_get_position (iter->user_data));
Soeren Sandmann's avatar
Soeren Sandmann committed
676 677
  
  return path;
678 679 680
}

static void
681 682 683 684
gtk_list_store_get_value (GtkTreeModel *tree_model,
			  GtkTreeIter  *iter,
			  gint          column,
			  GValue       *value)
685
{
686
  GtkListStore *list_store = GTK_LIST_STORE (tree_model);
687
  GtkListStorePrivate *priv = list_store->priv;
688 689 690
  GtkTreeDataList *list;
  gint tmp_column = column;

691
  g_return_if_fail (column < priv->n_columns);
692
  g_return_if_fail (iter_is_valid (iter, list_store));
Soeren Sandmann's avatar
Soeren Sandmann committed
693
		    
694
  list = g_sequence_get (iter->user_data);
695 696 697 698

  while (tmp_column-- > 0 && list)
    list = list->next;

699
  if (list == NULL)
700
    g_value_init (value, priv->column_headers[column]);
701 702
  else
    _gtk_tree_data_list_node_to_value (list,
703
				       priv->column_headers[column],
704
				       value);
705 706 707
}

static gboolean
708 709
gtk_list_store_iter_next (GtkTreeModel  *tree_model,
			  GtkTreeIter   *iter)
710
{
711
  GtkListStore *list_store = GTK_LIST_STORE (tree_model);
712
  GtkListStorePrivate *priv = list_store->priv;
713 714
  gboolean retval;

715
  g_return_val_if_fail (priv->stamp == iter->stamp, FALSE);
716
  iter->user_data = g_sequence_iter_next (iter->user_data);
717

718 719 720 721 722
  retval = g_sequence_iter_is_end (iter->user_data);
  if (retval)
    iter->stamp = 0;

  return !retval;
723 724
}

725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
static gboolean
gtk_list_store_iter_previous (GtkTreeModel *tree_model,
                              GtkTreeIter  *iter)
{
  GtkListStore *list_store = GTK_LIST_STORE (tree_model);
  GtkListStorePrivate *priv = list_store->priv;

  g_return_val_if_fail (priv->stamp == iter->stamp, FALSE);

  if (g_sequence_iter_is_begin (iter->user_data))
    {
      iter->stamp = 0;
      return FALSE;
    }

  iter->user_data = g_sequence_iter_prev (iter->user_data);

  return TRUE;
}

745
static gboolean
746
gtk_list_store_iter_children (GtkTreeModel *tree_model,
747 748
			      GtkTreeIter  *iter,
			      GtkTreeIter  *parent)
749
{
750
  GtkListStore *list_store = (GtkListStore *) tree_model;
751
  GtkListStorePrivate *priv = list_store->priv;
752

753
  /* this is a list, nodes have no children */
754
  if (parent)
755 756 757 758
    {
      iter->stamp = 0;
      return FALSE;
    }
759

760
  if (g_sequence_get_length (priv->seq) > 0)
761
    {
762 763
      iter->stamp = priv->stamp;
      iter->user_data = g_sequence_get_begin_iter (priv->seq);
764 765
      return TRUE;
    }
766
  else
767 768 769 770
    {
      iter->stamp = 0;
      return FALSE;
    }
771 772 773
}

static gboolean
774
gtk_list_store_iter_has_child (GtkTreeModel *tree_model,
775
			       GtkTreeIter  *iter)
776 777 778 779 780
{
  return FALSE;
}

static gint
781
gtk_list_store_iter_n_children (GtkTreeModel *tree_model,
782
				GtkTreeIter  *iter)
783
{
784
  GtkListStore *list_store = GTK_LIST_STORE (tree_model);
785
  GtkListStorePrivate *priv = list_store->priv;
Soeren Sandmann's avatar
Soeren Sandmann committed
786

787
  if (iter == NULL)
788
    return g_sequence_get_length (priv->seq);
789

790
  g_return_val_if_fail (priv->stamp == iter->stamp, -1);
791 792

  return 0;
793 794
}

795
static gboolean
796
gtk_list_store_iter_nth_child (GtkTreeModel *tree_model,
797 798
			       GtkTreeIter  *iter,
			       GtkTreeIter  *parent,
799
			       gint          n)
800
{
801
  GtkListStore *list_store = GTK_LIST_STORE (tree_model);
802
  GtkListStorePrivate *priv = list_store->priv;
803
  GSequenceIter *child;
804

805 806
  iter->stamp = 0;

807
  if (parent)
808
    return FALSE;
809

810
  child = g_sequence_get_iter_at_pos (priv->seq, n);
811

812
  if (g_sequence_iter_is_end (child))
813
    return FALSE;
Soeren Sandmann's avatar
Soeren Sandmann committed
814

815
  iter->stamp = priv->stamp;
Soeren Sandmann's avatar
Soeren Sandmann committed
816
  iter->user_data = child;
817

Soeren Sandmann's avatar
Soeren Sandmann committed
818
  return TRUE;
819 820
}

821 822 823 824
static gboolean
gtk_list_store_iter_parent (GtkTreeModel *tree_model,
			    GtkTreeIter  *iter,
			    GtkTreeIter  *child)
825
{
826
  iter->stamp = 0;
827
  return FALSE;
828 829
}

830
static gboolean
831 832 833
gtk_list_store_real_set_value (GtkListStore *list_store,
			       GtkTreeIter  *iter,
			       gint          column,
834 835
			       GValue       *value,
			       gboolean      sort)
836
{
837
  GtkListStorePrivate *priv = list_store->priv;
838 839
  GtkTreeDataList *list;
  GtkTreeDataList *prev;
840
  gint old_column = column;
Javier Jardón's avatar
Javier Jardón committed
841
  GValue real_value = G_VALUE_INIT;
842
  gboolean converted = FALSE;
843
  gboolean retval = FALSE;
844

845
  if (! g_type_is_a (G_VALUE_TYPE (value), priv->column_headers[column]))
846
    {
847 848
      if (! (g_value_type_compatible (G_VALUE_TYPE (value), priv->column_headers[column]) &&
	     g_value_type_compatible (priv->column_headers[column], G_VALUE_TYPE (value))))
849 850 851 852
	{
	  g_warning ("%s: Unable to convert from %s to %s\n",
		     G_STRLOC,
		     g_type_name (G_VALUE_TYPE (value)),
853
		     g_type_name (priv->column_headers[column]));
854
	  return retval;
855 856 857 858 859 860
	}
      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)),
861
		     g_type_name (priv->column_headers[column]));
862
	  g_value_unset (&real_value);
863
	  return retval;
864 865 866
	}
      converted = TRUE;
    }
867

868
  prev = list = g_sequence_get (iter->user_data);
869 870 871 872 873

  while (list != NULL)
    {
      if (column == 0)
	{
874 875 876 877
	  if (converted)
	    _gtk_tree_data_list_value_to_node (list, &real_value);
	  else
	    _gtk_tree_data_list_value_to_node (list, value);
878
	  retval = TRUE;
879 880
	  if (converted)
	    g_value_unset (&real_value);
881 882
         if (sort && GTK_LIST_STORE_IS_SORTED (list_store))
            gtk_list_store_sort_iter_changed (list_store, iter, old_column);
883
	  return retval;
884 885 886 887 888 889 890
	}

      column--;
      prev = list;
      list = list->next;
    }

891
  if (g_sequence_get (iter->user_data) == NULL)
892
    {
Soeren Sandmann's avatar
Soeren Sandmann committed
893
      list = _gtk_tree_data_list_alloc();
894
      g_sequence_set (iter->user_data, list);
895 896 897 898
      list->next = NULL;
    }
  else
    {
899
      list = prev->next = _gtk_tree_data_list_alloc ();
900 901 902 903 904
      list->next = NULL;
    }

  while (column != 0)
    {
905
      list->next = _gtk_tree_data_list_alloc ();
906 907 908 909
      list = list->next;
      list->next = NULL;
      column --;
    }
910

911
  if (converted)
912
    _gtk_tree_data_list_value_to_node (list, &real_value);
913 914
  else
    _gtk_tree_data_list_value_to_node (list, value);
915

916
  retval = TRUE;
917 918
  if (converted)
    g_value_unset (&real_value);
919

920
  if (sort && GTK_LIST_STORE_IS_SORTED (list_store))
921
    gtk_list_store_sort_iter_changed (list_store, iter, old_column);
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944

  return retval;
}


/**
 * gtk_list_store_set_value:
 * @list_store: A #GtkListStore
 * @iter: A valid #GtkTreeIter for the row being modified
 * @column: column number to modify
 * @value: new value for the cell
 *
 * Sets the data in the cell specified by @iter and @column.
 * The type of @value must be convertible to the type of the
 * column.
 *
 **/
void
gtk_list_store_set_value (GtkListStore *list_store,
			  GtkTreeIter  *iter,
			  gint          column,
			  GValue       *value)
{
945
  GtkListStorePrivate *priv;
946

947
  g_return_if_fail (GTK_IS_LIST_STORE (list_store));
948
  g_return_if_fail (iter_is_valid (iter, list_store));
949
  g_return_if_fail (G_IS_VALUE (value));
950 951
  priv = list_store->priv;
  g_return_if_fail (column >= 0 && column < priv->n_columns);
952

953
  if (gtk_list_store_real_set_value (list_store, iter, column, value, TRUE))
954 955 956
    {
      GtkTreePath *path;

957
      path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter);
958 959 960
      gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter);
      gtk_tree_path_free (path);
    }
961 962
}

963 964
static GtkTreeIterCompareFunc
gtk_list_store_get_compare_func (GtkListStore *list_store)
Havoc Pennington's avatar
Havoc Pennington committed
965
{
966
  GtkListStorePrivate *priv = list_store->priv;
967
  GtkTreeIterCompareFunc func = NULL;
Havoc Pennington's avatar
Havoc Pennington committed
968

969
  if (GTK_LIST_STORE_IS_SORTED (list_store))
970
    {
971
      if (priv->sort_column_id != -1)
972 973
	{
	  GtkTreeDataSortHeader *header;
974 975
	  header = _gtk_tree_data_list_get_header (priv->sort_list,
						   priv->sort_column_id);
976 977
	  g_return_val_if_fail (header != NULL, NULL);
	  g_return_val_if_fail (header->func != NULL, NULL);
978 979 980 981
	  func = header->func;
	}
      else
	{
982
	  func = priv->default_sort_func;
983
	}
984 985
    }

986 987 988
  return func;
}

989 990 991 992 993 994 995 996 997
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)
{
998
  GtkListStorePrivate *priv = list_store->priv;
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
  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 &&
1015
	  columns[i] == priv->sort_column_id)
1016 1017 1018 1019
	*maybe_need_sort = TRUE;
    }
}

1020 1021 1022 1023 1024 1025 1026
static void
gtk_list_store_set_valist_internal (GtkListStore *list_store,
				    GtkTreeIter  *iter,
				    gboolean     *emit_signal,
				    gboolean     *maybe_need_sort,
				    va_list	  var_args)
{
1027
  GtkListStorePrivate *priv = list_store->priv;
1028 1029 1030 1031 1032 1033
  gint column;
  GtkTreeIterCompareFunc func = NULL;

  column = va_arg (var_args, gint);

  func = gtk_list_store_get_compare_func (list_store);
1034
  if (func != _gtk_tree_data_list_compare_func)
1035
    *maybe_need_sort = TRUE;
1036

Havoc Pennington's avatar
Havoc Pennington committed
1037 1038
  while (column != -1)
    {
Javier Jardón's avatar
Javier Jardón committed
1039
      GValue value = G_VALUE_INIT;
Havoc Pennington's avatar
Havoc Pennington committed
1040 1041
      gchar *error = NULL;

1042
      if (column < 0 || column >= priv->n_columns)
Havoc Pennington's avatar
Havoc Pennington committed
1043 1044 1045 1046 1047
	{
	  g_warning ("%s: Invalid column number %d added to iter (remember to end your list of columns with a -1)", G_STRLOC, column);
	  break;
	}

1048 1049
      G_VALUE_COLLECT_INIT (&value, priv->column_headers[column],
                            var_args, 0, &error);
Havoc Pennington's avatar
Havoc Pennington committed
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060
      if (error)
	{
	  g_warning ("%s: %s", G_STRLOC, error);
	  g_free (error);

 	  /* we purposely leak the value here, it might not be
	   * in a sane state if an error condition occoured
	   */
	  break;
	}

Jonathan Blandford's avatar
Jonathan Blandford committed
1061
      /* FIXME: instead of calling this n times, refactor with above */
1062 1063 1064 1065 1066 1067
      *emit_signal = gtk_list_store_real_set_value (list_store,
						    iter,
						    column,
						    &value,
						    FALSE) || *emit_signal;
      
1068
      if (func == _gtk_tree_data_list_compare_func &&
1069
	  column == priv->sort_column_id)
1070
	*maybe_need_sort = TRUE;
Havoc Pennington's avatar
Havoc Pennington committed
1071 1072 1073 1074 1075

      g_value_unset (&value);

      column = va_arg (var_args, gint);
    }
1076 1077
}

1078 1079 1080 1081
/**
 * gtk_list_store_set_valuesv:
 * @list_store: A #GtkListStore
 * @iter: A valid #GtkTreeIter for the row being modified
1082 1083
 * @columns: (array length=n_values): an array of column numbers
 * @values: (array length=n_values): an array of GValues
1084
 * @n_values: the length of the @columns and @values arrays
1085
 *
1086 1087 1088 1089 1090 1091 1092
 * 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
1093
 * Rename to: gtk_list_store_set
1094 1095 1096 1097 1098 1099 1100 1101
 */
void
gtk_list_store_set_valuesv (GtkListStore *list_store,
			    GtkTreeIter  *iter,
			    gint         *columns,
			    GValue       *values,
			    gint          n_values)
{
1102
  GtkListStorePrivate *priv;
1103 1104 1105 1106
  gboolean emit_signal = FALSE;
  gboolean maybe_need_sort = FALSE;

  g_return_if_fail (GTK_IS_LIST_STORE (list_store));
1107
  g_return_if_fail (iter_is_valid (iter, list_store));
1108

1109 1110
  priv = list_store->priv;

1111 1112 1113 1114 1115 1116
  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))
1117
    gtk_list_store_sort_iter_changed (list_store, iter, priv->sort_column_id);
1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128

  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);
    }
}

1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
/**
 * 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)
{
1144
  GtkListStorePrivate *priv;
1145 1146 1147 1148
  gboolean emit_signal = FALSE;
  gboolean maybe_need_sort = FALSE;

  g_return_if_fail (GTK_IS_LIST_STORE (list_store));
1149
  g_return_if_fail (iter_is_valid (iter, list_store));
1150

1151 1152
  priv = list_store->priv;

1153 1154 1155 1156
  gtk_list_store_set_valist_internal (list_store, iter, 
				      &emit_signal, 
				      &maybe_need_sort,
				      var_args);
1157

1158
  if (maybe_need_sort && GTK_LIST_STORE_IS_SORTED (list_store))
1159
    gtk_list_store_sort_iter_changed (list_store, iter, priv->sort_column_id);
1160

1161 1162 1163 1164
  if (emit_signal)
    {
      GtkTreePath *path;

1165
      path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter);
1166 1167 1168
      gtk_tree_model_row_changed (GTK_TREE_MODEL (list_store), path, iter);
      gtk_tree_path_free (path);
    }
Havoc Pennington's avatar
Havoc Pennington committed
1169 1170 1171 1172 1173 1174
}

/**
 * gtk_list_store_set:
 * @list_store: a #GtkListStore
 * @iter: row iterator
Matthias Clasen's avatar
Matthias Clasen committed
1175
 * @...: pairs of column number and value, terminated with -1
1176
 *