4
* Copyright (C) 2010 Stefan Walter
5
* Copyright (C) 2011 Collabora Ltd.
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU Lesser General Public License as
9
* published by the Free Software Foundation; either version 2.1 of
10
* the License, or (at your option) any later version.
12
* This program is distributed in the hope that it will be useful, but
13
* WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22
* Author: Stef Walter <stefw@collabora.co.uk>
27
#include "gcr-collection-model.h"
28
#include "gcr-enum-types.h"
36
* SECTION:gcr-collection-model
37
* @title: GcrCollectionModel
38
* @short_description: A GtkTreeModel that represents a collection
40
* This is an implementation of #GtkTreeModel which represents the objects in
41
* the a #GcrCollection. As objects are added or removed from the collection,
42
* rows are added and removed from this model.
44
* The row values come from the properties of the objects in the collection. Use
45
* gcr_collection_model_new() to create a new collection model. To have more
46
* control over the values use a set of #GcrColumn structures to define the
47
* columns. This can be done with gcr_collection_model_new_full() or
48
* gcr_collection_model_set_columns().
50
* Each row can have a selected state, which is represented by a boolean column.
51
* The selected state can be toggled with gcr_collection_model_toggle_selected()
52
* or set with gcr_collection_model_set_selected_objects() and retrieved with
53
* gcr_collection_model_get_selected_objects().
55
* To determine which object a row represents and vice versa, use the
56
* gcr_collection_model_iter_for_object() or gcr_collection_model_object_for_iter()
63
* A #GtkTreeModel which contains a row for each object in a #GcrCollection.
67
* GcrCollectionModelClass:
68
* @parent_class: The parent class
70
* The class for #GcrCollectionModel.
74
* GcrCollectionModelMode:
75
* @GCR_COLLECTION_MODEL_LIST: only objects in the top collection, no child objects
76
* @GCR_COLLECTION_MODEL_TREE: show objects in the collection, and child objects in a tree form
78
* If set GcrCollectionModel is created with a mode of %GCR_COLLECTION_MODEL_TREE,
79
* then any included objects that are themselves a #GcrCollection, will have all child
80
* objects include as child rows in a tree form.
83
#define COLLECTION_MODEL_STAMP 0xAABBCCDD
94
GSequenceIter *parent;
99
GtkTreeIterCompareFunc sort_func;
101
GDestroyNotify destroy_func;
102
} GcrCollectionSortClosure;
104
typedef struct _GcrCollectionColumn {
107
GtkTreeIterCompareFunc sort_func;
109
GDestroyNotify sort_destroy;
110
} GcrCollectionColumn;
112
struct _GcrCollectionModelPrivate {
113
GcrCollectionModelMode mode;
114
GcrCollection *collection;
115
GHashTable *selected;
116
GSequence *root_sequence;
117
GHashTable *object_to_seq;
119
const GcrColumn *columns;
122
/* Sort information */
124
GtkSortType sort_order_type;
125
GcrCollectionSortClosure *column_sort_closures;
126
GcrCollectionSortClosure default_sort_closure;
128
/* Sequence ordering information */
129
GCompareDataFunc order_current;
130
gpointer order_argument;
133
/* Forward declarations */
134
static void gcr_collection_model_tree_model_init (GtkTreeModelIface *iface);
135
static void gcr_collection_model_tree_sortable_init (GtkTreeSortableIface *iface);
137
G_DEFINE_TYPE_EXTENDED (GcrCollectionModel, gcr_collection_model, G_TYPE_OBJECT, 0,
138
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, gcr_collection_model_tree_model_init)
139
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, gcr_collection_model_tree_sortable_init)
142
typedef gint (*CompareValueFunc) (const GValue *va,
146
compare_int_value (const GValue *va,
149
gint a = g_value_get_int (va);
150
gint b = g_value_get_int (vb);
152
else if (a < b) return -1;
157
compare_uint_value (const GValue *va,
160
guint a = g_value_get_uint (va);
161
guint b = g_value_get_uint (vb);
163
else if (a < b) return -1;
168
compare_long_value (const GValue *va,
171
glong a = g_value_get_long (va);
172
glong b = g_value_get_long (vb);
174
else if (a < b) return -1;
179
compare_ulong_value (const GValue *va,
182
gulong a = g_value_get_ulong (va);
183
gulong b = g_value_get_ulong (vb);
185
else if (a < b) return -1;
190
compare_string_value (const GValue *va,
193
const gchar *a = g_value_get_string (va);
194
const gchar *b = g_value_get_string (vb);
206
case_a = g_utf8_casefold (a, -1);
207
case_b = g_utf8_casefold (b, -1);
208
ret = g_utf8_collate (case_a, case_b);
216
compare_date_value (const GValue *va,
219
GDate *a = g_value_get_boxed (va);
220
GDate *b = g_value_get_boxed (vb);
229
return g_date_compare (a, b);
232
static CompareValueFunc
233
lookup_compare_func (GType type)
237
return compare_int_value;
239
return compare_uint_value;
241
return compare_long_value;
243
return compare_ulong_value;
245
return compare_string_value;
248
if (type == G_TYPE_DATE)
249
return compare_date_value;
255
order_sequence_by_closure (gconstpointer a,
259
GcrCollectionModel *self = GCR_COLLECTION_MODEL (user_data);
260
GcrCollectionSortClosure *closure = self->pv->order_argument;
261
const GcrCollectionRow *row_a = a;
262
const GcrCollectionRow *row_b = b;
267
g_assert (closure->sort_func);
269
if (!gcr_collection_model_iter_for_object (self, row_a->object, &iter_a))
270
g_return_val_if_reached (0);
271
if (!gcr_collection_model_iter_for_object (self, row_b->object, &iter_b))
272
g_return_val_if_reached (0);
274
return (closure->sort_func) (GTK_TREE_MODEL (self),
275
&iter_a, &iter_b, closure->user_data);
279
order_sequence_by_closure_reverse (gconstpointer a,
283
return 0 - order_sequence_by_closure (a, b, user_data);
287
order_sequence_as_unsorted (gconstpointer a,
291
const GcrCollectionRow *row_a = a;
292
const GcrCollectionRow *row_b = b;
293
return GPOINTER_TO_INT (row_a->object) - GPOINTER_TO_INT (row_b->object);
297
order_sequence_as_unsorted_reverse (gconstpointer a,
301
const GcrCollectionRow *row_a = a;
302
const GcrCollectionRow *row_b = b;
303
return GPOINTER_TO_INT (row_b->object) - GPOINTER_TO_INT (row_a->object);
307
lookup_object_property (GObject *object,
308
const gchar *property_name,
311
if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name))
312
g_object_get_property (object, property_name, value);
314
/* Other types have sane defaults */
315
else if (G_VALUE_TYPE (value) == G_TYPE_STRING)
316
g_value_set_string (value, "");
320
order_sequence_by_property (gconstpointer a,
324
const GcrCollectionRow *row_a = a;
325
const GcrCollectionRow *row_b = b;
326
GcrCollectionModel *self = GCR_COLLECTION_MODEL (user_data);
327
const GcrColumn *column = self->pv->order_argument;
328
GValue value_a = { 0, };
329
GValue value_b = { 0, };
330
CompareValueFunc compare;
335
/* Sort according to property values */
336
column = &self->pv->columns[self->pv->sort_column_id];
337
g_value_init (&value_a, column->property_type);
338
lookup_object_property (row_a->object, column->property_name, &value_a);
339
g_value_init (&value_b, column->property_type);
340
lookup_object_property (row_b->object, column->property_name, &value_b);
342
compare = lookup_compare_func (column->property_type);
343
g_assert (compare != NULL);
345
ret = (compare) (&value_a, &value_b);
347
g_value_unset (&value_a);
348
g_value_unset (&value_b);
354
order_sequence_by_property_reverse (gconstpointer a,
358
return 0 - order_sequence_by_property (a, b, user_data);
362
selected_hash_table_new (void)
364
return g_hash_table_new (g_direct_hash, g_direct_equal);
368
sequence_iter_to_tree (GcrCollectionModel *self,
372
GcrCollectionRow *row;
374
g_return_val_if_fail (seq != NULL, FALSE);
376
if (g_sequence_iter_is_end (seq))
379
row = g_sequence_get (seq);
380
g_return_val_if_fail (row != NULL && G_IS_OBJECT (row->object), FALSE);
382
memset (iter, 0, sizeof (*iter));
383
iter->stamp = COLLECTION_MODEL_STAMP;
384
iter->user_data = row->object;
385
iter->user_data2 = seq;
389
static GSequenceIter *
390
sequence_iter_for_tree (GcrCollectionModel *self,
393
g_return_val_if_fail (iter != NULL, NULL);
394
g_return_val_if_fail (iter->stamp == COLLECTION_MODEL_STAMP, NULL);
395
return iter->user_data2;
399
sequence_iter_to_path (GcrCollectionModel *self,
402
GcrCollectionRow *row;
405
path = gtk_tree_path_new ();
407
gtk_tree_path_prepend_index (path, g_sequence_iter_get_position (seq));
408
row = g_sequence_get (seq);
415
child_sequence_for_tree (GcrCollectionModel *self,
418
GcrCollectionRow *row;
422
return self->pv->root_sequence;
424
seq = sequence_iter_for_tree (self, iter);
425
g_return_val_if_fail (seq != NULL, NULL);
426
row = g_sequence_get (seq);
427
return row->children;
432
on_object_notify (GObject *object, GParamSpec *spec, GcrCollectionModel *self)
436
gboolean found = FALSE;
439
g_return_if_fail (spec->name);
441
for (i = 0; i < self->pv->n_columns - 1; ++i) {
442
g_assert (self->pv->columns[i].property_name);
443
if (g_str_equal (self->pv->columns[i].property_name, spec->name)) {
449
/* Tell the tree view that this row changed */
451
if (!gcr_collection_model_iter_for_object (self, object, &iter))
452
g_return_if_reached ();
453
path = gtk_tree_model_get_path (GTK_TREE_MODEL (self), &iter);
454
g_return_if_fail (path);
455
gtk_tree_model_row_changed (GTK_TREE_MODEL (self), path, &iter);
456
gtk_tree_path_free (path);
461
on_object_gone (gpointer unused, GObject *was_object)
463
g_warning ("object contained in GcrCollection and included in GcrCollectionModel "
464
"was destroyed before it was removed from the collection");
467
static void on_collection_added (GcrCollection *collection,
471
static void on_collection_removed (GcrCollection *collection,
475
static void add_object_to_sequence (GcrCollectionModel *self,
477
GSequenceIter *parent,
481
static void remove_object_from_sequence (GcrCollectionModel *self,
488
add_children_to_sequence (GcrCollectionModel *self,
490
GSequenceIter *parent,
491
GcrCollection *collection,
498
for (l = children; l; l = g_list_next (l)) {
499
if (!exclude || g_hash_table_lookup (exclude, l->data) == NULL)
500
add_object_to_sequence (self, sequence, parent, l->data, emit);
503
/* Now listen in for any changes */
504
g_signal_connect_after (collection, "added", G_CALLBACK (on_collection_added), self);
505
g_signal_connect_after (collection, "removed", G_CALLBACK (on_collection_removed), self);
509
add_object_to_sequence (GcrCollectionModel *self,
511
GSequenceIter *parent,
515
GcrCollectionRow *row;
516
GcrCollection *collection;
522
g_assert (GCR_IS_COLLECTION_MODEL (self));
523
g_assert (G_IS_OBJECT (object));
524
g_assert (self->pv->order_current);
526
if (g_hash_table_lookup (self->pv->object_to_seq, object)) {
527
g_warning ("object was already added to the GcrCollectionModel. Perhaps "
528
"a loop exists in a tree structure?");
532
row = g_slice_new0 (GcrCollectionRow);
533
row->object = object;
534
row->parent = parent;
535
row->children = NULL;
537
seq = g_sequence_insert_sorted (sequence, row, self->pv->order_current, self);
538
g_hash_table_insert (self->pv->object_to_seq, object, seq);
539
g_object_weak_ref (G_OBJECT (object), (GWeakNotify)on_object_gone, self);
540
g_signal_connect (object, "notify", G_CALLBACK (on_object_notify), self);
543
if (!sequence_iter_to_tree (self, seq, &iter))
544
g_assert_not_reached ();
545
path = sequence_iter_to_path (self, seq);
546
g_assert (path != NULL);
547
gtk_tree_model_row_inserted (GTK_TREE_MODEL (self), path, &iter);
548
gtk_tree_path_free (path);
551
if (self->pv->mode == GCR_COLLECTION_MODEL_TREE &&
552
GCR_IS_COLLECTION (object)) {
553
row->children = g_sequence_new (NULL);
554
collection = GCR_COLLECTION (object);
555
children = gcr_collection_get_objects (collection);
556
add_children_to_sequence (self, row->children, seq,
557
collection, children, NULL, emit);
558
g_list_free (children);
563
on_collection_added (GcrCollection *collection,
567
GcrCollectionModel *self = GCR_COLLECTION_MODEL (user_data);
569
GSequenceIter *parent;
570
GcrCollectionRow *row;
572
if (collection == self->pv->collection) {
573
sequence = self->pv->root_sequence;
576
parent = g_hash_table_lookup (self->pv->object_to_seq, G_OBJECT (collection));
577
row = g_sequence_get (parent);
578
g_assert (row->children);
579
sequence = row->children;
582
add_object_to_sequence (self, sequence, parent, object, TRUE);
586
remove_children_from_sequence (GcrCollectionModel *self,
588
GcrCollection *collection,
592
GSequenceIter *seq, *next;
593
GcrCollectionRow *row;
595
g_signal_handlers_disconnect_by_func (collection, on_collection_added, self);
596
g_signal_handlers_disconnect_by_func (collection, on_collection_removed, self);
598
for (seq = g_sequence_get_begin_iter (sequence);
599
!g_sequence_iter_is_end (seq); seq = next) {
600
next = g_sequence_iter_next (seq);
601
row = g_sequence_get (seq);
602
if (!exclude || g_hash_table_lookup (exclude, row->object) == NULL)
603
remove_object_from_sequence (self, sequence, seq, row->object, emit);
608
remove_object_from_sequence (GcrCollectionModel *self,
614
GcrCollectionRow *row;
615
GtkTreePath *path = NULL;
618
path = sequence_iter_to_path (self, seq);
619
g_assert (path != NULL);
622
row = g_sequence_get (seq);
623
g_assert (row->object == object);
625
g_object_weak_unref (object, on_object_gone, self);
626
g_signal_handlers_disconnect_by_func (object, on_object_notify, self);
629
g_assert (self->pv->mode == GCR_COLLECTION_MODEL_TREE);
630
g_assert (GCR_IS_COLLECTION (object));
631
remove_children_from_sequence (self, row->children,
632
GCR_COLLECTION (object), NULL, emit);
633
g_assert (g_sequence_get_length (row->children) == 0);
634
g_sequence_free (row->children);
635
row->children = NULL;
638
if (self->pv->selected)
639
g_hash_table_remove (self->pv->selected, object);
640
if (!g_hash_table_remove (self->pv->object_to_seq, object))
641
g_assert_not_reached ();
643
g_sequence_remove (seq);
644
g_slice_free (GcrCollectionRow, row);
646
/* Fire signal for this removed row */
648
gtk_tree_model_row_deleted (GTK_TREE_MODEL (self), path);
649
gtk_tree_path_free (path);
655
on_collection_removed (GcrCollection *collection,
659
GcrCollectionModel *self = GCR_COLLECTION_MODEL (user_data);
663
seq = g_hash_table_lookup (self->pv->object_to_seq, object);
664
g_return_if_fail (seq != NULL);
666
sequence = g_sequence_iter_get_sequence (seq);
667
g_assert (sequence != NULL);
669
remove_object_from_sequence (self, sequence, seq, object, TRUE);
673
free_owned_columns (gpointer data)
678
/* Only the property column is in use */
679
for (columns = data; columns->property_name; ++columns)
680
g_free ((gchar*)columns->property_name);
684
static GtkTreeModelFlags
685
gcr_collection_model_real_get_flags (GtkTreeModel *model)
687
return GTK_TREE_MODEL_ITERS_PERSIST;
691
gcr_collection_model_real_get_n_columns (GtkTreeModel *model)
693
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
694
return self->pv->n_columns;
698
gcr_collection_model_real_get_column_type (GtkTreeModel *model,
701
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
702
g_return_val_if_fail (column_id >= 0 && column_id <= self->pv->n_columns, 0);
704
/* The last is the selected column */
705
if (column_id == self->pv->n_columns)
706
return G_TYPE_BOOLEAN;
708
return self->pv->columns[column_id].column_type;
712
gcr_collection_model_real_get_iter (GtkTreeModel *model,
716
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
720
GcrCollectionRow *row;
724
sequence = self->pv->root_sequence;
727
indices = gtk_tree_path_get_indices_with_depth (path, &count);
731
for (i = 0; i < count; i++) {
734
seq = g_sequence_get_iter_at_pos (sequence, indices[i]);
735
if (g_sequence_iter_is_end (seq))
737
row = g_sequence_get (seq);
738
sequence = row->children;
741
return sequence_iter_to_tree (self, seq, iter);
745
gcr_collection_model_real_get_path (GtkTreeModel *model,
748
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
752
return gtk_tree_path_new ();
754
seq = sequence_iter_for_tree (self, iter);
755
g_return_val_if_fail (seq != NULL, NULL);
756
return sequence_iter_to_path (self, seq);
760
gcr_collection_model_real_get_value (GtkTreeModel *model,
765
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
768
const GcrColumn *column;
771
object = gcr_collection_model_object_for_iter (self, iter);
772
g_return_if_fail (G_IS_OBJECT (object));
773
g_return_if_fail (column_id >= 0 && column_id < self->pv->n_columns);
775
/* The selected column? Last one */
776
if (column_id == self->pv->n_columns - 1) {
777
g_value_init (value, G_TYPE_BOOLEAN);
778
g_value_set_boolean (value, gcr_collection_model_is_selected (self, iter));
782
/* Figure out which property */
783
column = &self->pv->columns[column_id];
784
g_assert (column->property_name);
785
g_value_init (value, column->column_type);
787
/* Lookup the property on the object */
788
spec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), column->property_name);
790
/* A transformer is specified, or mismatched types */
791
if (column->transformer || column->column_type != column->property_type) {
792
memset (&original, 0, sizeof (original));
793
g_value_init (&original, column->property_type);
794
g_object_get_property (object, column->property_name, &original);
796
if (column->transformer) {
797
(column->transformer) (&original, value);
799
g_warning ("%s property of %s class was of type %s instead of type %s"
800
" and cannot be converted due to lack of transformer",
801
column->property_name, G_OBJECT_TYPE_NAME (object),
802
g_type_name (column->property_type),
803
g_type_name (column->column_type));
807
/* Simple, no transformation necessary */
809
g_object_get_property (object, column->property_name, value);
815
/* All the number types have sane defaults */
816
if (column->column_type == G_TYPE_STRING)
817
g_value_set_string (value, "");
822
gcr_collection_model_real_iter_next (GtkTreeModel *model,
825
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
826
GSequenceIter *seq = sequence_iter_for_tree (self, iter);
827
g_return_val_if_fail (seq != NULL, FALSE);
828
return sequence_iter_to_tree (self, g_sequence_iter_next (seq), iter);
832
gcr_collection_model_real_iter_children (GtkTreeModel *model,
836
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
837
GSequence *sequence = child_sequence_for_tree (self, parent);
838
return sequence && sequence_iter_to_tree (self, g_sequence_get_begin_iter (sequence), iter);
842
gcr_collection_model_real_iter_has_child (GtkTreeModel *model,
845
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
846
GSequence *sequence = child_sequence_for_tree (self, iter);
847
return sequence && !g_sequence_iter_is_end (g_sequence_get_begin_iter (sequence));
851
gcr_collection_model_real_iter_n_children (GtkTreeModel *model,
854
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
855
GSequence *sequence = child_sequence_for_tree (self, iter);
856
return sequence ? g_sequence_get_length (sequence) : 0;
860
gcr_collection_model_real_iter_nth_child (GtkTreeModel *model,
865
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
869
sequence = child_sequence_for_tree (self, parent);
870
if (sequence == NULL)
872
seq = g_sequence_get_iter_at_pos (sequence, n);
873
return sequence_iter_to_tree (self, seq, iter);
877
gcr_collection_model_real_iter_parent (GtkTreeModel *model,
881
GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
883
GcrCollectionRow *row;
885
seq = sequence_iter_for_tree (self, child);
886
g_return_val_if_fail (seq != NULL, FALSE);
887
row = g_sequence_get (seq);
888
if (row->parent == NULL)
890
return sequence_iter_to_tree (self, row->parent, iter);
894
gcr_collection_model_real_ref_node (GtkTreeModel *model,
901
gcr_collection_model_real_unref_node (GtkTreeModel *model,
908
gcr_collection_model_tree_model_init (GtkTreeModelIface *iface)
910
iface->get_flags = gcr_collection_model_real_get_flags;
911
iface->get_n_columns = gcr_collection_model_real_get_n_columns;
912
iface->get_column_type = gcr_collection_model_real_get_column_type;
913
iface->get_iter = gcr_collection_model_real_get_iter;
914
iface->get_path = gcr_collection_model_real_get_path;
915
iface->get_value = gcr_collection_model_real_get_value;
916
iface->iter_next = gcr_collection_model_real_iter_next;
917
iface->iter_children = gcr_collection_model_real_iter_children;
918
iface->iter_has_child = gcr_collection_model_real_iter_has_child;
919
iface->iter_n_children = gcr_collection_model_real_iter_n_children;
920
iface->iter_nth_child = gcr_collection_model_real_iter_nth_child;
921
iface->iter_parent = gcr_collection_model_real_iter_parent;
922
iface->ref_node = gcr_collection_model_real_ref_node;
923
iface->unref_node = gcr_collection_model_real_unref_node;
927
collection_resort_sequence (GcrCollectionModel *self,
928
GSequenceIter *parent,
932
GSequenceIter *seq, *next;
936
GcrCollectionRow *row;
940
/* Make note of how things stand, and at same time resort all kids */
941
previous = g_ptr_array_new ();
942
for (seq = g_sequence_get_begin_iter (sequence);
943
!g_sequence_iter_is_end (seq); seq = next) {
944
next = g_sequence_iter_next (seq);
945
row = g_sequence_get (seq);
947
collection_resort_sequence (self, seq, row->children);
948
g_ptr_array_add (previous, row->object);
951
if (previous->len == 0) {
952
g_ptr_array_free (previous, TRUE);
956
/* Actually perform the sort */
957
g_sequence_sort (sequence, self->pv->order_current, self);
959
/* Now go through and map out how things changed */
960
new_order = g_new0 (gint, previous->len);
961
for (i = 0; i < previous->len; i++) {
962
seq = g_hash_table_lookup (self->pv->object_to_seq, previous->pdata[i]);
963
g_assert (seq != NULL);
964
index = g_sequence_iter_get_position (seq);
965
g_assert (index >= 0 && index < previous->len);
966
new_order[index] = i;
969
g_ptr_array_free (previous, TRUE);
971
path = sequence_iter_to_path (self, parent);
972
if (parent == NULL) {
973
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (self), path, NULL, new_order);
975
if (!sequence_iter_to_tree (self, parent, &iter))
976
g_assert_not_reached ();
977
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (self), path, &iter, new_order);
979
gtk_tree_path_free (path);
984
gcr_collection_model_get_sort_column_id (GtkTreeSortable *sortable,
985
gint *sort_column_id,
988
GcrCollectionModel *self = GCR_COLLECTION_MODEL (sortable);
991
*order = self->pv->sort_order_type;
993
*sort_column_id = self->pv->sort_column_id;
994
return (self->pv->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID &&
995
self->pv->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID);
999
gcr_collection_model_set_sort_column_id (GtkTreeSortable *sortable,
1000
gint sort_column_id,
1003
GcrCollectionModel *self = GCR_COLLECTION_MODEL (sortable);
1004
GCompareDataFunc func;
1006
const GcrColumn *column;
1009
reverse = (order == GTK_SORT_DESCENDING);
1011
if (sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) {
1012
func = reverse ? order_sequence_as_unsorted_reverse : order_sequence_as_unsorted;
1015
} else if (sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) {
1016
func = reverse ? order_sequence_by_closure_reverse : order_sequence_by_closure;
1017
argument = &self->pv->default_sort_closure;
1019
} else if (sort_column_id >= 0 && sort_column_id < self->pv->n_columns) {
1020
if (self->pv->column_sort_closures[sort_column_id].sort_func) {
1021
func = reverse ? order_sequence_by_closure_reverse : order_sequence_by_closure;
1022
argument = &self->pv->column_sort_closures[sort_column_id];
1024
column = &self->pv->columns[sort_column_id];
1025
if (!(column->flags & GCR_COLUMN_SORTABLE))
1027
if (!lookup_compare_func (column->property_type)) {
1028
g_warning ("no sort implementation defined for type '%s' on column '%s'",
1029
g_type_name (column->property_type), column->property_name);
1033
func = reverse ? order_sequence_by_property_reverse : order_sequence_by_property;
1034
argument = (gpointer)column;
1037
g_warning ("invalid sort_column_id passed to gtk_tree_sortable_set_sort_column_id(): %d",
1042
if (sort_column_id != self->pv->sort_column_id ||
1043
order != self->pv->sort_order_type) {
1044
self->pv->sort_column_id = sort_column_id;
1045
self->pv->sort_order_type = order;
1046
gtk_tree_sortable_sort_column_changed (sortable);
1049
if (func != self->pv->order_current ||
1050
argument != self->pv->order_argument) {
1051
self->pv->order_current = func;
1052
self->pv->order_argument = (gpointer)argument;
1053
collection_resort_sequence (self, NULL, self->pv->root_sequence);
1058
clear_sort_closure (GcrCollectionSortClosure *closure)
1060
if (closure->destroy_func)
1061
(closure->destroy_func) (closure->user_data);
1062
closure->sort_func = NULL;
1063
closure->destroy_func = NULL;
1064
closure->user_data = NULL;
1068
set_sort_closure (GcrCollectionSortClosure *closure,
1069
GtkTreeIterCompareFunc func,
1071
GDestroyNotify destroy)
1073
clear_sort_closure (closure);
1074
closure->sort_func = func;
1075
closure->user_data = data;
1076
closure->destroy_func = destroy;
1080
gcr_collection_model_set_sort_func (GtkTreeSortable *sortable,
1081
gint sort_column_id,
1082
GtkTreeIterCompareFunc func,
1084
GDestroyNotify destroy)
1086
GcrCollectionModel *self = GCR_COLLECTION_MODEL (sortable);
1088
g_return_if_fail (sort_column_id >= 0 && sort_column_id < self->pv->n_columns);
1090
set_sort_closure (&self->pv->column_sort_closures[sort_column_id],
1091
func, data, destroy);
1093
/* Resorts if necessary */
1094
if (self->pv->sort_column_id == sort_column_id) {
1095
gcr_collection_model_set_sort_column_id (sortable,
1096
self->pv->sort_column_id,
1097
self->pv->sort_order_type);
1102
gcr_collection_model_set_default_sort_func (GtkTreeSortable *sortable,
1103
GtkTreeIterCompareFunc func,
1104
gpointer data, GDestroyNotify destroy)
1106
GcrCollectionModel *self = GCR_COLLECTION_MODEL (sortable);
1108
set_sort_closure (&self->pv->default_sort_closure,
1109
func, data, destroy);
1111
/* Resorts if necessary */
1112
if (self->pv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) {
1113
gcr_collection_model_set_sort_column_id (sortable,
1114
self->pv->sort_column_id,
1115
self->pv->sort_order_type);
1120
gcr_collection_model_has_default_sort_func (GtkTreeSortable *sortable)
1122
GcrCollectionModel *self = GCR_COLLECTION_MODEL (sortable);
1124
return (self->pv->default_sort_closure.sort_func != NULL);
1128
gcr_collection_model_tree_sortable_init (GtkTreeSortableIface *iface)
1130
iface->get_sort_column_id = gcr_collection_model_get_sort_column_id;
1131
iface->set_sort_column_id = gcr_collection_model_set_sort_column_id;
1132
iface->set_sort_func = gcr_collection_model_set_sort_func;
1133
iface->set_default_sort_func = gcr_collection_model_set_default_sort_func;
1134
iface->has_default_sort_func = gcr_collection_model_has_default_sort_func;
1138
gcr_collection_model_init (GcrCollectionModel *self)
1140
self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_COLLECTION_MODEL, GcrCollectionModelPrivate);
1142
self->pv->root_sequence = g_sequence_new (NULL);
1143
self->pv->object_to_seq = g_hash_table_new (g_direct_hash, g_direct_equal);
1144
self->pv->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
1145
self->pv->sort_order_type = GTK_SORT_ASCENDING;
1146
self->pv->order_current = order_sequence_as_unsorted;
1150
gcr_collection_model_set_property (GObject *object, guint prop_id,
1151
const GValue *value, GParamSpec *pspec)
1153
GcrCollectionModel *self = GCR_COLLECTION_MODEL (object);
1158
self->pv->mode = g_value_get_enum (value);
1160
case PROP_COLLECTION:
1161
gcr_collection_model_set_collection (self, g_value_get_object (value));
1164
columns = g_value_get_pointer (value);
1166
gcr_collection_model_set_columns (self, columns);
1169
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1175
gcr_collection_model_get_property (GObject *object, guint prop_id,
1176
GValue *value, GParamSpec *pspec)
1178
GcrCollectionModel *self = GCR_COLLECTION_MODEL (object);
1182
g_value_set_enum (value, self->pv->mode);
1184
case PROP_COLLECTION:
1185
g_value_set_object (value, self->pv->collection);
1188
g_value_set_pointer (value, (gpointer)self->pv->columns);
1191
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1197
gcr_collection_model_dispose (GObject *object)
1199
GcrCollectionModel *self = GCR_COLLECTION_MODEL (object);
1201
/* Disconnect from all rows */
1202
if (self->pv->collection) {
1203
remove_children_from_sequence (self, self->pv->root_sequence,
1204
self->pv->collection, NULL, FALSE);
1205
g_object_unref (self->pv->collection);
1206
self->pv->collection = NULL;
1209
G_OBJECT_CLASS (gcr_collection_model_parent_class)->dispose (object);
1213
gcr_collection_model_finalize (GObject *object)
1215
GcrCollectionModel *self = GCR_COLLECTION_MODEL (object);
1218
g_assert (!self->pv->collection);
1220
g_assert (g_sequence_get_length (self->pv->root_sequence) == 0);
1221
g_sequence_free (self->pv->root_sequence);
1222
g_assert (g_hash_table_size (self->pv->object_to_seq) == 0);
1223
g_hash_table_destroy (self->pv->object_to_seq);
1225
if (self->pv->selected) {
1226
g_assert (g_hash_table_size (self->pv->selected) == 0);
1227
g_hash_table_destroy (self->pv->selected);
1228
self->pv->selected = NULL;
1231
self->pv->columns = NULL;
1232
for (i = 0; i < self->pv->n_columns; i++)
1233
clear_sort_closure (&self->pv->column_sort_closures[i]);
1234
g_free (self->pv->column_sort_closures);
1235
clear_sort_closure (&self->pv->default_sort_closure);
1237
G_OBJECT_CLASS (gcr_collection_model_parent_class)->finalize (object);
1241
gcr_collection_model_class_init (GcrCollectionModelClass *klass)
1243
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1244
gcr_collection_model_parent_class = g_type_class_peek_parent (klass);
1246
gobject_class->dispose = gcr_collection_model_dispose;
1247
gobject_class->finalize = gcr_collection_model_finalize;
1248
gobject_class->set_property = gcr_collection_model_set_property;
1249
gobject_class->get_property = gcr_collection_model_get_property;
1251
g_object_class_install_property (gobject_class, PROP_MODE,
1252
g_param_spec_enum ("mode", "Mode", "Tree or list mode",
1253
GCR_TYPE_COLLECTION_MODEL_MODE, GCR_COLLECTION_MODEL_TREE,
1254
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1256
g_object_class_install_property (gobject_class, PROP_COLLECTION,
1257
g_param_spec_object ("collection", "Object Collection", "Collection to get objects from",
1258
GCR_TYPE_COLLECTION, G_PARAM_READWRITE));
1260
g_object_class_install_property (gobject_class, PROP_COLUMNS,
1261
g_param_spec_pointer ("columns", "Columns", "Columns for the model",
1262
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1264
g_type_class_add_private (klass, sizeof (GcrCollectionModelPrivate));
1268
* gcr_collection_model_new: (skip)
1269
* @collection: the collection to represent
1270
* @mode: whether list or tree mode
1271
* @...: the column names and types
1273
* Create a new #GcrCollectionModel. The variable argument list should contain
1274
* pairs of property names, and #GType values. The variable argument list should
1275
* be terminated with %NULL.
1277
* Returns: (transfer full): a newly allocated model, which should be released
1278
* with g_object_unref().
1281
gcr_collection_model_new (GcrCollection *collection,
1282
GcrCollectionModelMode mode,
1286
GcrCollectionModel *self;
1291
/* With a null terminator */
1292
array = g_array_new (TRUE, TRUE, sizeof (GcrColumn));
1294
va_start (va, mode);
1295
while ((arg = va_arg (va, const gchar*)) != NULL) {
1296
memset (&column, 0, sizeof (column));
1297
column.property_name = g_strdup (arg);
1298
column.property_type = va_arg (va, GType);
1299
column.column_type = column.property_type;
1300
g_array_append_val (array, column);
1304
self = gcr_collection_model_new_full (collection, mode, (GcrColumn*)array->data);
1305
g_object_set_data_full (G_OBJECT (self), "gcr_collection_model_new",
1306
g_array_free (array, FALSE), free_owned_columns);
1311
* gcr_collection_model_new_full: (skip)
1312
* @collection: the collection to represent
1313
* @mode: whether list or tree mode
1314
* @columns: the columns the model should contain
1316
* Create a new #GcrCollectionModel.
1318
* Returns: (transfer full): a newly allocated model, which should be released
1319
* with g_object_unref()
1322
gcr_collection_model_new_full (GcrCollection *collection,
1323
GcrCollectionModelMode mode,
1324
const GcrColumn *columns)
1326
GcrCollectionModel *self = g_object_new (GCR_TYPE_COLLECTION_MODEL,
1327
"collection", collection,
1330
gcr_collection_model_set_columns (self, columns);
1335
* gcr_collection_model_set_columns: (skip)
1337
* @columns: The columns the model should contain
1339
* Set the columns that the model should contain. @columns is an array of
1340
* #GcrColumn structures, with the last one containing %NULL for all values.
1342
* This function can only be called once, and only if the model was not created
1343
* without a set of columns. This function cannot be called after the model
1344
* has been added to a view.
1346
* The columns are accessed as static data. They should continue to remain
1347
* in memory for longer than the GcrCollectionModel object.
1349
* Returns: The number of columns
1352
gcr_collection_model_set_columns (GcrCollectionModel *self,
1353
const GcrColumn *columns)
1355
const GcrColumn *col;
1358
g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), 0);
1359
g_return_val_if_fail (columns, 0);
1360
g_return_val_if_fail (self->pv->n_columns == 0, 0);
1362
/* Count the number of columns, extra column for selected */
1363
for (col = columns, n_columns = 1; col->property_name; ++col)
1366
/* We expect the columns to stay around */
1367
self->pv->columns = columns;
1368
self->pv->n_columns = n_columns;
1369
self->pv->column_sort_closures = g_new0 (GcrCollectionSortClosure, self->pv->n_columns);
1371
return n_columns - 1;
1375
* gcr_collection_model_get_collection:
1376
* @self: a collection model
1378
* Get the collection which this model represents
1380
* Returns: (transfer none): the collection, owned by the model
1383
gcr_collection_model_get_collection (GcrCollectionModel *self)
1385
g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), NULL);
1386
return self->pv->collection;
1390
gcr_collection_model_set_collection (GcrCollectionModel *self,
1391
GcrCollection *collection)
1393
GcrCollection *previous;
1394
GHashTable *exclude;
1395
GList *children = NULL;
1398
g_return_if_fail (GCR_IS_COLLECTION_MODEL (self));
1399
g_return_if_fail (collection == NULL || GCR_IS_COLLECTION (collection));
1401
if (collection == self->pv->collection)
1405
g_object_ref (collection);
1406
previous = self->pv->collection;
1407
self->pv->collection = collection;
1410
children = gcr_collection_get_objects (collection);
1413
exclude = g_hash_table_new (g_direct_hash, g_direct_equal);
1414
for (l = children; l != NULL; l = g_list_next (l))
1415
g_hash_table_insert (exclude, l->data, l->data);
1417
remove_children_from_sequence (self, self->pv->root_sequence,
1418
previous, exclude, TRUE);
1420
g_hash_table_destroy (exclude);
1421
g_object_unref (previous);
1425
add_children_to_sequence (self, self->pv->root_sequence,
1426
NULL, collection, children,
1427
self->pv->object_to_seq, TRUE);
1428
g_list_free (children);
1431
g_object_notify (G_OBJECT (self), "collection");
1435
* gcr_collection_model_object_for_iter:
1439
* Get the object that is represented by the given row in the model.
1441
* Returns: (transfer none): The object, owned by the model.
1444
gcr_collection_model_object_for_iter (GcrCollectionModel *self, const GtkTreeIter *iter)
1446
g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), NULL);
1447
g_return_val_if_fail (iter != NULL, NULL);
1448
g_return_val_if_fail (iter->stamp == COLLECTION_MODEL_STAMP, NULL);
1449
g_return_val_if_fail (G_IS_OBJECT (iter->user_data), NULL);
1451
return G_OBJECT (iter->user_data);
1455
* gcr_collection_model_iter_for_object:
1457
* @object: The object
1458
* @iter: The row for the object
1460
* Set @iter to the row for the given object. If the object is not in this
1461
* model, then %FALSE will be returned.
1463
* Returns: %TRUE if the object was present.
1466
gcr_collection_model_iter_for_object (GcrCollectionModel *self, GObject *object,
1471
g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), FALSE);
1472
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
1473
g_return_val_if_fail (iter != NULL, FALSE);
1475
seq = g_hash_table_lookup (self->pv->object_to_seq, object);
1479
return sequence_iter_to_tree (self, seq, iter);
1483
* gcr_collection_model_column_for_selected:
1486
* Get the column identifier for the column that contains the values
1487
* of the selected state.
1489
* Returns: The column identifier.
1492
gcr_collection_model_column_for_selected (GcrCollectionModel *self)
1494
g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), 0);
1495
g_assert (self->pv->n_columns > 0);
1496
return self->pv->n_columns - 1;
1500
* gcr_collection_model_toggle_selected:
1504
* Toggle the selected state of a given row.
1507
gcr_collection_model_toggle_selected (GcrCollectionModel *self, GtkTreeIter *iter)
1511
g_return_if_fail (GCR_IS_COLLECTION_MODEL (self));
1513
object = gcr_collection_model_object_for_iter (self, iter);
1514
g_return_if_fail (G_IS_OBJECT (object));
1516
if (!self->pv->selected)
1517
self->pv->selected = selected_hash_table_new ();
1519
if (g_hash_table_lookup (self->pv->selected, object))
1520
g_hash_table_remove (self->pv->selected, object);
1522
g_hash_table_insert (self->pv->selected, object, object);
1526
* gcr_collection_model_change_selected:
1529
* @selected: Whether the row should be selected or not.
1531
* Set whether a given row is toggled selected or not.
1534
gcr_collection_model_change_selected (GcrCollectionModel *self, GtkTreeIter *iter, gboolean selected)
1539
g_return_if_fail (GCR_IS_COLLECTION_MODEL (self));
1541
object = gcr_collection_model_object_for_iter (self, iter);
1542
g_return_if_fail (G_IS_OBJECT (object));
1544
if (!self->pv->selected)
1545
self->pv->selected = g_hash_table_new (g_direct_hash, g_direct_equal);
1548
g_hash_table_insert (self->pv->selected, object, object);
1550
g_hash_table_remove (self->pv->selected, object);
1552
/* Tell the view that this row changed */
1553
path = gtk_tree_model_get_path (GTK_TREE_MODEL (self), iter);
1554
g_return_if_fail (path);
1555
gtk_tree_model_row_changed (GTK_TREE_MODEL (self), path, iter);
1556
gtk_tree_path_free (path);
1560
* gcr_collection_model_is_selected:
1564
* Check whether a given row has been toggled as selected.
1566
* Returns: Whether the row has been selected.
1569
gcr_collection_model_is_selected (GcrCollectionModel *self, GtkTreeIter *iter)
1573
g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), FALSE);
1575
object = gcr_collection_model_object_for_iter (self, iter);
1576
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
1578
if (!self->pv->selected)
1581
return g_hash_table_lookup (self->pv->selected, object) ? TRUE : FALSE;
1585
* gcr_collection_model_get_selected_objects:
1586
* @self: the collection model
1588
* Get a list of checked/selected objects.
1590
* Returns: (transfer container) (element-type GLib.Object): a list of selected
1591
* objects, which should be freed with g_list_free()
1594
gcr_collection_model_get_selected_objects (GcrCollectionModel *self)
1596
GHashTableIter iter;
1597
GList *result = NULL;
1600
g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), NULL);
1602
if (!self->pv->selected)
1605
g_hash_table_iter_init (&iter, self->pv->selected);
1606
while (g_hash_table_iter_next (&iter, &key, NULL))
1607
result = g_list_prepend (result, key);
1612
* gcr_collection_model_set_selected_objects:
1613
* @self: the collection model
1614
* @selected: (element-type GLib.Object): a list of objects to select
1616
* Set the checked/selected objects.
1619
gcr_collection_model_set_selected_objects (GcrCollectionModel *self,
1622
GHashTable *newly_selected;
1623
GList *old_selection;
1627
old_selection = gcr_collection_model_get_selected_objects (self);
1628
newly_selected = selected_hash_table_new ();
1630
/* Select all the objects in selected which aren't already selected */
1631
for (l = selected; l; l = g_list_next (l)) {
1632
if (!self->pv->selected || !g_hash_table_lookup (self->pv->selected, l->data)) {
1633
if (!gcr_collection_model_iter_for_object (self, l->data, &iter))
1634
g_return_if_reached ();
1635
gcr_collection_model_change_selected (self, &iter, TRUE);
1638
/* Note that we've seen this one */
1639
g_hash_table_insert (newly_selected, l->data, l->data);
1642
/* Unselect all the objects which aren't supposed to be selected */
1643
for (l = old_selection; l; l = g_list_next (l)) {
1644
if (!g_hash_table_lookup (newly_selected, l->data)) {
1645
if (!gcr_collection_model_iter_for_object (self, l->data, &iter))
1646
g_return_if_reached ();
1647
gcr_collection_model_change_selected (self, &iter, FALSE);
1651
g_list_free (old_selection);
1652
g_hash_table_destroy (newly_selected);