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
39
* Deprecated: 1.24: Use a #GListStore instance containing a custom
40
* object type with properties for each column instead.
44
#include "clutter-build-config.h"
50
#include <glib-object.h>
52
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
53
#include "clutter-list-model.h"
55
#include "clutter-model.h"
56
#include "clutter-model-private.h"
57
#include "clutter-private.h"
58
#include "clutter-debug.h"
60
#define CLUTTER_TYPE_LIST_MODEL_ITER \
61
(clutter_list_model_iter_get_type())
62
#define CLUTTER_LIST_MODEL_ITER(obj) \
63
(G_TYPE_CHECK_INSTANCE_CAST((obj), \
64
CLUTTER_TYPE_LIST_MODEL_ITER, \
65
ClutterListModelIter))
66
#define CLUTTER_IS_LIST_MODEL_ITER(obj) \
67
(G_TYPE_CHECK_INSTANCE_TYPE((obj), \
68
CLUTTER_TYPE_LIST_MODEL_ITER))
69
#define CLUTTER_LIST_MODEL_ITER_CLASS(klass) \
70
(G_TYPE_CHECK_CLASS_CAST ((klass), \
71
CLUTTER_TYPE_LIST_MODEL_ITER, \
72
ClutterListModelIterClass))
73
#define CLUTTER_IS_LIST_MODEL_ITER_CLASS(klass) \
74
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
75
CLUTTER_TYPE_LIST_MODEL_ITER))
76
#define CLUTTER_LIST_MODEL_ITER_GET_CLASS(obj) \
77
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
78
CLUTTER_TYPE_LIST_MODEL_ITER, \
79
ClutterListModelIterClass))
81
typedef struct _ClutterListModelIter ClutterListModelIter;
82
typedef struct _ClutterModelIterClass ClutterListModelIterClass;
84
struct _ClutterListModelPrivate
88
ClutterModelIter *temp_iter;
91
struct _ClutterListModelIter
93
ClutterModelIter parent_instance;
95
GSequenceIter *seq_iter;
100
GType clutter_list_model_iter_get_type (void);
106
G_DEFINE_TYPE (ClutterListModelIter,
107
clutter_list_model_iter,
108
CLUTTER_TYPE_MODEL_ITER)
111
clutter_list_model_iter_get_value (ClutterModelIter *iter,
115
ClutterListModelIter *iter_default;
118
GValue real_value = G_VALUE_INIT;
119
gboolean converted = FALSE;
121
iter_default = CLUTTER_LIST_MODEL_ITER (iter);
122
g_assert (iter_default->seq_iter != NULL);
124
values = g_sequence_get (iter_default->seq_iter);
125
iter_value = &values[column];
126
g_assert (iter_value != NULL);
128
if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value)))
130
if (!g_value_type_compatible (G_VALUE_TYPE (value),
131
G_VALUE_TYPE (iter_value)) &&
132
!g_value_type_compatible (G_VALUE_TYPE (iter_value),
133
G_VALUE_TYPE (value)))
135
g_warning ("%s: Unable to convert from %s to %s",
137
g_type_name (G_VALUE_TYPE (value)),
138
g_type_name (G_VALUE_TYPE (iter_value)));
142
if (!g_value_transform (iter_value, &real_value))
144
g_warning ("%s: Unable to make conversion from %s to %s",
146
g_type_name (G_VALUE_TYPE (value)),
147
g_type_name (G_VALUE_TYPE (iter_value)));
148
g_value_unset (&real_value);
156
g_value_copy (&real_value, value);
157
g_value_unset (&real_value);
160
g_value_copy (iter_value, value);
164
clutter_list_model_iter_set_value (ClutterModelIter *iter,
168
ClutterListModelIter *iter_default;
171
GValue real_value = G_VALUE_INIT;
172
gboolean converted = FALSE;
174
iter_default = CLUTTER_LIST_MODEL_ITER (iter);
175
g_assert (iter_default->seq_iter != NULL);
177
values = g_sequence_get (iter_default->seq_iter);
178
iter_value = &values[column];
179
g_assert (iter_value != NULL);
181
if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (iter_value)))
183
if (!g_value_type_compatible (G_VALUE_TYPE (value),
184
G_VALUE_TYPE (iter_value)) &&
185
!g_value_type_compatible (G_VALUE_TYPE (iter_value),
186
G_VALUE_TYPE (value)))
188
g_warning ("%s: Unable to convert from %s to %s\n",
190
g_type_name (G_VALUE_TYPE (value)),
191
g_type_name (G_VALUE_TYPE (iter_value)));
195
if (!g_value_transform (value, &real_value))
197
g_warning ("%s: Unable to make conversion from %s to %s\n",
199
g_type_name (G_VALUE_TYPE (value)),
200
g_type_name (G_VALUE_TYPE (iter_value)));
201
g_value_unset (&real_value);
209
g_value_copy (&real_value, iter_value);
210
g_value_unset (&real_value);
213
g_value_copy (value, iter_value);
217
clutter_list_model_iter_is_first (ClutterModelIter *iter)
219
ClutterListModelIter *iter_default;
221
ClutterModelIter *temp_iter;
223
GSequenceIter *begin, *end;
225
iter_default = CLUTTER_LIST_MODEL_ITER (iter);
226
g_assert (iter_default->seq_iter != NULL);
228
model = clutter_model_iter_get_model (iter);
230
sequence = CLUTTER_LIST_MODEL (model)->priv->sequence;
232
begin = g_sequence_get_begin_iter (sequence);
233
end = iter_default->seq_iter;
235
temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
237
while (!g_sequence_iter_is_begin (begin))
239
CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = begin;
241
if (clutter_model_filter_iter (model, temp_iter))
247
begin = g_sequence_iter_next (begin);
250
/* This is because the 'begin_iter' is always *before* the last valid
251
* iter, otherwise we'd have endless loops
253
end = g_sequence_iter_prev (end);
255
return iter_default->seq_iter == end;
259
clutter_list_model_iter_is_last (ClutterModelIter *iter)
261
ClutterListModelIter *iter_default;
262
ClutterModelIter *temp_iter;
265
GSequenceIter *begin, *end;
267
iter_default = CLUTTER_LIST_MODEL_ITER (iter);
268
g_assert (iter_default->seq_iter != NULL);
270
if (g_sequence_iter_is_end (iter_default->seq_iter))
273
model = clutter_model_iter_get_model (iter);
275
sequence = CLUTTER_LIST_MODEL (model)->priv->sequence;
277
begin = g_sequence_get_end_iter (sequence);
278
begin = g_sequence_iter_prev (begin);
279
end = iter_default->seq_iter;
281
temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
283
while (!g_sequence_iter_is_begin (begin))
285
CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = begin;
287
if (clutter_model_filter_iter (model, temp_iter))
293
begin = g_sequence_iter_prev (begin);
296
/* This is because the 'end_iter' is always *after* the last valid iter.
297
* Otherwise we'd have endless loops
299
end = g_sequence_iter_next (end);
301
return iter_default->seq_iter == end;
304
static ClutterModelIter *
305
clutter_list_model_iter_next (ClutterModelIter *iter)
307
ClutterListModelIter *iter_default;
308
ClutterModelIter *temp_iter;
309
ClutterModel *model = NULL;
310
GSequenceIter *filter_next;
313
iter_default = CLUTTER_LIST_MODEL_ITER (iter);
314
g_assert (iter_default->seq_iter != NULL);
316
model = clutter_model_iter_get_model (iter);
317
row = clutter_model_iter_get_row (iter);
319
filter_next = g_sequence_iter_next (iter_default->seq_iter);
320
g_assert (filter_next != NULL);
322
temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
324
while (!g_sequence_iter_is_end (filter_next))
326
CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = filter_next;
328
if (clutter_model_filter_iter (model, temp_iter))
334
filter_next = g_sequence_iter_next (filter_next);
337
if (g_sequence_iter_is_end (filter_next))
340
/* update the iterator and return it */
341
_clutter_model_iter_set_row (CLUTTER_MODEL_ITER (iter_default), row);
342
iter_default->seq_iter = filter_next;
344
return CLUTTER_MODEL_ITER (iter_default);
347
static ClutterModelIter *
348
clutter_list_model_iter_prev (ClutterModelIter *iter)
350
ClutterListModelIter *iter_default;
351
ClutterModelIter *temp_iter;
353
GSequenceIter *filter_prev;
356
iter_default = CLUTTER_LIST_MODEL_ITER (iter);
357
g_assert (iter_default->seq_iter != NULL);
359
model = clutter_model_iter_get_model (iter);
360
row = clutter_model_iter_get_row (iter);
362
filter_prev = g_sequence_iter_prev (iter_default->seq_iter);
363
g_assert (filter_prev != NULL);
365
temp_iter = CLUTTER_LIST_MODEL (model)->priv->temp_iter;
367
while (!g_sequence_iter_is_begin (filter_prev))
369
CLUTTER_LIST_MODEL_ITER (temp_iter)->seq_iter = filter_prev;
371
if (clutter_model_filter_iter (model, temp_iter))
377
filter_prev = g_sequence_iter_prev (filter_prev);
380
if (g_sequence_iter_is_begin (filter_prev))
383
/* update the iterator and return it */
384
_clutter_model_iter_set_row (CLUTTER_MODEL_ITER (iter_default), row);
385
iter_default->seq_iter = filter_prev;
387
return CLUTTER_MODEL_ITER (iter_default);
390
static ClutterModelIter *
391
clutter_list_model_iter_copy (ClutterModelIter *iter)
393
ClutterListModelIter *iter_default;
394
ClutterListModelIter *iter_copy;
398
iter_default = CLUTTER_LIST_MODEL_ITER (iter);
400
model = clutter_model_iter_get_model (iter);
401
row = clutter_model_iter_get_row (iter) - 1;
403
iter_copy = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
408
/* this is safe, because the seq_iter pointer on the passed
409
* iterator will be always be overwritten in ::next or ::prev
411
iter_copy->seq_iter = iter_default->seq_iter;
413
return CLUTTER_MODEL_ITER (iter_copy);
417
clutter_list_model_iter_class_init (ClutterListModelIterClass *klass)
419
ClutterModelIterClass *iter_class = CLUTTER_MODEL_ITER_CLASS (klass);
421
iter_class->get_value = clutter_list_model_iter_get_value;
422
iter_class->set_value = clutter_list_model_iter_set_value;
423
iter_class->is_first = clutter_list_model_iter_is_first;
424
iter_class->is_last = clutter_list_model_iter_is_last;
425
iter_class->next = clutter_list_model_iter_next;
426
iter_class->prev = clutter_list_model_iter_prev;
427
iter_class->copy = clutter_list_model_iter_copy;
431
clutter_list_model_iter_init (ClutterListModelIter *iter)
433
iter->seq_iter = NULL;
440
G_DEFINE_TYPE_WITH_PRIVATE (ClutterListModel, clutter_list_model, CLUTTER_TYPE_MODEL)
442
static ClutterModelIter *
443
clutter_list_model_get_iter_at_row (ClutterModel *model,
446
ClutterListModel *model_default = CLUTTER_LIST_MODEL (model);
447
GSequence *sequence = model_default->priv->sequence;
448
GSequenceIter *filter_next;
449
gint seq_length = g_sequence_get_length (sequence);
450
ClutterListModelIter *retval;
453
if (row >= seq_length)
456
retval = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
461
/* short-circuit in case we don't have a filter in place */
462
if (!clutter_model_get_filter_set (model))
464
retval->seq_iter = g_sequence_get_iter_at_pos (sequence, row);
466
return CLUTTER_MODEL_ITER (retval);
469
filter_next = g_sequence_get_begin_iter (sequence);
470
g_assert (filter_next != NULL);
472
while (!g_sequence_iter_is_end (filter_next))
474
retval->seq_iter = filter_next;
476
if (clutter_model_filter_iter (model, CLUTTER_MODEL_ITER (retval)))
478
/* We've found a row that is valid under the filter */
484
filter_next = g_sequence_iter_next (filter_next);
489
g_object_unref (retval);
492
return CLUTTER_MODEL_ITER (retval);
495
static ClutterModelIter *
496
clutter_list_model_insert_row (ClutterModel *model,
499
ClutterListModel *model_default = CLUTTER_LIST_MODEL (model);
500
GSequence *sequence = model_default->priv->sequence;
501
ClutterListModelIter *retval;
502
guint n_columns, i, pos;
504
GSequenceIter *seq_iter;
506
n_columns = clutter_model_get_n_columns (model);
507
values = g_new0 (GValue, n_columns);
509
for (i = 0; i < n_columns; i++)
510
g_value_init (&values[i], clutter_model_get_column_type (model, i));
514
seq_iter = g_sequence_append (sequence, values);
515
pos = g_sequence_get_length (sequence) - 1;
517
else if (index_ == 0)
519
seq_iter = g_sequence_prepend (sequence, values);
524
seq_iter = g_sequence_get_iter_at_pos (sequence, index_);
525
seq_iter = g_sequence_insert_before (seq_iter, values);
529
retval = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
533
retval->seq_iter = seq_iter;
535
return CLUTTER_MODEL_ITER (retval);
539
clutter_list_model_remove_row (ClutterModel *model,
542
ClutterListModel *model_default = CLUTTER_LIST_MODEL (model);
543
GSequence *sequence = model_default->priv->sequence;
544
GSequenceIter *seq_iter;
547
seq_iter = g_sequence_get_begin_iter (sequence);
548
while (!g_sequence_iter_is_end (seq_iter))
550
if (clutter_model_filter_row (model, pos))
554
ClutterModelIter *iter;
556
iter = g_object_new (CLUTTER_TYPE_LIST_MODEL_ITER,
560
CLUTTER_LIST_MODEL_ITER (iter)->seq_iter = seq_iter;
562
/* the actual row is removed from the sequence inside
563
* the ::row-removed signal class handler, so that every
564
* handler connected to ::row-removed will still get
565
* a valid iterator, and every signal connected to
566
* ::row-removed with the AFTER flag will get an updated
569
g_signal_emit_by_name (model, "row-removed", iter);
571
g_object_unref (iter);
578
seq_iter = g_sequence_iter_next (seq_iter);
586
ClutterModelSortFunc func;
591
sort_model_default (gconstpointer a,
595
const GValue *row_a = a;
596
const GValue *row_b = b;
597
SortClosure *clos = data;
599
return clos->func (clos->model,
600
&row_a[clos->column],
601
&row_b[clos->column],
606
clutter_list_model_resort (ClutterModel *model,
607
ClutterModelSortFunc func,
610
SortClosure sort_closure = { NULL, 0, NULL, NULL };
612
sort_closure.model = model;
613
sort_closure.column = clutter_model_get_sorting_column (model);
614
sort_closure.func = func;
615
sort_closure.data = data;
617
g_sequence_sort (CLUTTER_LIST_MODEL (model)->priv->sequence,
623
clutter_list_model_get_n_rows (ClutterModel *model)
625
ClutterListModel *list_model = CLUTTER_LIST_MODEL (model);
627
/* short-circuit in case we don't have a filter in place */
628
if (!clutter_model_get_filter_set (model))
629
return g_sequence_get_length (list_model->priv->sequence);
631
return CLUTTER_MODEL_CLASS (clutter_list_model_parent_class)->get_n_rows (model);
635
clutter_list_model_row_removed (ClutterModel *model,
636
ClutterModelIter *iter)
638
ClutterListModelIter *iter_default;
642
n_columns = clutter_model_get_n_columns (model);
644
iter_default = CLUTTER_LIST_MODEL_ITER (iter);
646
values = g_sequence_get (iter_default->seq_iter);
648
for (i = 0; i < n_columns; i++)
649
g_value_unset (&values[i]);
653
g_sequence_remove (iter_default->seq_iter);
654
iter_default->seq_iter = NULL;
658
clutter_list_model_finalize (GObject *gobject)
660
ClutterListModel *model = CLUTTER_LIST_MODEL (gobject);
661
GSequence *sequence = model->priv->sequence;
665
n_columns = clutter_model_get_n_columns (CLUTTER_MODEL (gobject));
667
iter = g_sequence_get_begin_iter (sequence);
668
while (!g_sequence_iter_is_end (iter))
670
GValue *values = g_sequence_get (iter);
672
for (i = 0; i < n_columns; i++)
673
g_value_unset (&values[i]);
677
iter = g_sequence_iter_next (iter);
679
g_sequence_free (sequence);
681
G_OBJECT_CLASS (clutter_list_model_parent_class)->finalize (gobject);
685
clutter_list_model_dispose (GObject *gobject)
687
ClutterListModel *model = CLUTTER_LIST_MODEL (gobject);
689
if (model->priv->temp_iter)
691
g_object_unref (model->priv->temp_iter);
692
model->priv->temp_iter = NULL;
695
G_OBJECT_CLASS (clutter_list_model_parent_class)->dispose (gobject);
699
clutter_list_model_class_init (ClutterListModelClass *klass)
701
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
702
ClutterModelClass *model_class = CLUTTER_MODEL_CLASS (klass);
704
gobject_class->finalize = clutter_list_model_finalize;
705
gobject_class->dispose = clutter_list_model_dispose;
707
model_class->get_iter_at_row = clutter_list_model_get_iter_at_row;
708
model_class->insert_row = clutter_list_model_insert_row;
709
model_class->remove_row = clutter_list_model_remove_row;
710
model_class->resort = clutter_list_model_resort;
711
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_instance_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
755
* Deprecated: 1.24: Use #GListStore instead
758
clutter_list_model_new (guint n_columns,
765
g_return_val_if_fail (n_columns > 0, NULL);
767
model = g_object_new (CLUTTER_TYPE_LIST_MODEL, NULL);
768
_clutter_model_set_n_columns (model, n_columns, TRUE, TRUE);
770
va_start (args, n_columns);
772
for (i = 0; i < n_columns; i++)
774
GType type = va_arg (args, GType);
775
const gchar *name = va_arg (args, gchar*);
777
if (!_clutter_model_check_type (type))
779
g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
780
g_object_unref (model);
785
_clutter_model_set_column_type (model, i, type);
786
_clutter_model_set_column_name (model, i, name);
795
* clutter_list_model_newv:
796
* @n_columns: number of columns in the model
797
* @types: (array length=n_columns): an array of #GType types for the columns, from first to last
798
* @names: (array length=n_columns): an array of names for the columns, from first to last
800
* Non-vararg version of clutter_list_model_new(). This function is
801
* useful for language bindings.
803
* Return value: (transfer full): a new default #ClutterModel
807
* Deprecated: 1.24: Use #GListStore instead
810
clutter_list_model_newv (guint n_columns,
812
const gchar * const names[])
817
g_return_val_if_fail (n_columns > 0, NULL);
819
model = g_object_new (CLUTTER_TYPE_LIST_MODEL, NULL);
820
_clutter_model_set_n_columns (model, n_columns, TRUE, TRUE);
822
for (i = 0; i < n_columns; i++)
824
if (!_clutter_model_check_type (types[i]))
826
g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
827
g_object_unref (model);
831
_clutter_model_set_column_type (model, i, types[i]);
832
_clutter_model_set_column_name (model, i, names[i]);