b8c68d5df50837b0fdda4d072e8c061c66b468c4
[profile/ivi/clutter.git] / clutter / clutter-model.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Authored By Matthew Allum  <mallum@openedhand.com>
7  *             Neil Jagdish Patel <njp@o-hand.com>
8  *             Emmanuele Bassi <ebassi@openedhand.com>
9  *
10  * Copyright (C) 2006 OpenedHand
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the
24  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  *
27  * NB: Inspiration for column storage taken from GtkListStore
28  */
29
30 /**
31  * SECTION:clutter-model
32  * @short_description: A generic model implementation
33  *
34  * #ClutterModel is a generic list model API which can be used to implement
35  * the model-view-controller architectural pattern in Clutter.
36  *
37  * The #ClutterModel class is a list model which can accept most GObject 
38  * types as a column type.
39  * 
40  * Creating a simple clutter model:
41  * <informalexample><programlisting>
42  * enum
43  * {
44  *   COLUMN_INT,
45  *   COLUMN_STRING,
46  *
47  *   N_COLUMNS
48  * };
49  * 
50  * {
51  *   ClutterModel *model;
52  *   gint i;
53  *
54  *   model = clutter_model_default_new (N_COLUMNS,
55  *                                      /<!-- -->* column type, column title *<!-- -->/
56  *                                      G_TYPE_INT,     "my integers",
57  *                                      G_TYPE_STRING,  "my strings");
58  *   for (i = 0; i < 10; i++)
59  *     {
60  *       gchar *string = g_strdup_printf ("String %d", i);
61  *       clutter_model_append (model,
62  *                             COLUMN_INT, i,
63  *                             COLUMN_STRING, string,
64  *                             -1);
65  *       g_free (string);
66  *     }
67  *
68  *   
69  * }
70  * </programlisting></informalexample>
71  *
72  * Iterating through the model consists of retrieving a new #ClutterModelIter
73  * pointing to the starting row, and calling clutter_model_iter_next() or
74  * clutter_model_iter_prev() to move forward or backwards, repectively.
75  *
76  * A valid #ClutterModelIter represents the position between two rows in the
77  * model. For example, the "first" iterator represents the gap immediately 
78  * before the first row, and the "last" iterator represents the gap immediately
79  * after the last row. In an empty sequence, the first and last iterators are
80  * the same.
81  *
82  * Iterating a #ClutterModel:
83  * <informalexample><programlisting>
84  * enum
85  * {
86  *   COLUMN_INT,
87  *   COLUMN_STRING.
88  *
89  *   N_COLUMNS
90  * };
91  * 
92  * {
93  *   ClutterModel *model;
94  *   ClutterModelIter *iter = NULL;
95  *
96  *   /<!-- -->*  Fill the model *<!-- -->/
97  *   model = populate_model ();
98  *
99  *   /<!-- -->* Get the first iter *<!-- -->/
100  *   iter = clutter_model_get_first_iter (model);
101  *   while (!clutter_model_iter_is_last (iter))
102  *     {
103  *       print_row (iter);
104  *       
105  *       iter = clutter_model_iter_next (iter);
106  *     }
107  *
108  *   /<!-- -->* Make sure to unref the iter *<!-- -->/
109  *   g_object_unref (iter);
110  * }
111  * </programlisting></informalexample>
112  *
113  * #ClutterModel is an abstract class. Clutter provides a list model
114  * implementation called #ClutterListModel which has been optimised
115  * for insertion and look up in sorted lists.
116  *
117  * #ClutterModel is available since Clutter 0.6
118  */
119
120
121 #ifdef HAVE_CONFIG_H
122 #include "config.h"
123 #endif
124
125 #include <string.h>
126 #include <glib-object.h>
127 #include <gobject/gvaluecollector.h>
128
129 #include "clutter-model.h"
130 #include "clutter-model-private.h"
131
132 #include "clutter-marshal.h"
133 #include "clutter-private.h"
134 #include "clutter-debug.h"
135
136 G_DEFINE_ABSTRACT_TYPE (ClutterModel, clutter_model, G_TYPE_OBJECT);
137
138 enum
139 {
140   PROP_0,
141
142   PROP_FILTER_SET
143 };
144
145 enum
146 {
147   ROW_ADDED,
148   ROW_REMOVED,
149   ROW_CHANGED,
150
151   SORT_CHANGED,
152   FILTER_CHANGED,
153   
154   LAST_SIGNAL
155 };
156
157 static guint model_signals[LAST_SIGNAL] = { 0, };
158
159 #define CLUTTER_MODEL_GET_PRIVATE(obj) \
160   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_MODEL, ClutterModelPrivate))
161
162 struct _ClutterModelPrivate
163 {
164   GType                  *column_types;
165   gchar                 **column_names;
166   gint                    n_columns; 
167
168   ClutterModelFilterFunc  filter_func;
169   gpointer                filter_data;
170   GDestroyNotify          filter_notify;
171
172   gint                    sort_column;
173   ClutterModelSortFunc    sort_func;
174   gpointer                sort_data;
175   GDestroyNotify          sort_notify;
176 };
177
178 static GType
179 clutter_model_real_get_column_type (ClutterModel *model,
180                                     guint         column)
181 {
182   ClutterModelPrivate *priv = model->priv;
183
184   if (column < 0 || column >= clutter_model_get_n_columns (model))
185     return G_TYPE_INVALID;
186
187   return priv->column_types[column];
188 }
189
190 static const gchar *
191 clutter_model_real_get_column_name (ClutterModel *model,
192                                     guint         column)
193 {
194   ClutterModelPrivate *priv = model->priv;
195
196   if (column < 0 || column >= clutter_model_get_n_columns (model))
197     return NULL;
198
199   if (priv->column_names && priv->column_names[column])
200     return priv->column_names[column];
201
202   return g_type_name (priv->column_types[column]);
203 }
204
205 static guint
206 clutter_model_real_get_n_columns (ClutterModel *model)
207 {
208   return model->priv->n_columns;
209 }
210
211 static void 
212 clutter_model_finalize (GObject *object)
213 {
214   ClutterModelPrivate *priv = CLUTTER_MODEL (object)->priv;
215   gint i;
216
217   if (priv->sort_notify)
218     priv->sort_notify (priv->sort_data);
219   
220   if (priv->filter_notify)
221     priv->filter_notify (priv->filter_data);
222
223   g_free (priv->column_types);
224
225   if (priv->column_names != NULL)
226     {
227       /* the column_names vector might have holes in it, so we need
228        * to use the columns number to clear up everything
229        */
230       for (i = 0; i < priv->n_columns; i++)
231         g_free (priv->column_names[i]);
232
233       g_free (priv->column_names);
234     }
235
236   G_OBJECT_CLASS (clutter_model_parent_class)->finalize (object);
237 }
238
239 static void
240 clutter_model_get_property (GObject    *gobject,
241                             guint       prop_id,
242                             GValue     *value,
243                             GParamSpec *pspec)
244 {
245   ClutterModelPrivate *priv = CLUTTER_MODEL (gobject)->priv;
246
247   switch (prop_id)
248     {
249     case PROP_FILTER_SET:
250       g_value_set_boolean (value, priv->filter_func != NULL);
251       break;
252
253     default:
254       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
255       break;
256     }
257 }
258
259 static void
260 clutter_model_class_init (ClutterModelClass *klass)
261 {
262   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
263   GParamSpec *pspec;
264
265   gobject_class->get_property = clutter_model_get_property;
266   gobject_class->finalize = clutter_model_finalize;
267
268   g_type_class_add_private (gobject_class, sizeof (ClutterModelPrivate));
269
270   klass->get_column_name  = clutter_model_real_get_column_name;
271   klass->get_column_type  = clutter_model_real_get_column_type;
272   klass->get_n_columns    = clutter_model_real_get_n_columns;
273
274   /**
275    * ClutterModel:filter-set:
276    *
277    * Whether the #ClutterModel has a filter set
278    *
279    * This property is set to %TRUE if a filter function has been
280    * set using clutter_model_set_filter()
281    *
282    * Since: 1.0
283    */
284   pspec = g_param_spec_boolean ("filter-set",
285                                 "Filter Set",
286                                 "Whether the model has a filter",
287                                 FALSE,
288                                 CLUTTER_PARAM_READABLE);
289   g_object_class_install_property (gobject_class, PROP_FILTER_SET, pspec);
290
291    /**
292    * ClutterModel::row-added:
293    * @model: the #ClutterModel on which the signal is emitted
294    * @iter: a #ClutterModelIter pointing to the new row
295    *
296    * The ::row-added signal is emitted when a new row has been added.
297    * The data on the row has already been set when the ::row-added signal
298    * has been emitted.
299    *
300    * Since: 0.6
301    */
302   model_signals[ROW_ADDED] =
303     g_signal_new ("row-added",
304                   G_TYPE_FROM_CLASS (gobject_class),
305                   G_SIGNAL_RUN_LAST,
306                   G_STRUCT_OFFSET (ClutterModelClass, row_added),
307                   NULL, NULL,
308                   clutter_marshal_VOID__OBJECT,
309                   G_TYPE_NONE, 1,
310                   CLUTTER_TYPE_MODEL_ITER);
311    /**
312    * ClutterModel::row-removed:
313    * @model: the #ClutterModel on which the signal is emitted
314    * @iter: a #ClutterModelIter pointing to the removed row
315    *
316    * The ::row-removed signal is emitted when a row has been removed.
317    * The data on the row pointed by the passed iterator is still valid
318    * when the ::row-removed signal has been emitted.
319    *
320    * Since: 0.6
321    */
322   model_signals[ROW_REMOVED] =
323     g_signal_new ("row-removed",
324                   G_TYPE_FROM_CLASS (gobject_class),
325                   G_SIGNAL_RUN_LAST,
326                   G_STRUCT_OFFSET (ClutterModelClass, row_removed),
327                   NULL, NULL,
328                   clutter_marshal_VOID__OBJECT,
329                   G_TYPE_NONE, 1,
330                   CLUTTER_TYPE_MODEL_ITER);
331    /**
332    * ClutterModel::row-changed:
333    * @model: the #ClutterModel on which the signal is emitted
334    * @iter: a #ClutterModelIter pointing to the changed row
335    *
336    * The ::row-removed signal is emitted when a row has been changed.
337    * The data on the row has already been updated when the ::row-changed
338    * signal has been emitted.
339    *
340    * Since: 0.6
341    */
342   model_signals[ROW_CHANGED] =
343     g_signal_new ("row-changed",
344                   G_TYPE_FROM_CLASS (gobject_class),
345                   G_SIGNAL_RUN_LAST,
346                   G_STRUCT_OFFSET (ClutterModelClass, row_changed),
347                   NULL, NULL,
348                   clutter_marshal_VOID__OBJECT,
349                   G_TYPE_NONE, 1,
350                   CLUTTER_TYPE_MODEL_ITER);
351   /**
352    * ClutterModel::sort-changed:
353    * @model: the #ClutterModel on which the signal is emitted
354    * 
355    * The ::sort-changed signal is emitted after the model has been sorted
356    *
357    * Since: 0.6
358    */
359   model_signals[SORT_CHANGED] =
360     g_signal_new ("sort-changed",
361                   G_TYPE_FROM_CLASS (gobject_class),
362                   G_SIGNAL_RUN_LAST,
363                   G_STRUCT_OFFSET (ClutterModelClass, sort_changed),
364                   NULL, NULL,
365                   clutter_marshal_VOID__VOID,
366                   G_TYPE_NONE, 0);
367    /**
368    * ClutterModel::filter-changed:
369    * @model: the #ClutterModel on which the signal is emitted   
370    *
371    * The ::filter-changed signal is emitted when a new filter has been applied
372    *
373    * Since: 0.6
374    */
375   model_signals[FILTER_CHANGED] =
376     g_signal_new ("filter-changed",
377                   G_TYPE_FROM_CLASS (gobject_class),
378                   G_SIGNAL_RUN_LAST,
379                   G_STRUCT_OFFSET (ClutterModelClass, filter_changed),
380                   NULL, NULL,
381                   clutter_marshal_VOID__VOID,
382                   G_TYPE_NONE, 0);
383 }
384
385 static void
386 clutter_model_init (ClutterModel *self)
387 {
388   ClutterModelPrivate *priv;
389   
390   self->priv = priv = CLUTTER_MODEL_GET_PRIVATE (self);
391
392   priv->n_columns = -1;
393   priv->column_types = NULL;
394   priv->column_names = NULL;
395
396   priv->filter_func = NULL;
397   priv->filter_data = NULL;
398   priv->filter_notify = NULL;
399
400   priv->sort_column = -1;
401   priv->sort_func = NULL;
402   priv->sort_data = NULL;
403   priv->sort_notify = NULL;
404 }
405
406 /* XXX - is this whitelist really necessary? we accept every fundamental
407  * type.
408  */
409 gboolean
410 clutter_model_check_type (GType gtype)
411 {
412   gint i = 0;
413   static const GType type_list[] =
414     {
415       G_TYPE_BOOLEAN,
416       G_TYPE_CHAR,
417       G_TYPE_UCHAR,
418       G_TYPE_INT,
419       G_TYPE_UINT,
420       G_TYPE_LONG,
421       G_TYPE_ULONG,
422       G_TYPE_INT64,
423       G_TYPE_UINT64,
424       G_TYPE_ENUM,
425       G_TYPE_FLAGS,
426       G_TYPE_FLOAT,
427       G_TYPE_DOUBLE,
428       G_TYPE_STRING,
429       G_TYPE_POINTER,
430       G_TYPE_BOXED,
431       G_TYPE_OBJECT,
432       G_TYPE_INVALID
433     };
434
435   if (! G_TYPE_IS_VALUE_TYPE (gtype))
436     return FALSE;
437
438   while (type_list[i] != G_TYPE_INVALID)
439     {
440       if (g_type_is_a (gtype, type_list[i]))
441         return TRUE;
442       i++;
443     }
444   return FALSE;
445 }
446
447
448
449 /**
450  * clutter_model_resort:
451  * @model: a #ClutterModel
452  *
453  * Force a resort on the @model. This function should only be
454  * used by subclasses of #ClutterModel.
455  *
456  * Since: 0.6
457  */
458 void
459 clutter_model_resort (ClutterModel *model)
460 {
461   ClutterModelPrivate *priv;
462   ClutterModelClass *klass;
463
464   g_return_if_fail (CLUTTER_IS_MODEL (model));
465   priv = model->priv;
466
467   klass = CLUTTER_MODEL_GET_CLASS (model);
468
469   if (klass->resort)
470     klass->resort (model, priv->sort_func, priv->sort_data);
471 }
472
473 /**
474  * clutter_model_filter_row:
475  * @model: a #ClutterModel
476  * @row: the row to filter
477  *
478  * Checks whether @row should be filtered or not using the
479  * filtering function set on @model.
480  *
481  * This function should be used only by subclasses of #ClutterModel.
482  *
483  * Return value: %TRUE if the row should be displayed,
484  *   %FALSE otherwise
485  *
486  * Since: 0.6
487  */
488 gboolean
489 clutter_model_filter_row (ClutterModel *model,
490                           guint         row)
491 {
492   ClutterModelPrivate *priv;
493   ClutterModelIter *iter;
494   gboolean res = TRUE;
495
496   g_return_val_if_fail (CLUTTER_IS_MODEL (model), TRUE);
497
498   priv = model->priv;
499
500   if (!priv->filter_func)
501     return TRUE;
502
503   iter = clutter_model_get_iter_at_row (model, row);
504   if (iter == NULL)
505     return FALSE;
506
507   res = priv->filter_func (model, iter, priv->filter_data);
508
509   g_object_unref (iter);
510
511   return res;
512 }
513
514 /**
515  * clutter_model_filter_iter:
516  * @model: a #ClutterModel
517  * @iter: the row to filter
518  *
519  * Checks whether the row pointer by @iter should be filtered or not using
520  * the filtering function set on @model.
521  *
522  * This function should be used only by subclasses of #ClutterModel.
523  *
524  * Return value: %TRUE if the row should be displayed,
525  *   %FALSE otherwise
526  *
527  * Since: 0.6
528  */
529 gboolean
530 clutter_model_filter_iter (ClutterModel     *model,
531                            ClutterModelIter *iter)
532 {
533   ClutterModelPrivate *priv;
534
535   g_return_val_if_fail (CLUTTER_IS_MODEL (model), TRUE);
536   g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), TRUE);
537
538   priv = model->priv;
539
540   if (!priv->filter_func)
541     return TRUE;
542
543   return priv->filter_func (model, iter, priv->filter_data);
544 }
545
546 /*
547  * clutter_model_set_n_columns:
548  * @model: a #ClutterModel
549  * @n_columns: number of columns
550  * @set_types: set the columns type
551  * @set_names: set the columns name
552  *
553  * Sets the number of columns in @model to @n_columns. If @set_types
554  * or @set_names are %TRUE, initialises the columns type and name
555  * arrays as well.
556  *
557  * This function can only be called once.
558  */
559 void
560 clutter_model_set_n_columns (ClutterModel *model,
561                              gint          n_columns,
562                              gboolean      set_types,
563                              gboolean      set_names)
564 {
565   ClutterModelPrivate *priv = model->priv;
566
567   if (priv->n_columns > 0 && priv->n_columns != n_columns)
568     return;
569
570   priv->n_columns = n_columns;
571   
572   if (set_types && !priv->column_types)
573     priv->column_types = g_new0 (GType,  n_columns);
574
575   if (set_names && !priv->column_names)
576     priv->column_names = g_new0 (gchar*, n_columns);
577 }
578
579 /*
580  * clutter_model_set_column_type:
581  * @model: a #ClutterModel
582  * @column: column index
583  * @gtype: type of the column
584  *
585  * Sets the type of @column inside @model
586  */
587 void
588 clutter_model_set_column_type (ClutterModel *model,
589                                gint          column,
590                                GType         gtype)
591 {
592   ClutterModelPrivate *priv = model->priv;
593
594   priv->column_types[column] = gtype;
595 }
596
597 /*
598  * clutter_model_set_column_name:
599  * @model: a #ClutterModel
600  * @column: column index
601  * @name: name of the column, or %NULL
602  *
603  * Sets the name of @column inside @model
604  */
605 void
606 clutter_model_set_column_name (ClutterModel *model,
607                                gint          column,
608                                const gchar  *name)
609 {
610   ClutterModelPrivate *priv = model->priv;
611
612   priv->column_names[column] = g_strdup (name);
613 }
614
615 /**
616  * clutter_model_set_types:
617  * @model: a #ClutterModel
618  * @n_columns: number of columns for the model
619  * @types: (array length=n_columns): an array of #GType types
620  *
621  * Sets the types of the columns inside a #ClutterModel.
622  *
623  * This function is meant primarily for #GObjects that inherit from
624  * #ClutterModel, and should only be used when contructing a #ClutterModel.
625  * It will not work after the initial creation of the #ClutterModel.
626  *
627  * Since: 0.6
628  */
629 void
630 clutter_model_set_types (ClutterModel *model,
631                          guint         n_columns,
632                          GType        *types)
633 {
634   ClutterModelPrivate *priv;
635   gint i;
636
637   g_return_if_fail (CLUTTER_IS_MODEL (model));
638   g_return_if_fail (n_columns > 0);
639
640   priv = model->priv;
641
642   g_return_if_fail (priv->n_columns < 0 || priv->n_columns == n_columns);
643   g_return_if_fail (priv->column_types == NULL);
644
645   clutter_model_set_n_columns (model, n_columns, TRUE, FALSE);
646
647   for (i = 0; i < n_columns; i++)
648     {
649       if (!clutter_model_check_type (types[i]))
650         {
651           g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
652           return;
653         }
654
655       clutter_model_set_column_type (model, i, types[i]);
656     }
657 }
658
659 /**
660  * clutter_model_set_names:
661  * @model: a #ClutterModel
662  * @n_columns: the number of column names
663  * @names: (array length=n_columns): an array of strings
664  *
665  * Assigns a name to the columns of a #ClutterModel.
666  *
667  * This function is meant primarily for #GObjects that inherit from
668  * #ClutterModel, and should only be used when contructing a #ClutterModel.
669  * It will not work after the initial creation of the #ClutterModel.
670  *
671  * Since: 0.6
672  */
673 void
674 clutter_model_set_names (ClutterModel        *model,
675                          guint                n_columns,
676                          const gchar * const  names[])
677 {
678   ClutterModelPrivate *priv;
679   gint i;
680
681   g_return_if_fail (CLUTTER_IS_MODEL (model));
682   g_return_if_fail (n_columns > 0);
683
684   priv = model->priv;
685
686   g_return_if_fail (priv->n_columns < 0 || priv->n_columns == n_columns);
687   g_return_if_fail (priv->column_names == NULL);
688
689   clutter_model_set_n_columns (model, n_columns, FALSE, TRUE);
690
691   for (i = 0; i < n_columns; i++)
692     clutter_model_set_column_name (model, i, names[i]);
693 }
694
695 /**
696  * clutter_model_get_n_columns:
697  * @model: a #ClutterModel
698  *
699  * Retrieves the number of columns inside @model.
700  *
701  * Return value: the number of columns
702  *
703  * Since: 0.6
704  */
705 guint
706 clutter_model_get_n_columns (ClutterModel *model)
707 {
708   g_return_val_if_fail (CLUTTER_IS_MODEL (model), 0);
709
710   return CLUTTER_MODEL_GET_CLASS (model)->get_n_columns (model);
711 }
712
713 /**
714  * clutter_model_appendv:
715  * @model: a #ClutterModel
716  * @n_columns: the number of columns and values
717  * @columns: (array length=n_columns): a vector with the columns to set
718  * @values: (array length=n_columns): a vector with the values
719  *
720  * Creates and appends a new row to the #ClutterModel, setting the row
721  * values for the given @columns upon creation.
722  *
723  * Since: 0.6
724  */
725 void
726 clutter_model_appendv (ClutterModel *model,
727                        guint         n_columns,
728                        guint        *columns,
729                        GValue       *values)
730 {
731   ClutterModelPrivate *priv;
732   ClutterModelIter *iter;
733   gint i;
734   gboolean resort = FALSE;
735
736   g_return_if_fail (CLUTTER_IS_MODEL (model));
737   g_return_if_fail (n_columns <= clutter_model_get_n_columns (model));
738   g_return_if_fail (columns != NULL);
739   g_return_if_fail (values != NULL);
740
741   priv = model->priv;
742
743   iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, -1);
744   g_assert (CLUTTER_IS_MODEL_ITER (iter));
745
746   for (i = 0; i < n_columns; i++)
747     {
748       if (priv->sort_column == columns[i])
749         resort = TRUE;
750
751       clutter_model_iter_set_value (iter, columns[i], &values[i]);
752     }
753
754   g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
755
756   if (resort)
757     clutter_model_resort (model);
758
759   g_object_unref (iter);
760 }
761
762 /* forward declaration */
763 static void clutter_model_iter_set_internal_valist (ClutterModelIter *iter,
764                                                     va_list           args);
765
766 /**
767  * clutter_model_append:
768  * @model: a #ClutterModel
769  * @Varargs: pairs of column number and value, terminated with -1
770  *
771  * Creates and appends a new row to the #ClutterModel, setting the
772  * row values upon creation. For example, to append a new row where
773  * column 0 is type %G_TYPE_INT and column 1 is of type %G_TYPE_STRING:
774  *
775  * <informalexample><programlisting>
776  *   ClutterModel *model;
777  *   model = clutter_model_default_new (2,
778  *                                      G_TYPE_INT,    "Score",
779  *                                      G_TYPE_STRING, "Team");
780  *   clutter_model_append (model, 0, 42, 1, "Team #1", -1);
781  * </programlisting></informalexample>
782  *
783  * Since: 0.6
784  */
785 void
786 clutter_model_append (ClutterModel *model,
787                       ...)
788 {
789   ClutterModelIter *iter;
790   va_list args;
791
792   g_return_if_fail (CLUTTER_IS_MODEL (model));
793
794   iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, -1);
795   g_assert (CLUTTER_IS_MODEL_ITER (iter));
796
797   /* do not emit the ::row-changed signal */
798   va_start (args, model);
799   clutter_model_iter_set_internal_valist (iter, args);
800   va_end (args);
801
802   g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
803
804   g_object_unref (iter);
805 }
806
807 /**
808  * clutter_model_prependv:
809  * @model: a #ClutterModel
810  * @n_columns: the number of columns and values to set
811  * @columns: (array length=n_columns): a vector containing the columns to set
812  * @values: (array length=n_columns): a vector containing the values for the cells
813  *
814  * Creates and prepends a new row to the #ClutterModel, setting the row
815  * values for the given @columns upon creation.
816  *
817  * Since: 0.6
818  */
819 void
820 clutter_model_prependv (ClutterModel *model,
821                         guint         n_columns,
822                         guint        *columns,
823                         GValue       *values)
824 {
825   ClutterModelPrivate *priv;
826   ClutterModelIter *iter;
827   gint i;
828   gboolean resort = FALSE;
829
830   g_return_if_fail (CLUTTER_IS_MODEL (model));
831   g_return_if_fail (n_columns <= clutter_model_get_n_columns (model));
832   g_return_if_fail (columns != NULL);
833   g_return_if_fail (values != NULL);
834
835   priv = model->priv;
836
837   iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, 0);
838   g_assert (CLUTTER_IS_MODEL_ITER (iter));
839
840   for (i = 0; i < n_columns; i++)
841     {
842       if (priv->sort_column == columns[i])
843         resort = TRUE;
844
845       clutter_model_iter_set_value (iter, columns[i], &values[i]);
846     }
847
848   g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
849
850   if (resort)
851     clutter_model_resort (model);
852
853   g_object_unref (iter);
854 }
855
856 /**
857  * clutter_model_prepend:
858  * @model: a #ClutterModel
859  * @Varargs: pairs of column number and value, terminated with -1
860  *
861  * Creates and prepends a new row to the #ClutterModel, setting the row
862  * values upon creation. For example, to prepend a new row where column 0
863  * is type %G_TYPE_INT and column 1 is of type %G_TYPE_STRING:
864  *
865  * <informalexample><programlisting>
866  *   ClutterModel *model;
867  *   model = clutter_model_default_new (2,
868  *                                      G_TYPE_INT,    "Score",
869  *                                      G_TYPE_STRING, "Team");
870  *   clutter_model_prepend (model, 0, 42, 1, "Team #1", -1);
871  * </programlisting></informalexample>
872  *
873  * Since: 0.6
874  */
875 void
876 clutter_model_prepend (ClutterModel *model,
877                        ...)
878 {
879   ClutterModelIter *iter;
880   va_list args;
881
882   g_return_if_fail (CLUTTER_IS_MODEL (model));
883
884   iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, 0);
885   g_assert (CLUTTER_IS_MODEL_ITER (iter));
886
887   va_start (args, model);
888   clutter_model_iter_set_internal_valist (iter, args);
889   va_end (args);
890
891   g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
892
893   g_object_unref (iter);
894 }
895
896
897 /**
898  * clutter_model_insert:
899  * @model: a #ClutterModel
900  * @row: the position to insert the new row
901  * @Varargs: pairs of column number and value, terminated with -1
902  *
903  * Inserts a new row to the #ClutterModel at @row, setting the row
904  * values upon creation. For example, to insert a new row at index 100,
905  * where column 0 is type %G_TYPE_INT and column 1 is of type
906  * %G_TYPE_STRING:
907  *
908  * <informalexample><programlisting>
909  *   ClutterModel *model;
910  *   model = clutter_model_default_new (2,
911  *                                      G_TYPE_INT,    "Score",
912  *                                      G_TYPE_STRING, "Team");
913  *   clutter_model_insert (model, 3, 0, 42, 1, "Team #1", -1);
914  * </programlisting></informalexample>
915  *
916  * Since: 0.6
917  */
918 void
919 clutter_model_insert (ClutterModel *model,
920                       guint         row,
921                       ...)
922 {
923   ClutterModelIter *iter;
924   va_list args;
925
926   g_return_if_fail (CLUTTER_IS_MODEL (model));
927
928   iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, row);
929   g_assert (CLUTTER_IS_MODEL_ITER (iter));
930
931   /* set_valist() will call clutter_model_resort() if one of the
932    * passed columns matches the model sorting column index
933    */
934   va_start (args, row);
935   clutter_model_iter_set_internal_valist (iter, args);
936   va_end (args);
937
938   g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
939
940   g_object_unref (iter);
941 }
942
943 /**
944  * clutter_model_insertv:
945  * @model: a #ClutterModel
946  * @row: row index
947  * @n_columns: the number of columns and values to set
948  * @columns: (array length=n_columns): a vector containing the columns to set
949  * @values: (array length=n_columns): a vector containing the values for the cells
950  *
951  * Inserts data at @row into the #ClutterModel, setting the row
952  * values for the given @columns upon creation.
953  *
954  * Since: 0.6
955  */
956 void
957 clutter_model_insertv (ClutterModel *model,
958                        guint         row,
959                        guint         n_columns,
960                        guint        *columns,
961                        GValue       *values)
962 {
963   ClutterModelPrivate *priv;
964   ClutterModelIter *iter;
965   gint i;
966   gboolean resort = FALSE;
967
968   g_return_if_fail (CLUTTER_IS_MODEL (model));
969   g_return_if_fail (n_columns <= clutter_model_get_n_columns (model));
970   g_return_if_fail (columns != NULL);
971   g_return_if_fail (values != NULL);
972
973   priv = model->priv;
974
975   iter = CLUTTER_MODEL_GET_CLASS (model)->insert_row (model, row);
976   g_assert (CLUTTER_IS_MODEL_ITER (iter));
977
978   for (i = 0; i < n_columns; i++)
979     {
980       if (priv->sort_column == columns[i])
981         resort = TRUE;
982
983       clutter_model_iter_set_value (iter, columns[i], &values[i]);
984     }
985
986   g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
987
988   if (resort)
989     clutter_model_resort (model);
990
991   g_object_unref (iter);
992 }
993
994 /**
995  * clutter_model_insert_value:
996  * @model: a #ClutterModel
997  * @row: position of the row to modify
998  * @column: column to modify
999  * @value: new value for the cell
1000  *
1001  * Sets the data in the cell specified by @iter and @column. The type of 
1002  * @value must be convertable to the type of the column. If the row does
1003  * not exist then it is created.
1004  *
1005  * Since: 0.6
1006  */
1007 void
1008 clutter_model_insert_value (ClutterModel *model,
1009                             guint         row,
1010                             guint         column,
1011                             const GValue *value)
1012 {
1013   ClutterModelPrivate *priv;
1014   ClutterModelClass *klass;
1015   ClutterModelIter *iter;
1016   gboolean added = FALSE;
1017   
1018   g_return_if_fail (CLUTTER_IS_MODEL (model));
1019
1020   priv = model->priv;
1021   klass = CLUTTER_MODEL_GET_CLASS (model);
1022
1023   iter = klass->get_iter_at_row (model, row);
1024   if (!iter)
1025     {
1026       iter = klass->insert_row (model, row);
1027       added = TRUE;
1028     }
1029
1030   g_assert (CLUTTER_IS_MODEL_ITER (iter));
1031
1032   clutter_model_iter_set_value (iter, column, value);
1033
1034   if (added)
1035     g_signal_emit (model, model_signals[ROW_ADDED], 0, iter);
1036
1037   if (priv->sort_column == column)
1038     clutter_model_resort (model);
1039
1040   g_object_unref (iter);
1041 }
1042
1043 /**
1044  * clutter_model_remove:
1045  * @model: a #ClutterModel
1046  * @row: position of row to remove
1047  *
1048  * Removes the row at the given position from the model.
1049  *
1050  * Since: 0.6
1051  */
1052 void
1053 clutter_model_remove (ClutterModel *model,
1054                       guint         row)
1055 {
1056   ClutterModelClass *klass;
1057
1058   g_return_if_fail (CLUTTER_IS_MODEL (model));
1059
1060   klass = CLUTTER_MODEL_GET_CLASS (model);
1061   if (klass->remove_row)
1062     klass->remove_row (model, row);
1063 }
1064
1065 /**
1066  * clutter_model_get_column_name:
1067  * @model: #ClutterModel
1068  * @column: the column number
1069  *
1070  * Retrieves the name of the @column
1071  *
1072  * Return value: the name of the column. The model holds the returned
1073  *   string, and it should not be modified or freed
1074  *
1075  * Since: 0.6
1076  */
1077 G_CONST_RETURN gchar *
1078 clutter_model_get_column_name (ClutterModel *model,
1079                                guint         column)
1080 {
1081   ClutterModelClass *klass;
1082
1083   g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL);
1084
1085   if (column < 0 || column >= clutter_model_get_n_columns (model))
1086     {
1087       g_warning ("%s: Invalid column id value %d\n", G_STRLOC, column);
1088       return NULL;
1089     }
1090
1091   klass = CLUTTER_MODEL_GET_CLASS (model);
1092   if (klass->get_column_name)
1093     return klass->get_column_name (model, column);
1094
1095   return NULL;
1096 }
1097
1098 /**
1099  * clutter_model_get_column_type:
1100  * @model: #ClutterModel
1101  * @column: the column number
1102  *
1103  * Retrieves the type of the @column.
1104  *
1105  * Return value: the type of the column.
1106  *
1107  * Since: 0.6
1108  */
1109 GType
1110 clutter_model_get_column_type (ClutterModel *model,
1111                                guint         column)
1112 {
1113   ClutterModelClass *klass;
1114
1115   g_return_val_if_fail (CLUTTER_IS_MODEL (model), G_TYPE_INVALID);
1116
1117   if (column < 0 || column >= clutter_model_get_n_columns (model))
1118     {
1119       g_warning ("%s: Invalid column id value %d\n", G_STRLOC, column);
1120       return G_TYPE_INVALID;
1121     }
1122
1123   klass = CLUTTER_MODEL_GET_CLASS (model);
1124   if (klass->get_column_type)
1125     return klass->get_column_type (model, column);
1126
1127   return G_TYPE_INVALID;
1128 }
1129
1130 /**
1131  * clutter_model_get_iter_at_row:
1132  * @model: a #ClutterModel
1133  * @row: position of the row to retrieve
1134  *
1135  * Retrieves a #ClutterModelIter representing the row at the given index.
1136  *
1137  * If a filter function has been set using clutter_model_set_filter()
1138  * then the @model implementation will return the first non filtered
1139  * row.
1140  *
1141  * Return value: (transfer full): A new #ClutterModelIter, or %NULL if @row was
1142  *   out of bounds. When done using the iterator object, call g_object_unref()
1143  *   to deallocate its resources
1144  *
1145  * Since: 0.6
1146  */
1147 ClutterModelIter * 
1148 clutter_model_get_iter_at_row (ClutterModel *model,
1149                                guint         row)
1150 {
1151   ClutterModelClass *klass;
1152
1153   g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL);
1154
1155   klass = CLUTTER_MODEL_GET_CLASS (model);
1156   if (klass->get_iter_at_row)
1157     return klass->get_iter_at_row (model, row);
1158
1159   return NULL;
1160 }
1161
1162
1163 /**
1164  * clutter_model_get_first_iter:
1165  * @model: a #ClutterModel
1166  *
1167  * Retrieves a #ClutterModelIter representing the first non-filtered
1168  * row in @model.
1169  *
1170  * Return value: (transfer full): A new #ClutterModelIter.
1171  *   Call g_object_unref() when done using it
1172  *
1173  * Since: 0.6
1174  */
1175 ClutterModelIter *
1176 clutter_model_get_first_iter (ClutterModel *model)
1177 {
1178   ClutterModelIter *retval;
1179
1180   g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL);
1181
1182   retval = clutter_model_get_iter_at_row (model, 0);
1183   if (retval != NULL)
1184     {
1185       g_assert (clutter_model_filter_iter (model, retval) != FALSE);
1186       g_assert (clutter_model_iter_get_row (retval) == 0);
1187     }
1188
1189   return retval;
1190 }
1191
1192 /**
1193  * clutter_model_get_last_iter:
1194  * @model: a #ClutterModel
1195  *
1196  * Retrieves a #ClutterModelIter representing the last non-filtered
1197  * row in @model.
1198  *
1199  * Return value: (transfer full): A new #ClutterModelIter.
1200  *   Call g_object_unref() when done using it
1201  *
1202  * Since: 0.6
1203  */
1204 ClutterModelIter *
1205 clutter_model_get_last_iter (ClutterModel *model)
1206 {
1207   ClutterModelIter *retval;
1208   guint length;
1209
1210   g_return_val_if_fail (CLUTTER_IS_MODEL (model), NULL);
1211
1212   length = clutter_model_get_n_rows (model);
1213   retval = clutter_model_get_iter_at_row (model, length - 1);
1214   if (retval != NULL)
1215     g_assert (clutter_model_filter_iter (model, retval) != FALSE);
1216
1217   return retval;
1218 }
1219
1220 /**
1221  * clutter_model_get_n_rows:
1222  * @model: a #ClutterModel
1223  *
1224  * Retrieves the number of rows inside @model, eventually taking
1225  * into account any filtering function set using clutter_model_set_filter().
1226  *
1227  * Return value: The length of the @model. If there is a filter set, then
1228  *   the length of the filtered @model is returned.
1229  *
1230  * Since: 0.6
1231  */
1232 guint
1233 clutter_model_get_n_rows (ClutterModel *model)
1234 {
1235   ClutterModelClass *klass;
1236   guint row_count;
1237
1238   g_return_val_if_fail (CLUTTER_IS_MODEL (model), 0);
1239
1240   klass = CLUTTER_MODEL_GET_CLASS (model);
1241   if (klass->get_n_rows)
1242     row_count = klass->get_n_rows (model);
1243   else
1244     {
1245       ClutterModelIter *iter;
1246
1247       iter = clutter_model_get_first_iter (model);
1248       if (iter == NULL)
1249         return 0;
1250
1251       row_count = 0;
1252       while (!clutter_model_iter_is_last (iter))
1253         {
1254           if (clutter_model_filter_iter (model, iter))
1255             row_count += 1;
1256
1257           iter = clutter_model_iter_next (iter);
1258         }
1259
1260       g_object_unref (iter);
1261     }
1262
1263   return row_count;
1264 }
1265
1266
1267 /**
1268  * clutter_model_set_sorting_column:
1269  * @model: a #ClutterModel
1270  * @column: the column of the @model to sort, or -1
1271  *
1272  * Sets the model to sort by @column. If @column is a negative value
1273  * the sorting column will be unset.
1274  *
1275  * Since: 0.6
1276  */
1277 void               
1278 clutter_model_set_sorting_column (ClutterModel *model,
1279                                   gint          column)
1280 {
1281   ClutterModelPrivate *priv;
1282
1283   g_return_if_fail (CLUTTER_IS_MODEL (model));
1284   priv = model->priv;
1285
1286   /* The extra comparison for >= 0 is because column gets promoted to
1287      unsigned in the second comparison */
1288   if (column >= 0 && column >= clutter_model_get_n_columns (model))
1289     {
1290       g_warning ("%s: Invalid column id value %d\n", G_STRLOC, column);
1291       return;
1292     }
1293
1294   priv->sort_column = column;
1295
1296   if (priv->sort_column >= 0)
1297     clutter_model_resort (model);
1298
1299   g_signal_emit (model, model_signals[SORT_CHANGED], 0);
1300 }
1301
1302 /**
1303  * clutter_model_get_sorting_column:
1304  * @model: a #ClutterModel
1305  *
1306  * Retrieves the number of column used for sorting the @model.
1307  *
1308  * Return value: a column number, or -1 if the model is not sorted
1309  *
1310  * Since: 0.6
1311  */
1312 gint
1313 clutter_model_get_sorting_column (ClutterModel *model)
1314 {
1315   g_return_val_if_fail (CLUTTER_IS_MODEL (model), -1);
1316
1317   return model->priv->sort_column;
1318 }
1319
1320 /**
1321  * clutter_model_foreach:
1322  * @model: a #ClutterModel
1323  * @func: a #ClutterModelForeachFunc
1324  * @user_data: user data to pass to @func
1325  *
1326  * Calls @func for each row in the model. 
1327  *
1328  * Since: 0.6
1329  */
1330 void
1331 clutter_model_foreach (ClutterModel            *model,
1332                        ClutterModelForeachFunc  func,
1333                        gpointer                 user_data)
1334 {
1335   ClutterModelIter *iter;
1336   
1337   g_return_if_fail (CLUTTER_IS_MODEL (model));
1338
1339   iter = clutter_model_get_first_iter (model);
1340   if (!iter)
1341     return;
1342
1343   while (!clutter_model_iter_is_last (iter))
1344     {
1345       if (clutter_model_filter_iter (model, iter))
1346         {
1347           if (!func (model, iter, user_data))
1348             break;
1349         }
1350
1351       iter = clutter_model_iter_next (iter);
1352     }
1353
1354   g_object_unref (iter);
1355 }
1356
1357 /**
1358  * clutter_model_set_sort:
1359  * @model: a #ClutterModel
1360  * @column: the column to sort on
1361  * @func: a #ClutterModelSortFunc, or #NULL
1362  * @user_data: user data to pass to @func, or #NULL
1363  * @notify: destroy notifier of @user_data, or #NULL
1364  *
1365  * Sorts @model using the given sorting function.
1366  *
1367  * Since: 0.6
1368  */
1369 void
1370 clutter_model_set_sort (ClutterModel         *model,
1371                         guint                 column,
1372                         ClutterModelSortFunc  func,
1373                         gpointer              user_data,
1374                         GDestroyNotify        notify)
1375 {
1376   ClutterModelPrivate *priv;
1377     
1378   g_return_if_fail (CLUTTER_IS_MODEL (model));
1379   g_return_if_fail ((func != NULL && column >= 0) ||
1380                     (func == NULL && column == -1));
1381
1382   priv = model->priv;
1383
1384   if (priv->sort_notify)
1385     priv->sort_notify (priv->sort_data);
1386
1387   priv->sort_func = func;
1388   priv->sort_data = user_data;
1389   priv->sort_notify = notify;
1390   
1391   /* This takes care of calling _model_sort & emitting the signal*/
1392   clutter_model_set_sorting_column (model, column);
1393 }
1394
1395 /**
1396  * clutter_model_set_filter:
1397  * @model: a #ClutterModel
1398  * @func: a #ClutterModelFilterFunc, or #NULL
1399  * @user_data: user data to pass to @func, or #NULL
1400  * @notify: destroy notifier of @user_data, or #NULL
1401  *
1402  * Filters the @model using the given filtering function.
1403  *
1404  * Since: 0.6
1405  */
1406 void
1407 clutter_model_set_filter (ClutterModel           *model,
1408                           ClutterModelFilterFunc  func,
1409                           gpointer                user_data,
1410                           GDestroyNotify          notify)
1411 {
1412   ClutterModelPrivate *priv;
1413     
1414   g_return_if_fail (CLUTTER_IS_MODEL (model));
1415   priv = model->priv;
1416
1417   if (priv->filter_notify)
1418     priv->filter_notify (priv->filter_data);
1419
1420   priv->filter_func = func;
1421   priv->filter_data = user_data;
1422   priv->filter_notify = notify;
1423
1424   g_signal_emit (model, model_signals[FILTER_CHANGED], 0);
1425   g_object_notify (G_OBJECT (model), "filter-set");
1426 }
1427
1428 /**
1429  * clutter_model_get_filter_set:
1430  * @model: a #ClutterModel
1431  *
1432  * Returns whether the @model has a filter in place, set
1433  * using clutter_model_set_filter()
1434  *
1435  * Return value: %TRUE if a filter is set
1436  *
1437  * Since: 1.0
1438  */
1439 gboolean
1440 clutter_model_get_filter_set (ClutterModel *model)
1441 {
1442   g_return_val_if_fail (CLUTTER_IS_MODEL (model), FALSE);
1443
1444   return model->priv->filter_func != NULL;
1445 }
1446
1447 /*
1448  * ClutterModelIter Object 
1449  */
1450
1451 /**
1452  * SECTION:clutter-model-iter
1453  * @short_description: Iterates through a model
1454  *
1455  * #ClutterModelIter is an object used for iterating through all the rows
1456  * of a #ClutterModel. It allows setting and getting values on the row
1457  * which is currently pointing at.
1458  *
1459  * A #ClutterModelIter represents a position between two elements
1460  * of the sequence. For example, the iterator returned by
1461  * clutter_model_get_first_iter() represents the gap immediately before
1462  * the first row of the #ClutterModel, and the iterator returned by
1463  * clutter_model_get_last_iter() represents the gap immediately after the
1464  * last row.
1465  *
1466  * A #ClutterModelIter can only be created by a #ClutterModel implementation
1467  * and it is valid as long as the model does not change.
1468  *
1469  * #ClutterModelIter is available since Clutter 0.6
1470  */
1471
1472 G_DEFINE_ABSTRACT_TYPE (ClutterModelIter, clutter_model_iter, G_TYPE_OBJECT);
1473
1474 #define CLUTTER_MODEL_ITER_GET_PRIVATE(obj) \
1475   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
1476   CLUTTER_TYPE_MODEL_ITER, ClutterModelIterPrivate))
1477
1478 struct _ClutterModelIterPrivate
1479 {
1480   ClutterModel  *model;
1481
1482   gint row;
1483
1484   guint ignore_sort : 1;
1485 };
1486
1487 enum
1488 {
1489   ITER_PROP_0,
1490
1491   ITER_PROP_MODEL,
1492   ITER_PROP_ROW
1493 };
1494
1495 static ClutterModel *
1496 clutter_model_iter_real_get_model (ClutterModelIter *iter)
1497 {
1498   return iter->priv->model;
1499 }
1500
1501 static guint
1502 clutter_model_iter_real_get_row (ClutterModelIter *iter)
1503 {
1504   return iter->priv->row;
1505 }
1506
1507 /* private function */
1508 void
1509 clutter_model_iter_set_row (ClutterModelIter *iter,
1510                             guint             row)
1511 {
1512   iter->priv->row = row;
1513 }
1514
1515 static void
1516 clutter_model_iter_get_value_unimplemented (ClutterModelIter *iter,
1517                                             guint             column,
1518                                             GValue           *value)
1519 {
1520   g_warning ("%s: Iterator of type '%s' does not implement the "
1521              "ClutterModelIter::get_value() virtual function",
1522              G_STRLOC,
1523              g_type_name (G_OBJECT_TYPE (iter)));
1524 }
1525
1526 static void
1527 clutter_model_iter_set_value_unimplemented (ClutterModelIter *iter,
1528                                             guint             column,
1529                                             const GValue     *value)
1530 {
1531   g_warning ("%s: Iterator of type '%s' does not implement the "
1532              "ClutterModelIter::set_value() virtual function",
1533              G_STRLOC,
1534              g_type_name (G_OBJECT_TYPE (iter)));
1535 }
1536
1537 static gboolean
1538 clutter_model_iter_is_first_unimplemented (ClutterModelIter *iter)
1539 {
1540   g_warning ("%s: Iterator of type '%s' does not implement the "
1541              "ClutterModelIter::is_first() virtual function",
1542              G_STRLOC,
1543              g_type_name (G_OBJECT_TYPE (iter)));
1544   return FALSE;
1545 }
1546
1547 static gboolean
1548 clutter_model_iter_is_last_unimplemented (ClutterModelIter *iter)
1549 {
1550   g_warning ("%s: Iterator of type '%s' does not implement the "
1551              "ClutterModelIter::is_last() virtual function",
1552              G_STRLOC,
1553              g_type_name (G_OBJECT_TYPE (iter)));
1554   return FALSE;
1555 }
1556
1557 static ClutterModelIter *
1558 clutter_model_iter_next_unimplemented (ClutterModelIter *iter)
1559 {
1560   g_warning ("%s: Iterator of type '%s' does not implement the "
1561              "ClutterModelIter::next() virtual function",
1562              G_STRLOC,
1563              g_type_name (G_OBJECT_TYPE (iter)));
1564   return NULL;
1565 }
1566
1567 static ClutterModelIter *
1568 clutter_model_iter_prev_unimplemented (ClutterModelIter *iter)
1569 {
1570   g_warning ("%s: Iterator of type '%s' does not implement the "
1571              "ClutterModelIter::prev() virtual function",
1572              G_STRLOC,
1573              g_type_name (G_OBJECT_TYPE (iter)));
1574   return NULL;
1575 }
1576
1577 static ClutterModelIter *
1578 clutter_model_iter_copy_unimplemented (ClutterModelIter *iter)
1579 {
1580   g_warning ("%s: Iterator of type '%s' does not implement the "
1581              "ClutterModelIter::copy() virtual function",
1582              G_STRLOC,
1583              g_type_name (G_OBJECT_TYPE (iter)));
1584   return NULL;
1585 }
1586
1587 static void
1588 clutter_model_iter_get_property (GObject    *object,
1589                                  guint       prop_id,
1590                                  GValue     *value,
1591                                  GParamSpec *pspec)
1592 {
1593   ClutterModelIter *iter = CLUTTER_MODEL_ITER (object);
1594   ClutterModelIterPrivate *priv = iter->priv;
1595
1596   switch (prop_id)
1597     {
1598     case ITER_PROP_MODEL:
1599       g_value_set_object (value, priv->model);
1600       break;
1601     case ITER_PROP_ROW:
1602       g_value_set_uint (value, priv->row);
1603       break;
1604     default:
1605       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1606       break;
1607     }
1608 }
1609
1610 static void
1611 clutter_model_iter_set_property (GObject      *object,
1612                                  guint         prop_id,
1613                                  const GValue *value,
1614                                  GParamSpec   *pspec)
1615 {
1616   ClutterModelIter *iter = CLUTTER_MODEL_ITER (object);
1617   ClutterModelIterPrivate *priv = iter->priv;
1618
1619   switch (prop_id)
1620     {
1621     case ITER_PROP_MODEL:
1622       priv->model = g_value_get_object (value);
1623       break;
1624     case ITER_PROP_ROW:
1625       priv->row = g_value_get_uint (value);
1626       break;
1627     default:
1628       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1629       break;
1630     }
1631 }
1632
1633 static void
1634 clutter_model_iter_class_init (ClutterModelIterClass *klass)
1635 {
1636   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1637   GParamSpec *pspec;
1638
1639   gobject_class->get_property = clutter_model_iter_get_property;
1640   gobject_class->set_property = clutter_model_iter_set_property;
1641
1642   klass->get_model = clutter_model_iter_real_get_model;
1643   klass->get_row   = clutter_model_iter_real_get_row;
1644   klass->is_first  = clutter_model_iter_is_first_unimplemented;
1645   klass->is_last   = clutter_model_iter_is_last_unimplemented;
1646   klass->next      = clutter_model_iter_next_unimplemented;
1647   klass->prev      = clutter_model_iter_prev_unimplemented;
1648   klass->get_value = clutter_model_iter_get_value_unimplemented;
1649   klass->set_value = clutter_model_iter_set_value_unimplemented;
1650   klass->copy      = clutter_model_iter_copy_unimplemented;
1651
1652   /* Properties */
1653
1654   /**
1655    * ClutterModelIter:model:
1656    *
1657    * A reference to the #ClutterModel that this iter belongs to.
1658    *
1659    * Since: 0.6
1660    */
1661   pspec = g_param_spec_object ("model",
1662                                "Model",
1663                                "The model to which the iterator belongs to",
1664                                CLUTTER_TYPE_MODEL,
1665                                CLUTTER_PARAM_READWRITE);
1666   g_object_class_install_property (gobject_class, ITER_PROP_MODEL, pspec);
1667
1668   /**
1669    * ClutterModelIter:row:
1670    *
1671    * The row number to which this iter points to.
1672    *
1673    * Since: 0.6
1674    */
1675   pspec = g_param_spec_uint ("row",
1676                              "Row",
1677                              "The row to which the iterator points to",
1678                              0, G_MAXUINT, 0,
1679                              CLUTTER_PARAM_READWRITE);
1680   g_object_class_install_property (gobject_class, ITER_PROP_ROW, pspec);
1681   
1682   g_type_class_add_private (gobject_class, sizeof (ClutterModelIterPrivate));
1683 }
1684
1685 static void
1686 clutter_model_iter_init (ClutterModelIter *self)
1687 {
1688   ClutterModelIterPrivate *priv;
1689   
1690   self->priv = priv = CLUTTER_MODEL_ITER_GET_PRIVATE (self);
1691
1692   priv->model = NULL;
1693   priv->row = 0;
1694
1695   priv->ignore_sort = FALSE;
1696 }
1697
1698 /*
1699  *  Public functions
1700  */
1701
1702 static void
1703 clutter_model_iter_set_internal_valist (ClutterModelIter *iter,
1704                                         va_list           args)
1705 {
1706   ClutterModel *model;
1707   ClutterModelIterPrivate *priv;
1708   guint column = 0;
1709   gboolean sort = FALSE;
1710   
1711   priv = iter->priv;
1712   model = priv->model;
1713   g_assert (CLUTTER_IS_MODEL (model));
1714
1715   column = va_arg (args, gint);
1716   
1717   /* Don't want to sort while setting lots of fields, leave that till the end
1718    */
1719   priv->ignore_sort = TRUE;
1720   while (column != -1)
1721     {
1722       GValue value = { 0, };
1723       gchar *error = NULL;
1724
1725       if (column < 0 || column >= clutter_model_get_n_columns (model))
1726         { 
1727           g_warning ("%s: Invalid column number %d added to iter "
1728                      "(remember to end you list of columns with a -1)",
1729                      G_STRLOC, column);
1730           break;
1731         }
1732       g_value_init (&value, clutter_model_get_column_type (model, column));
1733
1734       G_VALUE_COLLECT (&value, args, 0, &error);
1735       if (error)
1736         {
1737           g_warning ("%s: %s", G_STRLOC, error);
1738           g_free (error);
1739
1740           /* Leak value as it might not be in a sane state */
1741           break;
1742         }
1743
1744       clutter_model_iter_set_value (iter, column, &value);
1745       
1746       g_value_unset (&value);
1747       
1748       if (column == clutter_model_get_sorting_column (model))
1749         sort = TRUE;
1750       
1751       column = va_arg (args, gint);
1752     }
1753
1754   priv->ignore_sort = FALSE;
1755   if (sort)
1756     clutter_model_resort (model);
1757 }
1758
1759 /**
1760  * clutter_model_iter_set_valist:
1761  * @iter: a #ClutterModelIter
1762  * @args: va_list of column/value pairs, terminiated by -1
1763  *
1764  * See clutter_model_iter_set(); this version takes a va_list for language
1765  * bindings.
1766  *
1767  * Since: 0.6
1768  */
1769 void 
1770 clutter_model_iter_set_valist (ClutterModelIter *iter,
1771                                va_list           args)
1772 {
1773   ClutterModelIterPrivate *priv;
1774   ClutterModel *model;
1775
1776   g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
1777
1778   clutter_model_iter_set_internal_valist (iter, args);
1779
1780   priv = iter->priv;
1781   model = priv->model;
1782   g_assert (CLUTTER_IS_MODEL (model));
1783
1784   g_signal_emit (model, model_signals[ROW_CHANGED], 0, iter);
1785 }
1786
1787 /**
1788  * clutter_model_iter_get:
1789  * @iter: a #ClutterModelIter
1790  * @Varargs: a list of column/return location pairs, terminated by -1
1791  *
1792  * Gets the value of one or more cells in the row referenced by @iter. The
1793  * variable argument list should contain integer column numbers, each column
1794  * column number followed by a place to store the value being retrieved. The
1795  * list is terminated by a -1.
1796  *
1797  * For example, to get a value from column 0 with type %G_TYPE_STRING use:
1798  * <informalexample><programlisting>
1799  *   clutter_model_iter_get (iter, 0, &place_string_here, -1);
1800  * </programlisting></informalexample>
1801  * 
1802  * where place_string_here is a gchar* to be filled with the string. If
1803  * appropriate, the returned values have to be freed or unreferenced.
1804  *
1805  * Since: 0.6
1806  */
1807 void
1808 clutter_model_iter_get (ClutterModelIter *iter,
1809                         ...)
1810 {
1811   va_list args;
1812
1813   g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
1814
1815   va_start (args, iter);
1816   clutter_model_iter_get_valist (iter, args);
1817   va_end (args);
1818 }
1819
1820 /**
1821  * clutter_model_iter_get_value:
1822  * @iter: a #ClutterModelIter
1823  * @column: column number to retrieve the value from
1824  * @value: an empty #GValue to set
1825  *
1826  * Sets an initializes @value to that at @column. When done with @value, 
1827  * g_value_unset() needs to be called to free any allocated memory.
1828  *
1829  * Since: 0.6
1830  */
1831 void
1832 clutter_model_iter_get_value (ClutterModelIter *iter,
1833                               guint             column,
1834                               GValue           *value)
1835 {
1836   ClutterModelIterClass *klass;
1837   ClutterModel *model;
1838
1839   g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
1840   
1841   model = iter->priv->model;
1842
1843   g_value_init (value, clutter_model_get_column_type (model, column));
1844
1845   klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
1846   if (klass && klass->get_value)
1847     klass->get_value (iter, column, value);
1848 }
1849
1850 /**
1851  * clutter_model_iter_get_valist:
1852  * @iter: a #ClutterModelIter
1853  * @args: a list of column/return location pairs, terminated by -1
1854  *
1855  * See clutter_model_iter_get(). This version takes a va_list for language
1856  * bindings.
1857  *
1858  * Since: 0.6
1859  */
1860 void 
1861 clutter_model_iter_get_valist (ClutterModelIter *iter,
1862                                va_list           args)
1863 {
1864   ClutterModelIterPrivate *priv;
1865   ClutterModel *model;
1866   guint column = 0;
1867   
1868   g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
1869
1870   priv = iter->priv;
1871   model = priv->model;
1872   g_assert (CLUTTER_IS_MODEL (model));
1873
1874   column = va_arg (args, gint);
1875
1876   while (column != -1)
1877     {
1878       GValue value = { 0, };
1879       gchar *error = NULL;
1880
1881       if (column < 0 || column >= clutter_model_get_n_columns (model))
1882         { 
1883           g_warning ("%s: Invalid column number %d added to iter "
1884                      "(remember to end you list of columns with a -1)",
1885                      G_STRLOC, column);
1886           break;
1887         }
1888
1889       /* this one will take care of initialising value to the
1890        * correct type
1891        */
1892       clutter_model_iter_get_value (iter, column, &value);
1893
1894       G_VALUE_LCOPY (&value, args, 0, &error);
1895       if (error)
1896         {
1897           g_warning ("%s: %s", G_STRLOC, error);
1898           g_free (error);
1899
1900           /* Leak value as it might not be in a sane state */
1901           break;
1902         }
1903      
1904       g_value_unset (&value);
1905       
1906       column = va_arg (args, gint);
1907     }
1908 }
1909
1910 /**
1911  * clutter_model_iter_set:
1912  * @iter: a #ClutterModelIter
1913  * @Varargs: a list of column/return location pairs, terminated by -1
1914  *
1915  * Sets the value of one or more cells in the row referenced by @iter. The
1916  * variable argument list should contain integer column numbers, each column
1917  * column number followed by the value to be set. The  list is terminated by a
1918  * -1.
1919  *
1920  * For example, to set column 0 with type %G_TYPE_STRING, use:
1921  * <informalexample><programlisting>
1922  *   clutter_model_iter_set (iter, 0, "foo", -1);
1923  * </programlisting></informalexample>
1924  *
1925  * Since: 0.6
1926  */
1927 void
1928 clutter_model_iter_set (ClutterModelIter *iter,
1929                         ...)
1930 {
1931   va_list args;
1932
1933   g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
1934
1935   va_start (args, iter);
1936   clutter_model_iter_set_valist (iter, args);
1937   va_end (args);
1938 }
1939
1940
1941 /**
1942  * clutter_model_iter_set_value:
1943  * @iter: a #ClutterModelIter
1944  * @column: column number to retrieve the value from
1945  * @value: new value for the cell
1946  *
1947  * Sets the data in the cell specified by @iter and @column. The type of
1948  * @value must be convertable to the type of the column.
1949  *
1950  * Since: 0.6
1951  */
1952 void
1953 clutter_model_iter_set_value (ClutterModelIter *iter,
1954                               guint             column,
1955                               const GValue     *value)
1956 {
1957   ClutterModelIterClass *klass;
1958
1959   g_return_if_fail (CLUTTER_IS_MODEL_ITER (iter));
1960   
1961   klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
1962   if (klass && klass->set_value)
1963     klass->set_value (iter, column, value);
1964 }
1965
1966 /**
1967  * clutter_model_iter_is_first:
1968  * @iter: a #ClutterModelIter
1969  *
1970  * Gets whether the current iterator is at the beginning of the model
1971  * to which it belongs.
1972  *
1973  * Return value: #TRUE if @iter is the first iter in the filtered model
1974  *
1975  * Since: 0.6
1976  */
1977 gboolean
1978 clutter_model_iter_is_first (ClutterModelIter *iter)
1979 {
1980   ClutterModelIterClass *klass;
1981
1982   g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), FALSE);
1983   
1984   klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
1985   if (klass && klass->is_first)
1986     return klass->is_first (iter);
1987
1988   return FALSE;
1989 }
1990
1991 /**
1992  * clutter_model_iter_is_last:
1993  * @iter: a #ClutterModelIter
1994  * 
1995  * Gets whether the iterator is at the end of the model to which it
1996  * belongs.
1997  *
1998  * Return value: #TRUE if @iter is the last iter in the filtered model.
1999  *
2000  * Since: 0.6
2001  */
2002 gboolean
2003 clutter_model_iter_is_last (ClutterModelIter *iter)
2004 {
2005   ClutterModelIterClass *klass;
2006
2007   g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), FALSE);
2008   
2009   klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
2010   if (klass && klass->is_last)
2011     return klass->is_last (iter);
2012
2013   return FALSE;
2014 }
2015
2016 /**
2017  * clutter_model_iter_next:
2018  * @iter: a #ClutterModelIter
2019  * 
2020  * Updates the @iter to point at the next position in the model.
2021  * The model implementation should take into account the presence of
2022  * a filter function.
2023  *
2024  * Return value: (transfer none): The passed iterator, updated to point at the next
2025  *   row in the model.
2026  *
2027  * Since: 0.6
2028  */
2029 ClutterModelIter *
2030 clutter_model_iter_next (ClutterModelIter *iter)
2031 {
2032   ClutterModelIterClass *klass;
2033
2034   g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), NULL);
2035   
2036   klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
2037   if (klass && klass->next)
2038     return klass->next (iter);
2039
2040   return NULL;
2041 }
2042
2043 /**
2044  * clutter_model_iter_prev:
2045  * @iter: a #ClutterModelIter
2046  * 
2047  * Sets the @iter to point at the previous position in the model.
2048  * The model implementation should take into account the presence of
2049  * a filter function.
2050  *
2051  * Return value: (transfer none): The passed iterator, updated to point at the previous
2052  *   row in the model.
2053  *
2054  * Since: 0.6
2055  */
2056 ClutterModelIter *
2057 clutter_model_iter_prev (ClutterModelIter *iter)
2058 {
2059   ClutterModelIterClass *klass;
2060
2061   g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), NULL);
2062   
2063   klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
2064   if (klass && klass->prev)
2065     return klass->prev (iter);
2066
2067   return NULL;
2068 }
2069
2070 /**
2071  * clutter_model_iter_get_model:
2072  * @iter: a #ClutterModelIter
2073  * 
2074  * Retrieves a pointer to the #ClutterModel that this iter is part of.
2075  *
2076  * Return value: (transfer none): a pointer to a #ClutterModel.
2077  *
2078  * Since: 0.6
2079  */
2080 ClutterModel *
2081 clutter_model_iter_get_model (ClutterModelIter *iter)
2082 {
2083   ClutterModelIterClass *klass;
2084
2085   g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), NULL);
2086   
2087   klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
2088   if (klass && klass->get_model)
2089     return klass->get_model (iter);
2090
2091   return NULL;
2092 }
2093
2094 /**
2095  * clutter_model_iter_get_row:
2096  * @iter: a #ClutterModelIter
2097  * 
2098  * Retrieves the position of the row that the @iter points to.
2099  *
2100  * Return value: the position of the @iter in the model
2101  *
2102  * Since: 0.6
2103  */
2104 guint
2105 clutter_model_iter_get_row (ClutterModelIter *iter)
2106 {
2107   ClutterModelIterClass *klass;
2108
2109   g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), 0);
2110   
2111   klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
2112   if (klass && klass->get_row)
2113     return klass->get_row (iter);
2114
2115   return 0;
2116 }
2117
2118 /**
2119  * clutter_model_iter_copy:
2120  * @iter: a #ClutterModelIter
2121  *
2122  * Copies the passed iterator.
2123  *
2124  * Return value: a copy of the iterator, or %NULL
2125  *
2126  * Since: 0.8
2127  */
2128 ClutterModelIter *
2129 clutter_model_iter_copy (ClutterModelIter *iter)
2130 {
2131   ClutterModelIterClass *klass;
2132
2133   g_return_val_if_fail (CLUTTER_IS_MODEL_ITER (iter), NULL);
2134
2135   klass = CLUTTER_MODEL_ITER_GET_CLASS (iter);
2136   if (klass->copy)
2137     return klass->copy (iter);
2138
2139   return NULL;
2140 }