update to 1.10.4
[profile/ivi/clutter.git] / clutter / clutter-list-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, see <http://www.gnu.org/licenses/>.
24  *
25  *
26  */
27
28 /**
29  * SECTION:clutter-list-model
30  * @short_description: List model implementation
31  *
32  * #ClutterListModel is a #ClutterModel implementation provided by
33  * Clutter. #ClutterListModel uses a #GSequence for storing the
34  * values for each row, so it's optimized for insertion and look up
35  * in sorted lists.
36  *
37  * #ClutterListModel is available since Clutter 0.6
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include <glib-object.h>
48
49 #include "clutter-model.h"
50 #include "clutter-model-private.h"
51 #include "clutter-list-model.h"
52 #include "clutter-private.h"
53 #include "clutter-debug.h"
54
55 #define CLUTTER_TYPE_LIST_MODEL_ITER                 \
56         (clutter_list_model_iter_get_type())
57 #define CLUTTER_LIST_MODEL_ITER(obj)                 \
58         (G_TYPE_CHECK_INSTANCE_CAST((obj),           \
59          CLUTTER_TYPE_LIST_MODEL_ITER,               \
60          ClutterListModelIter))
61 #define CLUTTER_IS_LIST_MODEL_ITER(obj)              \
62         (G_TYPE_CHECK_INSTANCE_TYPE((obj),           \
63          CLUTTER_TYPE_LIST_MODEL_ITER))
64 #define CLUTTER_LIST_MODEL_ITER_CLASS(klass)         \
65         (G_TYPE_CHECK_CLASS_CAST ((klass),           \
66          CLUTTER_TYPE_LIST_MODEL_ITER,               \
67          ClutterListModelIterClass))
68 #define CLUTTER_IS_LIST_MODEL_ITER_CLASS(klass)      \
69         (G_TYPE_CHECK_CLASS_TYPE ((klass),           \
70          CLUTTER_TYPE_LIST_MODEL_ITER))
71 #define CLUTTER_LIST_MODEL_ITER_GET_CLASS(obj)       \
72         (G_TYPE_INSTANCE_GET_CLASS ((obj),           \
73          CLUTTER_TYPE_LIST_MODEL_ITER,               \
74          ClutterListModelIterClass))
75
76 typedef struct _ClutterListModelIter    ClutterListModelIter;
77 typedef struct _ClutterModelIterClass   ClutterListModelIterClass;
78
79 #define CLUTTER_LIST_MODEL_GET_PRIVATE(obj)     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_LIST_MODEL, ClutterListModelPrivate))
80
81 struct _ClutterListModelPrivate
82 {
83   GSequence *sequence;
84
85   ClutterModelIter *temp_iter;
86 };
87
88 struct _ClutterListModelIter
89 {
90   ClutterModelIter parent_instance;
91
92   GSequenceIter *seq_iter;
93 };
94
95 \f
96
97 GType clutter_list_model_iter_get_type (void);
98
99 /*
100  * ClutterListModel
101  */
102
103 G_DEFINE_TYPE (ClutterListModelIter,
104                clutter_list_model_iter,
105                CLUTTER_TYPE_MODEL_ITER);
106
107 static void
108 clutter_list_model_iter_get_value (ClutterModelIter *iter,
109                                    guint             column,
110                                    GValue           *value)
111 {
112   ClutterListModelIter *iter_default;
113   GValue *values;
114   GValue *iter_value;
115   GValue real_value = G_VALUE_INIT;
116   gboolean converted = FALSE;
117
118   iter_default = CLUTTER_LIST_MODEL_ITER (iter);
119   g_assert (iter_default->seq_iter != NULL);
120
121   values = g_sequence_get (iter_default->seq_iter);
122   iter_value = &values[column];
123   g_assert (iter_value != NULL);
124
125   if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value)))
126     {
127       if (!g_value_type_compatible (G_VALUE_TYPE (value), 
128                                     G_VALUE_TYPE (iter_value)) &&
129           !g_value_type_compatible (G_VALUE_TYPE (iter_value), 
130                                     G_VALUE_TYPE (value)))
131         {
132           g_warning ("%s: Unable to convert from %s to %s",
133                      G_STRLOC,
134                      g_type_name (G_VALUE_TYPE (value)),
135                      g_type_name (G_VALUE_TYPE (iter_value)));
136           return;
137         }
138
139       if (!g_value_transform (iter_value, &real_value))
140         {
141           g_warning ("%s: Unable to make conversion from %s to %s",
142                      G_STRLOC, 
143                      g_type_name (G_VALUE_TYPE (value)),
144                      g_type_name (G_VALUE_TYPE (iter_value)));
145           g_value_unset (&real_value);
146         }
147
148       converted = TRUE;
149     }
150   
151   if (converted)
152     {
153       g_value_copy (&real_value, value);
154       g_value_unset (&real_value);
155     }
156   else
157     g_value_copy (iter_value, value);
158 }
159
160 static void
161 clutter_list_model_iter_set_value (ClutterModelIter *iter,
162                                    guint             column,
163                                    const GValue     *value)
164 {
165   ClutterListModelIter *iter_default;
166   GValue *values;
167   GValue *iter_value;
168   GValue real_value = G_VALUE_INIT;
169   gboolean converted = FALSE;
170
171   iter_default = CLUTTER_LIST_MODEL_ITER (iter);
172   g_assert (iter_default->seq_iter != NULL);
173
174   values = g_sequence_get (iter_default->seq_iter);
175   iter_value = &values[column];
176   g_assert (iter_value != NULL);
177
178   if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value)))
179     {
180       if (!g_value_type_compatible (G_VALUE_TYPE (value), 
181                                     G_VALUE_TYPE (iter_value)) &&
182           !g_value_type_compatible (G_VALUE_TYPE (iter_value), 
183                                     G_VALUE_TYPE (value)))
184         {
185           g_warning ("%s: Unable to convert from %s to %s\n",
186                      G_STRLOC,
187                      g_type_name (G_VALUE_TYPE (value)),
188                      g_type_name (G_VALUE_TYPE (iter_value)));
189           return;
190         }
191
192       if (!g_value_transform (value, &real_value))
193         {
194           g_warning ("%s: Unable to make conversion from %s to %s\n",
195                      G_STRLOC, 
196                      g_type_name (G_VALUE_TYPE (value)),
197                      g_type_name (G_VALUE_TYPE (iter_value)));
198           g_value_unset (&real_value);
199         }
200
201       converted = TRUE;
202     }
203  
204   if (converted)
205     {
206       g_value_copy (&real_value, iter_value);
207       g_value_unset (&real_value);
208     }
209   else
210     g_value_copy (value, iter_value);
211 }
212
213 static gboolean
214 clutter_list_model_iter_is_first (ClutterModelIter *iter)
215 {
216   ClutterListModelIter *iter_default;
217   ClutterModel *model;
218   ClutterModelIter *temp_iter;
219   GSequence *sequence;
220   GSequenceIter *begin, *end;
221
222   iter_default = CLUTTER_LIST_MODEL_ITER (iter);
223   g_assert (iter_default->seq_iter != NULL);
224
225   model = clutter_model_iter_get_model (iter);
226
227   sequence = CLUTTER_LIST_MODEL (model)->priv->sequence;
228
229   begin = g_sequence_get_begin_iter (sequence);
230   end   = iter_default->seq_iter;
231
232   temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
233
234   while (!g_sequence_iter_is_begin (begin))
235     {
236       CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = begin;
237
238       if (clutter_model_filter_iter (model, temp_iter))
239         {
240           end = begin;
241           break;
242         }
243
244       begin = g_sequence_iter_next (begin);
245     }
246
247   /* This is because the 'begin_iter' is always *before* the last valid
248    * iter, otherwise we'd have endless loops 
249    */
250   end = g_sequence_iter_prev (end);
251
252   return iter_default->seq_iter == end;
253 }
254
255 static gboolean
256 clutter_list_model_iter_is_last (ClutterModelIter *iter)
257 {
258   ClutterListModelIter *iter_default;
259   ClutterModelIter *temp_iter;
260   ClutterModel *model;
261   GSequence *sequence;
262   GSequenceIter *begin, *end;
263
264   iter_default = CLUTTER_LIST_MODEL_ITER (iter);
265   g_assert (iter_default->seq_iter != NULL);
266
267   if (g_sequence_iter_is_end (iter_default->seq_iter))
268     return TRUE;
269
270   model = clutter_model_iter_get_model (iter);
271
272   sequence = CLUTTER_LIST_MODEL (model)->priv->sequence;
273
274   begin = g_sequence_get_end_iter (sequence);
275   begin = g_sequence_iter_prev (begin);
276   end   = iter_default->seq_iter;
277
278   temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
279
280   while (!g_sequence_iter_is_begin (begin))
281     {
282       CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = begin;
283
284       if (clutter_model_filter_iter (model, temp_iter))
285         {
286           end = begin;
287           break;
288         }
289
290       begin = g_sequence_iter_prev (begin);
291     }
292
293   /* This is because the 'end_iter' is always *after* the last valid iter.
294    * Otherwise we'd have endless loops 
295    */
296   end = g_sequence_iter_next (end);
297
298   return iter_default->seq_iter == end;
299 }
300
301 static ClutterModelIter *
302 clutter_list_model_iter_next (ClutterModelIter *iter)
303 {
304   ClutterListModelIter *iter_default;
305   ClutterModelIter *temp_iter;
306   ClutterModel *model = NULL;
307   GSequenceIter *filter_next;
308   guint row;
309
310   iter_default = CLUTTER_LIST_MODEL_ITER (iter);
311   g_assert (iter_default->seq_iter != NULL);
312
313   model = clutter_model_iter_get_model (iter);
314   row   = clutter_model_iter_get_row (iter);
315
316   filter_next = g_sequence_iter_next (iter_default->seq_iter);
317   g_assert (filter_next != NULL);
318
319   temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
320
321   while (!g_sequence_iter_is_end (filter_next))
322     {
323       CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = filter_next;
324
325       if (clutter_model_filter_iter (model, temp_iter))
326         {
327           row += 1;
328           break;
329         }
330
331       filter_next = g_sequence_iter_next (filter_next);
332     }
333
334   if (g_sequence_iter_is_end (filter_next))
335     row += 1;
336
337   /* update the iterator and return it */
338   _clutter_model_iter_set_row (CLUTTER_MODEL_ITER (iter_default), row);
339   iter_default->seq_iter = filter_next;
340
341   return CLUTTER_MODEL_ITER (iter_default);
342 }
343
344 static ClutterModelIter *
345 clutter_list_model_iter_prev (ClutterModelIter *iter)
346 {
347   ClutterListModelIter *iter_default;
348   ClutterModelIter *temp_iter;
349   ClutterModel *model;
350   GSequenceIter *filter_prev;
351   guint row;
352
353   iter_default = CLUTTER_LIST_MODEL_ITER (iter);
354   g_assert (iter_default->seq_iter != NULL);
355
356   model = clutter_model_iter_get_model (iter);
357   row   = clutter_model_iter_get_row (iter);
358
359   filter_prev = g_sequence_iter_prev (iter_default->seq_iter);
360   g_assert (filter_prev != NULL);
361
362   temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
363
364   while (!g_sequence_iter_is_begin (filter_prev))
365     {
366       CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = filter_prev;
367
368       if (clutter_model_filter_iter (model, temp_iter))
369         {
370           row -= 1;
371           break;
372         }
373
374       filter_prev = g_sequence_iter_prev (filter_prev);
375     }
376
377   if (g_sequence_iter_is_begin (filter_prev))
378     row -= 1;
379
380   /* update the iterator and return it */
381   _clutter_model_iter_set_row (CLUTTER_MODEL_ITER (iter_default), row);
382   iter_default->seq_iter = filter_prev;
383
384   return CLUTTER_MODEL_ITER (iter_default);
385 }
386
387 static ClutterModelIter *
388 clutter_list_model_iter_copy (ClutterModelIter *iter)
389 {
390   ClutterListModelIter *iter_default;
391   ClutterListModelIter *iter_copy;
392   ClutterModel *model;
393   guint row;
394  
395   iter_default = CLUTTER_LIST_MODEL_ITER (iter);
396
397   model = clutter_model_iter_get_model (iter);
398   row   = clutter_model_iter_get_row (iter) - 1;
399
400   iter_copy = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
401                             "model", model,
402                             "row", row,
403                             NULL);
404
405   /* this is safe, because the seq_iter pointer on the passed
406    * iterator will be always be overwritten in ::next or ::prev
407    */
408   iter_copy->seq_iter = iter_default->seq_iter;
409
410   return CLUTTER_MODEL_ITER (iter_copy);
411 }
412
413 static void
414 clutter_list_model_iter_class_init (ClutterListModelIterClass *klass)
415 {
416   ClutterModelIterClass *iter_class = CLUTTER_MODEL_ITER_CLASS (klass);
417
418   iter_class->get_value = clutter_list_model_iter_get_value;
419   iter_class->set_value = clutter_list_model_iter_set_value;
420   iter_class->is_first  = clutter_list_model_iter_is_first;
421   iter_class->is_last   = clutter_list_model_iter_is_last;
422   iter_class->next      = clutter_list_model_iter_next;
423   iter_class->prev      = clutter_list_model_iter_prev;
424   iter_class->copy      = clutter_list_model_iter_copy;
425 }
426
427 static void
428 clutter_list_model_iter_init (ClutterListModelIter *iter)
429 {
430   iter->seq_iter = NULL;
431 }
432
433 /*
434  * ClutterListModel
435  */
436
437 G_DEFINE_TYPE (ClutterListModel, clutter_list_model, CLUTTER_TYPE_MODEL);
438
439 static ClutterModelIter *
440 clutter_list_model_get_iter_at_row (ClutterModel *model,
441                                     guint         row)
442 {
443   ClutterListModel *model_default = CLUTTER_LIST_MODEL (model);
444   GSequence *sequence = model_default->priv->sequence;
445   GSequenceIter *filter_next;
446   gint seq_length = g_sequence_get_length (sequence);
447   ClutterListModelIter *retval;
448   gint count = -1;
449
450   if (row >= seq_length)
451     return NULL;
452
453   retval = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
454                          "model", model,
455                          "row", row,
456                          NULL);
457
458   /* short-circuit in case we don't have a filter in place */
459   if (!clutter_model_get_filter_set (model))
460     {
461       retval->seq_iter = g_sequence_get_iter_at_pos (sequence, row);
462
463       return CLUTTER_MODEL_ITER (retval);
464     }
465
466   filter_next = g_sequence_get_begin_iter (sequence);
467   g_assert (filter_next != NULL);
468
469   while (!g_sequence_iter_is_end (filter_next))
470     {
471       retval->seq_iter = filter_next;
472
473       if (clutter_model_filter_iter (model, CLUTTER_MODEL_ITER (retval)))
474         {
475           /* We've found a row that is valid under the filter */
476           count++;
477           if (count == row)
478             break;
479         }
480
481       filter_next = g_sequence_iter_next (filter_next);
482     }
483
484   if (count != row)
485     {
486       g_object_unref (retval);
487       return NULL;
488     }
489   return CLUTTER_MODEL_ITER (retval);
490 }
491
492 static ClutterModelIter *
493 clutter_list_model_insert_row (ClutterModel *model,
494                                gint          index_)
495 {
496   ClutterListModel *model_default = CLUTTER_LIST_MODEL (model);
497   GSequence *sequence = model_default->priv->sequence;
498   ClutterListModelIter *retval;
499   guint n_columns, i, pos;
500   GValue *values;
501   GSequenceIter *seq_iter;
502
503   n_columns = clutter_model_get_n_columns (model);
504   values = g_new0 (GValue, n_columns);
505
506   for (i = 0; i < n_columns; i++)
507     g_value_init (&values[i], clutter_model_get_column_type (model, i));
508
509   if (index_ < 0)
510     {
511       seq_iter = g_sequence_append (sequence, values);
512       pos = g_sequence_get_length (sequence) - 1;
513     }
514   else if (index_ == 0)
515     {
516       seq_iter = g_sequence_prepend (sequence, values);
517       pos = 0;
518     }
519   else
520     {
521       seq_iter = g_sequence_get_iter_at_pos (sequence, index_);
522       seq_iter = g_sequence_insert_before (seq_iter, values);
523       pos = index_;
524     }
525
526   retval = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
527                          "model", model,
528                          "row", pos,
529                          NULL);
530   retval->seq_iter = seq_iter;
531
532   return CLUTTER_MODEL_ITER (retval);
533 }
534
535 static void
536 clutter_list_model_remove_row (ClutterModel *model,
537                                guint         row)
538 {
539   ClutterListModel *model_default = CLUTTER_LIST_MODEL (model);
540   GSequence *sequence = model_default->priv->sequence;
541   GSequenceIter *seq_iter;
542   guint pos = 0;
543
544   seq_iter = g_sequence_get_begin_iter (sequence);
545   while (!g_sequence_iter_is_end (seq_iter))
546     {
547       if (clutter_model_filter_row (model, pos))
548         {
549           if (pos == row)
550             {  
551               ClutterModelIter *iter;
552
553               iter = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
554                                    "model", model,
555                                    "row", pos,
556                                    NULL);
557               CLUTTER_LIST_MODEL_ITER (iter)->seq_iter = seq_iter;
558
559               /* the actual row is removed from the sequence inside
560                * the ::row-removed signal class handler, so that every
561                * handler connected to ::row-removed will still get
562                * a valid iterator, and every signal connected to
563                * ::row-removed with the AFTER flag will get an updated
564                * model
565                */
566               g_signal_emit_by_name (model, "row-removed", iter);
567
568               g_object_unref (iter);
569
570               break;
571             }
572         }
573
574       pos += 1;
575       seq_iter = g_sequence_iter_next (seq_iter);
576     }
577 }
578
579 typedef struct
580 {
581   ClutterModel *model;
582   guint column;
583   ClutterModelSortFunc func;
584   gpointer data;
585 } SortClosure;
586
587 static gint
588 sort_model_default (gconstpointer a,
589                     gconstpointer b,
590                     gpointer      data)
591 {
592   const GValue *row_a = a;
593   const GValue *row_b = b;
594   SortClosure *clos = data;
595
596   return clos->func (clos->model,
597                      &row_a[clos->column],
598                      &row_b[clos->column],
599                      clos->data);
600 }
601
602 static void
603 clutter_list_model_resort (ClutterModel         *model,
604                            ClutterModelSortFunc  func,
605                            gpointer              data)
606 {
607   SortClosure sort_closure = { NULL, 0, NULL, NULL };
608
609   sort_closure.model  = model;
610   sort_closure.column = clutter_model_get_sorting_column (model);
611   sort_closure.func   = func;
612   sort_closure.data   = data;
613
614   g_sequence_sort (CLUTTER_LIST_MODEL (model)->priv->sequence,
615                    sort_model_default,
616                    &sort_closure);
617 }
618
619 static guint
620 clutter_list_model_get_n_rows (ClutterModel *model)
621 {
622   ClutterListModel *list_model = CLUTTER_LIST_MODEL (model);
623
624   /* short-circuit in case we don't have a filter in place */
625   if (!clutter_model_get_filter_set (model))
626     return g_sequence_get_length (list_model->priv->sequence);
627
628   return CLUTTER_MODEL_CLASS (clutter_list_model_parent_class)->get_n_rows (model);
629 }
630
631 static void
632 clutter_list_model_row_removed (ClutterModel     *model,
633                                 ClutterModelIter *iter)
634 {
635   ClutterListModelIter *iter_default;
636   guint i, n_columns;
637   GValue *values;
638
639   n_columns = clutter_model_get_n_columns (model);
640
641   iter_default = CLUTTER_LIST_MODEL_ITER (iter);
642
643   values = g_sequence_get (iter_default->seq_iter);
644
645   for (i = 0; i < n_columns; i++)
646     g_value_unset (&values[i]);
647
648   g_free (values);
649
650   g_sequence_remove (iter_default->seq_iter);
651   iter_default->seq_iter = NULL;
652 }
653
654 static void
655 clutter_list_model_finalize (GObject *gobject)
656 {
657   ClutterListModel *model = CLUTTER_LIST_MODEL (gobject);
658   GSequence *sequence = model->priv->sequence;
659   GSequenceIter *iter;
660   guint n_columns, i;
661
662   n_columns = clutter_model_get_n_columns (CLUTTER_MODEL (gobject));
663
664   iter = g_sequence_get_begin_iter (sequence);
665   while (!g_sequence_iter_is_end (iter))
666     {
667       GValue *values = g_sequence_get (iter);
668
669       for (i = 0; i < n_columns; i++)
670         g_value_unset (&values[i]);
671
672       g_free (values);
673
674       iter = g_sequence_iter_next (iter);
675     }
676   g_sequence_free (sequence);
677
678   G_OBJECT_CLASS (clutter_list_model_parent_class)->finalize (gobject);
679 }
680
681 static void
682 clutter_list_model_dispose (GObject *gobject)
683 {
684   ClutterListModel *model = CLUTTER_LIST_MODEL (gobject);
685
686   if (model->priv->temp_iter)
687     {
688       g_object_unref (model->priv->temp_iter);
689       model->priv->temp_iter = NULL;
690     }
691
692   G_OBJECT_CLASS (clutter_list_model_parent_class)->dispose (gobject);
693 }
694
695 static void
696 clutter_list_model_class_init (ClutterListModelClass *klass)
697 {
698   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
699   ClutterModelClass *model_class = CLUTTER_MODEL_CLASS (klass);
700
701   g_type_class_add_private (klass, sizeof (ClutterListModelPrivate));
702
703   gobject_class->finalize = clutter_list_model_finalize;
704   gobject_class->dispose = clutter_list_model_dispose;
705
706   model_class->get_iter_at_row = clutter_list_model_get_iter_at_row;
707   model_class->insert_row      = clutter_list_model_insert_row;
708   model_class->remove_row      = clutter_list_model_remove_row;
709   model_class->resort          = clutter_list_model_resort;
710   model_class->get_n_rows      = clutter_list_model_get_n_rows;
711
712   model_class->row_removed     = clutter_list_model_row_removed;
713 }
714
715 static void
716 clutter_list_model_init (ClutterListModel *model)
717 {
718   model->priv = CLUTTER_LIST_MODEL_GET_PRIVATE (model);
719
720   model->priv->sequence = g_sequence_new (NULL);
721   model->priv->temp_iter = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
722                                          "model",
723                                          model,
724                                          NULL);
725 }
726
727 /**
728  * clutter_list_model_new:
729  * @n_columns: number of columns in the model
730  * @...: @n_columns number of #GType and string pairs
731  *
732  * Creates a new default model with @n_columns columns with the types 
733  * and names passed in.
734  *
735  * For example:
736  * 
737  * <informalexample><programlisting>
738  * model = clutter_list_model_new (3,
739  *                                 G_TYPE_INT,      "Score",
740  *                                 G_TYPE_STRING,   "Team",
741  *                                 GDK_TYPE_PIXBUF, "Logo");
742  * </programlisting></informalexample>
743  *
744  * will create a new #ClutterModel with three columns of type int,
745  * string and #GdkPixbuf respectively.
746  *
747  * Note that the name of the column can be set to %NULL, in which case
748  * the canonical name of the type held by the column will be used as
749  * the title.
750  *
751  * Return value: a new #ClutterListModel
752  *
753  * Since: 0.6
754  */
755 ClutterModel *
756 clutter_list_model_new (guint n_columns,
757                         ...)
758 {
759   ClutterModel *model;
760   va_list args;
761   gint i;
762
763   g_return_val_if_fail (n_columns > 0, NULL);
764
765   model = g_object_new (CLUTTER_TYPE_LIST_MODEL, NULL);
766   _clutter_model_set_n_columns (model, n_columns, TRUE, TRUE);
767
768   va_start (args, n_columns);
769
770   for (i = 0; i < n_columns; i++)
771     { 
772       GType type = va_arg (args, GType);
773       const gchar *name = va_arg (args, gchar*);
774
775       if (!_clutter_model_check_type (type))
776         {
777           g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
778           g_object_unref (model);
779           return NULL;
780         }
781
782       _clutter_model_set_column_type (model, i, type);
783       _clutter_model_set_column_name (model, i, name);
784     }
785
786   va_end (args);
787
788   return model;
789 }
790
791 /**
792  * clutter_list_model_newv:
793  * @n_columns: number of columns in the model
794  * @types: (array length=n_columns): an array of #GType types for the columns, from first to last
795  * @names: (array length=n_columns): an array of names for the columns, from first to last
796  *
797  * Non-vararg version of clutter_list_model_new(). This function is
798  * useful for language bindings.
799  *
800  * Return value: (transfer full): a new default #ClutterModel
801  *
802  * Since: 0.6
803  */
804 ClutterModel *
805 clutter_list_model_newv (guint                n_columns,
806                          GType               *types,
807                          const gchar * const  names[])
808 {
809   ClutterModel *model;
810   gint i;
811
812   g_return_val_if_fail (n_columns > 0, NULL);
813
814   model = g_object_new (CLUTTER_TYPE_LIST_MODEL, NULL);
815   _clutter_model_set_n_columns (model, n_columns, TRUE, TRUE);
816
817   for (i = 0; i < n_columns; i++)
818     {
819       if (!_clutter_model_check_type (types[i]))
820         {
821           g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
822           g_object_unref (model);
823           return NULL;
824         }
825
826       _clutter_model_set_column_type (model, i, types[i]);
827       _clutter_model_set_column_name (model, i, names[i]);
828     }
829
830   return model;
831 }