4 * An OpenGL based 'interactive canvas' library.
6 * Authored By Matthew Allum <mallum@openedhand.com>
7 * Neil Jagdish Patel <njp@o-hand.com>
8 * Emmanuele Bassi <ebassi@openedhand.com>
10 * Copyright (C) 2006 OpenedHand
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.
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.
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/>.
29 * SECTION:clutter-list-model
30 * @short_description: List model implementation
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
37 * #ClutterListModel is available since Clutter 0.6
47 #include <glib-object.h>
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"
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))
76 typedef struct _ClutterListModelIter ClutterListModelIter;
77 typedef struct _ClutterModelIterClass ClutterListModelIterClass;
79 #define CLUTTER_LIST_MODEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_LIST_MODEL, ClutterListModelPrivate))
81 struct _ClutterListModelPrivate
85 ClutterModelIter *temp_iter;
88 struct _ClutterListModelIter
90 ClutterModelIter parent_instance;
92 GSequenceIter *seq_iter;
97 GType clutter_list_model_iter_get_type (void);
103 G_DEFINE_TYPE (ClutterListModelIter,
104 clutter_list_model_iter,
105 CLUTTER_TYPE_MODEL_ITER);
108 clutter_list_model_iter_get_value (ClutterModelIter *iter,
112 ClutterListModelIter *iter_default;
115 GValue real_value = G_VALUE_INIT;
116 gboolean converted = FALSE;
118 iter_default = CLUTTER_LIST_MODEL_ITER (iter);
119 g_assert (iter_default->seq_iter != NULL);
121 values = g_sequence_get (iter_default->seq_iter);
122 iter_value = &values[column];
123 g_assert (iter_value != NULL);
125 if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value)))
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)))
132 g_warning ("%s: Unable to convert from %s to %s",
134 g_type_name (G_VALUE_TYPE (value)),
135 g_type_name (G_VALUE_TYPE (iter_value)));
139 if (!g_value_transform (iter_value, &real_value))
141 g_warning ("%s: Unable to make conversion from %s to %s",
143 g_type_name (G_VALUE_TYPE (value)),
144 g_type_name (G_VALUE_TYPE (iter_value)));
145 g_value_unset (&real_value);
153 g_value_copy (&real_value, value);
154 g_value_unset (&real_value);
157 g_value_copy (iter_value, value);
161 clutter_list_model_iter_set_value (ClutterModelIter *iter,
165 ClutterListModelIter *iter_default;
168 GValue real_value = G_VALUE_INIT;
169 gboolean converted = FALSE;
171 iter_default = CLUTTER_LIST_MODEL_ITER (iter);
172 g_assert (iter_default->seq_iter != NULL);
174 values = g_sequence_get (iter_default->seq_iter);
175 iter_value = &values[column];
176 g_assert (iter_value != NULL);
178 if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value)))
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)))
185 g_warning ("%s: Unable to convert from %s to %s\n",
187 g_type_name (G_VALUE_TYPE (value)),
188 g_type_name (G_VALUE_TYPE (iter_value)));
192 if (!g_value_transform (value, &real_value))
194 g_warning ("%s: Unable to make conversion from %s to %s\n",
196 g_type_name (G_VALUE_TYPE (value)),
197 g_type_name (G_VALUE_TYPE (iter_value)));
198 g_value_unset (&real_value);
206 g_value_copy (&real_value, iter_value);
207 g_value_unset (&real_value);
210 g_value_copy (value, iter_value);
214 clutter_list_model_iter_is_first (ClutterModelIter *iter)
216 ClutterListModelIter *iter_default;
218 ClutterModelIter *temp_iter;
220 GSequenceIter *begin, *end;
222 iter_default = CLUTTER_LIST_MODEL_ITER (iter);
223 g_assert (iter_default->seq_iter != NULL);
225 model = clutter_model_iter_get_model (iter);
227 sequence = CLUTTER_LIST_MODEL (model)->priv->sequence;
229 begin = g_sequence_get_begin_iter (sequence);
230 end = iter_default->seq_iter;
232 temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
234 while (!g_sequence_iter_is_begin (begin))
236 CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = begin;
238 if (clutter_model_filter_iter (model, temp_iter))
244 begin = g_sequence_iter_next (begin);
247 /* This is because the 'begin_iter' is always *before* the last valid
248 * iter, otherwise we'd have endless loops
250 end = g_sequence_iter_prev (end);
252 return iter_default->seq_iter == end;
256 clutter_list_model_iter_is_last (ClutterModelIter *iter)
258 ClutterListModelIter *iter_default;
259 ClutterModelIter *temp_iter;
262 GSequenceIter *begin, *end;
264 iter_default = CLUTTER_LIST_MODEL_ITER (iter);
265 g_assert (iter_default->seq_iter != NULL);
267 if (g_sequence_iter_is_end (iter_default->seq_iter))
270 model = clutter_model_iter_get_model (iter);
272 sequence = CLUTTER_LIST_MODEL (model)->priv->sequence;
274 begin = g_sequence_get_end_iter (sequence);
275 begin = g_sequence_iter_prev (begin);
276 end = iter_default->seq_iter;
278 temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
280 while (!g_sequence_iter_is_begin (begin))
282 CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = begin;
284 if (clutter_model_filter_iter (model, temp_iter))
290 begin = g_sequence_iter_prev (begin);
293 /* This is because the 'end_iter' is always *after* the last valid iter.
294 * Otherwise we'd have endless loops
296 end = g_sequence_iter_next (end);
298 return iter_default->seq_iter == end;
301 static ClutterModelIter *
302 clutter_list_model_iter_next (ClutterModelIter *iter)
304 ClutterListModelIter *iter_default;
305 ClutterModelIter *temp_iter;
306 ClutterModel *model = NULL;
307 GSequenceIter *filter_next;
310 iter_default = CLUTTER_LIST_MODEL_ITER (iter);
311 g_assert (iter_default->seq_iter != NULL);
313 model = clutter_model_iter_get_model (iter);
314 row = clutter_model_iter_get_row (iter);
316 filter_next = g_sequence_iter_next (iter_default->seq_iter);
317 g_assert (filter_next != NULL);
319 temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
321 while (!g_sequence_iter_is_end (filter_next))
323 CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = filter_next;
325 if (clutter_model_filter_iter (model, temp_iter))
331 filter_next = g_sequence_iter_next (filter_next);
334 if (g_sequence_iter_is_end (filter_next))
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;
341 return CLUTTER_MODEL_ITER (iter_default);
344 static ClutterModelIter *
345 clutter_list_model_iter_prev (ClutterModelIter *iter)
347 ClutterListModelIter *iter_default;
348 ClutterModelIter *temp_iter;
350 GSequenceIter *filter_prev;
353 iter_default = CLUTTER_LIST_MODEL_ITER (iter);
354 g_assert (iter_default->seq_iter != NULL);
356 model = clutter_model_iter_get_model (iter);
357 row = clutter_model_iter_get_row (iter);
359 filter_prev = g_sequence_iter_prev (iter_default->seq_iter);
360 g_assert (filter_prev != NULL);
362 temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
364 while (!g_sequence_iter_is_begin (filter_prev))
366 CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = filter_prev;
368 if (clutter_model_filter_iter (model, temp_iter))
374 filter_prev = g_sequence_iter_prev (filter_prev);
377 if (g_sequence_iter_is_begin (filter_prev))
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;
384 return CLUTTER_MODEL_ITER (iter_default);
387 static ClutterModelIter *
388 clutter_list_model_iter_copy (ClutterModelIter *iter)
390 ClutterListModelIter *iter_default;
391 ClutterListModelIter *iter_copy;
395 iter_default = CLUTTER_LIST_MODEL_ITER (iter);
397 model = clutter_model_iter_get_model (iter);
398 row = clutter_model_iter_get_row (iter) - 1;
400 iter_copy = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
405 /* this is safe, because the seq_iter pointer on the passed
406 * iterator will be always be overwritten in ::next or ::prev
408 iter_copy->seq_iter = iter_default->seq_iter;
410 return CLUTTER_MODEL_ITER (iter_copy);
414 clutter_list_model_iter_class_init (ClutterListModelIterClass *klass)
416 ClutterModelIterClass *iter_class = CLUTTER_MODEL_ITER_CLASS (klass);
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;
428 clutter_list_model_iter_init (ClutterListModelIter *iter)
430 iter->seq_iter = NULL;
437 G_DEFINE_TYPE (ClutterListModel, clutter_list_model, CLUTTER_TYPE_MODEL);
439 static ClutterModelIter *
440 clutter_list_model_get_iter_at_row (ClutterModel *model,
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;
450 if (row >= seq_length)
453 retval = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
458 /* short-circuit in case we don't have a filter in place */
459 if (!clutter_model_get_filter_set (model))
461 retval->seq_iter = g_sequence_get_iter_at_pos (sequence, row);
463 return CLUTTER_MODEL_ITER (retval);
466 filter_next = g_sequence_get_begin_iter (sequence);
467 g_assert (filter_next != NULL);
469 while (!g_sequence_iter_is_end (filter_next))
471 retval->seq_iter = filter_next;
473 if (clutter_model_filter_iter (model, CLUTTER_MODEL_ITER (retval)))
475 /* We've found a row that is valid under the filter */
481 filter_next = g_sequence_iter_next (filter_next);
486 g_object_unref (retval);
489 return CLUTTER_MODEL_ITER (retval);
492 static ClutterModelIter *
493 clutter_list_model_insert_row (ClutterModel *model,
496 ClutterListModel *model_default = CLUTTER_LIST_MODEL (model);
497 GSequence *sequence = model_default->priv->sequence;
498 ClutterListModelIter *retval;
499 guint n_columns, i, pos;
501 GSequenceIter *seq_iter;
503 n_columns = clutter_model_get_n_columns (model);
504 values = g_new0 (GValue, n_columns);
506 for (i = 0; i < n_columns; i++)
507 g_value_init (&values[i], clutter_model_get_column_type (model, i));
511 seq_iter = g_sequence_append (sequence, values);
512 pos = g_sequence_get_length (sequence) - 1;
514 else if (index_ == 0)
516 seq_iter = g_sequence_prepend (sequence, values);
521 seq_iter = g_sequence_get_iter_at_pos (sequence, index_);
522 seq_iter = g_sequence_insert_before (seq_iter, values);
526 retval = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
530 retval->seq_iter = seq_iter;
532 return CLUTTER_MODEL_ITER (retval);
536 clutter_list_model_remove_row (ClutterModel *model,
539 ClutterListModel *model_default = CLUTTER_LIST_MODEL (model);
540 GSequence *sequence = model_default->priv->sequence;
541 GSequenceIter *seq_iter;
544 seq_iter = g_sequence_get_begin_iter (sequence);
545 while (!g_sequence_iter_is_end (seq_iter))
547 if (clutter_model_filter_row (model, pos))
551 ClutterModelIter *iter;
553 iter = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
557 CLUTTER_LIST_MODEL_ITER (iter)->seq_iter = seq_iter;
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
566 g_signal_emit_by_name (model, "row-removed", iter);
568 g_object_unref (iter);
575 seq_iter = g_sequence_iter_next (seq_iter);
583 ClutterModelSortFunc func;
588 sort_model_default (gconstpointer a,
592 const GValue *row_a = a;
593 const GValue *row_b = b;
594 SortClosure *clos = data;
596 return clos->func (clos->model,
597 &row_a[clos->column],
598 &row_b[clos->column],
603 clutter_list_model_resort (ClutterModel *model,
604 ClutterModelSortFunc func,
607 SortClosure sort_closure = { NULL, 0, NULL, NULL };
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;
614 g_sequence_sort (CLUTTER_LIST_MODEL (model)->priv->sequence,
620 clutter_list_model_get_n_rows (ClutterModel *model)
622 ClutterListModel *list_model = CLUTTER_LIST_MODEL (model);
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);
628 return CLUTTER_MODEL_CLASS (clutter_list_model_parent_class)->get_n_rows (model);
632 clutter_list_model_row_removed (ClutterModel *model,
633 ClutterModelIter *iter)
635 ClutterListModelIter *iter_default;
639 n_columns = clutter_model_get_n_columns (model);
641 iter_default = CLUTTER_LIST_MODEL_ITER (iter);
643 values = g_sequence_get (iter_default->seq_iter);
645 for (i = 0; i < n_columns; i++)
646 g_value_unset (&values[i]);
650 g_sequence_remove (iter_default->seq_iter);
651 iter_default->seq_iter = NULL;
655 clutter_list_model_finalize (GObject *gobject)
657 ClutterListModel *model = CLUTTER_LIST_MODEL (gobject);
658 GSequence *sequence = model->priv->sequence;
662 n_columns = clutter_model_get_n_columns (CLUTTER_MODEL (gobject));
664 iter = g_sequence_get_begin_iter (sequence);
665 while (!g_sequence_iter_is_end (iter))
667 GValue *values = g_sequence_get (iter);
669 for (i = 0; i < n_columns; i++)
670 g_value_unset (&values[i]);
674 iter = g_sequence_iter_next (iter);
676 g_sequence_free (sequence);
678 G_OBJECT_CLASS (clutter_list_model_parent_class)->finalize (gobject);
682 clutter_list_model_dispose (GObject *gobject)
684 ClutterListModel *model = CLUTTER_LIST_MODEL (gobject);
686 if (model->priv->temp_iter)
688 g_object_unref (model->priv->temp_iter);
689 model->priv->temp_iter = NULL;
692 G_OBJECT_CLASS (clutter_list_model_parent_class)->dispose (gobject);
696 clutter_list_model_class_init (ClutterListModelClass *klass)
698 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
699 ClutterModelClass *model_class = CLUTTER_MODEL_CLASS (klass);
701 g_type_class_add_private (klass, sizeof (ClutterListModelPrivate));
703 gobject_class->finalize = clutter_list_model_finalize;
704 gobject_class->dispose = clutter_list_model_dispose;
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;
712 model_class->row_removed = clutter_list_model_row_removed;
716 clutter_list_model_init (ClutterListModel *model)
718 model->priv = CLUTTER_LIST_MODEL_GET_PRIVATE (model);
720 model->priv->sequence = g_sequence_new (NULL);
721 model->priv->temp_iter = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
728 * clutter_list_model_new:
729 * @n_columns: number of columns in the model
730 * @...: @n_columns number of #GType and string pairs
732 * Creates a new default model with @n_columns columns with the types
733 * and names passed in.
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>
744 * will create a new #ClutterModel with three columns of type int,
745 * string and #GdkPixbuf respectively.
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
751 * Return value: a new #ClutterListModel
756 clutter_list_model_new (guint n_columns,
763 g_return_val_if_fail (n_columns > 0, NULL);
765 model = g_object_new (CLUTTER_TYPE_LIST_MODEL, NULL);
766 _clutter_model_set_n_columns (model, n_columns, TRUE, TRUE);
768 va_start (args, n_columns);
770 for (i = 0; i < n_columns; i++)
772 GType type = va_arg (args, GType);
773 const gchar *name = va_arg (args, gchar*);
775 if (!_clutter_model_check_type (type))
777 g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
778 g_object_unref (model);
782 _clutter_model_set_column_type (model, i, type);
783 _clutter_model_set_column_name (model, i, name);
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
797 * Non-vararg version of clutter_list_model_new(). This function is
798 * useful for language bindings.
800 * Return value: (transfer full): a new default #ClutterModel
805 clutter_list_model_newv (guint n_columns,
807 const gchar * const names[])
812 g_return_val_if_fail (n_columns > 0, NULL);
814 model = g_object_new (CLUTTER_TYPE_LIST_MODEL, NULL);
815 _clutter_model_set_n_columns (model, n_columns, TRUE, TRUE);
817 for (i = 0; i < n_columns; i++)
819 if (!_clutter_model_check_type (types[i]))
821 g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
822 g_object_unref (model);
826 _clutter_model_set_column_type (model, i, types[i]);
827 _clutter_model_set_column_name (model, i, names[i]);