~ubuntu-branches/ubuntu/raring/gcr/raring-proposed

« back to all changes in this revision

Viewing changes to .pc/00git_gobject_gi_name.patch/gcr/gcr-collection-model.c

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2013-04-03 11:17:42 UTC
  • Revision ID: package-import@ubuntu.com-20130403111742-3pgbsva6v4cqrh1i
Tags: 3.6.2-0ubuntu2
Add 00git_gobject_gi_name.patch: Use GObject.Object instead of GLib.Object
in introspection annotations. Patch backported from 3.8. Fixes FTBFS.
(LP: #1163786)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * gnome-keyring
 
3
 *
 
4
 * Copyright (C) 2010 Stefan Walter
 
5
 * Copyright (C) 2011 Collabora Ltd.
 
6
 *
 
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.
 
11
 *
 
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.
 
16
 *
 
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
 
20
 * 02111-1307, USA.
 
21
 *
 
22
 * Author: Stef Walter <stefw@collabora.co.uk>
 
23
 */
 
24
 
 
25
#include "config.h"
 
26
 
 
27
#include "gcr-collection-model.h"
 
28
#include "gcr-enum-types.h"
 
29
 
 
30
#include <gtk/gtk.h>
 
31
 
 
32
#include <string.h>
 
33
#include <unistd.h>
 
34
 
 
35
/**
 
36
 * SECTION:gcr-collection-model
 
37
 * @title: GcrCollectionModel
 
38
 * @short_description: A GtkTreeModel that represents a collection
 
39
 *
 
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.
 
43
 *
 
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().
 
49
 *
 
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().
 
54
 *
 
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()
 
57
 * functions.
 
58
 */
 
59
 
 
60
/**
 
61
 * GcrCollectionModel:
 
62
 *
 
63
 * A #GtkTreeModel which contains a row for each object in a #GcrCollection.
 
64
 */
 
65
 
 
66
/**
 
67
 * GcrCollectionModelClass:
 
68
 * @parent_class: The parent class
 
69
 *
 
70
 * The class for #GcrCollectionModel.
 
71
 */
 
72
 
 
73
/**
 
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
 
77
 *
 
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.
 
81
 */
 
82
 
 
83
#define COLLECTION_MODEL_STAMP 0xAABBCCDD
 
84
 
 
85
enum {
 
86
        PROP_0,
 
87
        PROP_COLLECTION,
 
88
        PROP_COLUMNS,
 
89
        PROP_MODE
 
90
};
 
91
 
 
92
typedef struct {
 
93
        GObject *object;
 
94
        GSequenceIter *parent;
 
95
        GSequence *children;
 
96
} GcrCollectionRow;
 
97
 
 
98
typedef struct {
 
99
        GtkTreeIterCompareFunc sort_func;
 
100
        gpointer user_data;
 
101
        GDestroyNotify destroy_func;
 
102
} GcrCollectionSortClosure;
 
103
 
 
104
typedef struct _GcrCollectionColumn {
 
105
        gchar *property;
 
106
        GType *type;
 
107
        GtkTreeIterCompareFunc sort_func;
 
108
        gpointer sort_data;
 
109
        GDestroyNotify sort_destroy;
 
110
} GcrCollectionColumn;
 
111
 
 
112
struct _GcrCollectionModelPrivate {
 
113
        GcrCollectionModelMode mode;
 
114
        GcrCollection *collection;
 
115
        GHashTable *selected;
 
116
        GSequence *root_sequence;
 
117
        GHashTable *object_to_seq;
 
118
 
 
119
        const GcrColumn *columns;
 
120
        guint n_columns;
 
121
 
 
122
        /* Sort information */
 
123
        gint sort_column_id;
 
124
        GtkSortType sort_order_type;
 
125
        GcrCollectionSortClosure *column_sort_closures;
 
126
        GcrCollectionSortClosure default_sort_closure;
 
127
 
 
128
        /* Sequence ordering information */
 
129
        GCompareDataFunc order_current;
 
130
        gpointer order_argument;
 
131
};
 
132
 
 
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);
 
136
 
 
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)
 
140
);
 
141
 
 
142
typedef gint (*CompareValueFunc) (const GValue *va,
 
143
                                  const GValue *vb);
 
144
 
 
145
static gint
 
146
compare_int_value (const GValue *va,
 
147
                   const GValue *vb)
 
148
{
 
149
        gint a = g_value_get_int (va);
 
150
        gint b = g_value_get_int (vb);
 
151
        if (a > b) return 1;
 
152
        else if (a < b) return -1;
 
153
        return 0;
 
154
}
 
155
 
 
156
static gint
 
157
compare_uint_value (const GValue *va,
 
158
                    const GValue *vb)
 
159
{
 
160
        guint a = g_value_get_uint (va);
 
161
        guint b = g_value_get_uint (vb);
 
162
        if (a > b) return 1;
 
163
        else if (a < b) return -1;
 
164
        return 0;
 
165
}
 
166
 
 
167
static gint
 
168
compare_long_value (const GValue *va,
 
169
                    const GValue *vb)
 
170
{
 
171
        glong a = g_value_get_long (va);
 
172
        glong b = g_value_get_long (vb);
 
173
        if (a > b) return 1;
 
174
        else if (a < b) return -1;
 
175
        return 0;
 
176
}
 
177
 
 
178
static gint
 
179
compare_ulong_value (const GValue *va,
 
180
                     const GValue *vb)
 
181
{
 
182
        gulong a = g_value_get_ulong (va);
 
183
        gulong b = g_value_get_ulong (vb);
 
184
        if (a > b) return 1;
 
185
        else if (a < b) return -1;
 
186
        return 0;
 
187
}
 
188
 
 
189
static gint
 
190
compare_string_value (const GValue *va,
 
191
                      const GValue *vb)
 
192
{
 
193
        const gchar *a = g_value_get_string (va);
 
194
        const gchar *b = g_value_get_string (vb);
 
195
        gchar *case_a;
 
196
        gchar *case_b;
 
197
        gboolean ret;
 
198
 
 
199
        if (a == b)
 
200
                return 0;
 
201
        else if (!a)
 
202
                return -1;
 
203
        else if (!b)
 
204
                return 1;
 
205
 
 
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);
 
209
        g_free (case_a);
 
210
        g_free (case_b);
 
211
 
 
212
        return ret;
 
213
}
 
214
 
 
215
static gint
 
216
compare_date_value (const GValue *va,
 
217
                    const GValue *vb)
 
218
{
 
219
        GDate *a = g_value_get_boxed (va);
 
220
        GDate *b = g_value_get_boxed (vb);
 
221
 
 
222
        if (a == b)
 
223
                return 0;
 
224
        else if (!a)
 
225
                return -1;
 
226
        else if (!b)
 
227
                return 1;
 
228
        else
 
229
                return g_date_compare (a, b);
 
230
}
 
231
 
 
232
static CompareValueFunc
 
233
lookup_compare_func (GType type)
 
234
{
 
235
        switch (type) {
 
236
        case G_TYPE_INT:
 
237
                return compare_int_value;
 
238
        case G_TYPE_UINT:
 
239
                return compare_uint_value;
 
240
        case G_TYPE_LONG:
 
241
                return compare_long_value;
 
242
        case G_TYPE_ULONG:
 
243
                return compare_ulong_value;
 
244
        case G_TYPE_STRING:
 
245
                return compare_string_value;
 
246
        }
 
247
 
 
248
        if (type == G_TYPE_DATE)
 
249
                return compare_date_value;
 
250
 
 
251
        return NULL;
 
252
}
 
253
 
 
254
static gint
 
255
order_sequence_by_closure (gconstpointer a,
 
256
                           gconstpointer b,
 
257
                           gpointer user_data)
 
258
{
 
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;
 
263
        GtkTreeIter iter_a;
 
264
        GtkTreeIter iter_b;
 
265
 
 
266
        g_assert (closure);
 
267
        g_assert (closure->sort_func);
 
268
 
 
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);
 
273
 
 
274
        return (closure->sort_func) (GTK_TREE_MODEL (self),
 
275
                                     &iter_a, &iter_b, closure->user_data);
 
276
}
 
277
 
 
278
static gint
 
279
order_sequence_by_closure_reverse (gconstpointer a,
 
280
                                   gconstpointer b,
 
281
                                   gpointer user_data)
 
282
{
 
283
        return 0 - order_sequence_by_closure (a, b, user_data);
 
284
}
 
285
 
 
286
static gint
 
287
order_sequence_as_unsorted (gconstpointer a,
 
288
                            gconstpointer b,
 
289
                            gpointer user_data)
 
290
{
 
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);
 
294
}
 
295
 
 
296
static gint
 
297
order_sequence_as_unsorted_reverse (gconstpointer a,
 
298
                                    gconstpointer b,
 
299
                                    gpointer user_data)
 
300
{
 
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);
 
304
}
 
305
 
 
306
static void
 
307
lookup_object_property (GObject *object,
 
308
                        const gchar *property_name,
 
309
                        GValue *value)
 
310
{
 
311
        if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name))
 
312
                g_object_get_property (object, property_name, value);
 
313
 
 
314
        /* Other types have sane defaults */
 
315
        else if (G_VALUE_TYPE (value) == G_TYPE_STRING)
 
316
                g_value_set_string (value, "");
 
317
}
 
318
 
 
319
static gint
 
320
order_sequence_by_property (gconstpointer a,
 
321
                            gconstpointer b,
 
322
                            gpointer user_data)
 
323
{
 
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;
 
331
        gint ret;
 
332
 
 
333
        g_assert (column);
 
334
 
 
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);
 
341
 
 
342
        compare = lookup_compare_func (column->property_type);
 
343
        g_assert (compare != NULL);
 
344
 
 
345
        ret = (compare) (&value_a, &value_b);
 
346
 
 
347
        g_value_unset (&value_a);
 
348
        g_value_unset (&value_b);
 
349
 
 
350
        return ret;
 
351
}
 
352
 
 
353
static gint
 
354
order_sequence_by_property_reverse (gconstpointer a,
 
355
                                    gconstpointer b,
 
356
                                    gpointer user_data)
 
357
{
 
358
        return 0 - order_sequence_by_property (a, b, user_data);
 
359
}
 
360
 
 
361
static GHashTable*
 
362
selected_hash_table_new (void)
 
363
{
 
364
        return g_hash_table_new (g_direct_hash, g_direct_equal);
 
365
}
 
366
 
 
367
static gboolean
 
368
sequence_iter_to_tree (GcrCollectionModel *self,
 
369
                       GSequenceIter *seq,
 
370
                       GtkTreeIter *iter)
 
371
{
 
372
        GcrCollectionRow *row;
 
373
 
 
374
        g_return_val_if_fail (seq != NULL, FALSE);
 
375
 
 
376
        if (g_sequence_iter_is_end (seq))
 
377
                return FALSE;
 
378
 
 
379
        row = g_sequence_get (seq);
 
380
        g_return_val_if_fail (row != NULL && G_IS_OBJECT (row->object), FALSE);
 
381
 
 
382
        memset (iter, 0, sizeof (*iter));
 
383
        iter->stamp = COLLECTION_MODEL_STAMP;
 
384
        iter->user_data = row->object;
 
385
        iter->user_data2 = seq;
 
386
        return TRUE;
 
387
}
 
388
 
 
389
static GSequenceIter *
 
390
sequence_iter_for_tree (GcrCollectionModel *self,
 
391
                        GtkTreeIter *iter)
 
392
{
 
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;
 
396
}
 
397
 
 
398
static GtkTreePath *
 
399
sequence_iter_to_path (GcrCollectionModel *self,
 
400
                       GSequenceIter *seq)
 
401
{
 
402
        GcrCollectionRow *row;
 
403
        GtkTreePath *path;
 
404
 
 
405
        path = gtk_tree_path_new ();
 
406
        while (seq) {
 
407
                gtk_tree_path_prepend_index (path, g_sequence_iter_get_position (seq));
 
408
                row = g_sequence_get (seq);
 
409
                seq = row->parent;
 
410
        }
 
411
        return path;
 
412
}
 
413
 
 
414
static GSequence *
 
415
child_sequence_for_tree (GcrCollectionModel *self,
 
416
                         GtkTreeIter *iter)
 
417
{
 
418
        GcrCollectionRow *row;
 
419
        GSequenceIter *seq;
 
420
 
 
421
        if (iter == NULL) {
 
422
                return self->pv->root_sequence;
 
423
        } else {
 
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;
 
428
        }
 
429
}
 
430
 
 
431
static void
 
432
on_object_notify (GObject *object, GParamSpec *spec, GcrCollectionModel *self)
 
433
{
 
434
        GtkTreeIter iter;
 
435
        GtkTreePath *path;
 
436
        gboolean found = FALSE;
 
437
        guint i;
 
438
 
 
439
        g_return_if_fail (spec->name);
 
440
 
 
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)) {
 
444
                        found = TRUE;
 
445
                        break;
 
446
                }
 
447
        }
 
448
 
 
449
        /* Tell the tree view that this row changed */
 
450
        if (found) {
 
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);
 
457
        }
 
458
}
 
459
 
 
460
static void
 
461
on_object_gone (gpointer unused, GObject *was_object)
 
462
{
 
463
        g_warning ("object contained in GcrCollection and included in GcrCollectionModel "
 
464
                   "was destroyed before it was removed from the collection");
 
465
}
 
466
 
 
467
static void      on_collection_added              (GcrCollection *collection,
 
468
                                                   GObject *object,
 
469
                                                   gpointer user_data);
 
470
 
 
471
static void      on_collection_removed            (GcrCollection *collection,
 
472
                                                   GObject *object,
 
473
                                                   gpointer user_data);
 
474
 
 
475
static void      add_object_to_sequence           (GcrCollectionModel *self,
 
476
                                                   GSequence *sequence,
 
477
                                                   GSequenceIter *parent,
 
478
                                                   GObject *object,
 
479
                                                   gboolean emit);
 
480
 
 
481
static void      remove_object_from_sequence      (GcrCollectionModel *self,
 
482
                                                   GSequence *sequence,
 
483
                                                   GSequenceIter *seq,
 
484
                                                   GObject *object,
 
485
                                                   gboolean emit);
 
486
 
 
487
static void
 
488
add_children_to_sequence (GcrCollectionModel *self,
 
489
                          GSequence *sequence,
 
490
                          GSequenceIter *parent,
 
491
                          GcrCollection *collection,
 
492
                          GList *children,
 
493
                          GHashTable *exclude,
 
494
                          gboolean emit)
 
495
{
 
496
        GList *l;
 
497
 
 
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);
 
501
        }
 
502
 
 
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);
 
506
}
 
507
 
 
508
static void
 
509
add_object_to_sequence (GcrCollectionModel *self,
 
510
                        GSequence *sequence,
 
511
                        GSequenceIter *parent,
 
512
                        GObject *object,
 
513
                        gboolean emit)
 
514
{
 
515
        GcrCollectionRow *row;
 
516
        GcrCollection *collection;
 
517
        GSequenceIter *seq;
 
518
        GtkTreeIter iter;
 
519
        GtkTreePath *path;
 
520
        GList *children;
 
521
 
 
522
        g_assert (GCR_IS_COLLECTION_MODEL (self));
 
523
        g_assert (G_IS_OBJECT (object));
 
524
        g_assert (self->pv->order_current);
 
525
 
 
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?");
 
529
                return;
 
530
        }
 
531
 
 
532
        row = g_slice_new0 (GcrCollectionRow);
 
533
        row->object = object;
 
534
        row->parent = parent;
 
535
        row->children = NULL;
 
536
 
 
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);
 
541
 
 
542
        if (emit) {
 
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);
 
549
        }
 
550
 
 
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);
 
559
        }
 
560
}
 
561
 
 
562
static void
 
563
on_collection_added (GcrCollection *collection,
 
564
                     GObject *object,
 
565
                     gpointer user_data)
 
566
{
 
567
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (user_data);
 
568
        GSequence *sequence;
 
569
        GSequenceIter *parent;
 
570
        GcrCollectionRow *row;
 
571
 
 
572
        if (collection == self->pv->collection) {
 
573
                sequence = self->pv->root_sequence;
 
574
                parent = NULL;
 
575
        } else {
 
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;
 
580
        }
 
581
 
 
582
        add_object_to_sequence (self, sequence, parent, object, TRUE);
 
583
}
 
584
 
 
585
static void
 
586
remove_children_from_sequence (GcrCollectionModel *self,
 
587
                               GSequence *sequence,
 
588
                               GcrCollection *collection,
 
589
                               GHashTable *exclude,
 
590
                               gboolean emit)
 
591
{
 
592
        GSequenceIter *seq, *next;
 
593
        GcrCollectionRow *row;
 
594
 
 
595
        g_signal_handlers_disconnect_by_func (collection, on_collection_added, self);
 
596
        g_signal_handlers_disconnect_by_func (collection, on_collection_removed, self);
 
597
 
 
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);
 
604
        }
 
605
}
 
606
 
 
607
static void
 
608
remove_object_from_sequence (GcrCollectionModel *self,
 
609
                             GSequence *sequence,
 
610
                             GSequenceIter *seq,
 
611
                             GObject *object,
 
612
                             gboolean emit)
 
613
{
 
614
        GcrCollectionRow *row;
 
615
        GtkTreePath *path = NULL;
 
616
 
 
617
        if (emit) {
 
618
                path = sequence_iter_to_path (self, seq);
 
619
                g_assert (path != NULL);
 
620
        }
 
621
 
 
622
        row = g_sequence_get (seq);
 
623
        g_assert (row->object == object);
 
624
 
 
625
        g_object_weak_unref (object, on_object_gone, self);
 
626
        g_signal_handlers_disconnect_by_func (object, on_object_notify, self);
 
627
 
 
628
        if (row->children) {
 
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;
 
636
        }
 
637
 
 
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 ();
 
642
 
 
643
        g_sequence_remove (seq);
 
644
        g_slice_free (GcrCollectionRow, row);
 
645
 
 
646
        /* Fire signal for this removed row */
 
647
        if (path != NULL) {
 
648
                gtk_tree_model_row_deleted (GTK_TREE_MODEL (self), path);
 
649
                gtk_tree_path_free (path);
 
650
        }
 
651
 
 
652
}
 
653
 
 
654
static void
 
655
on_collection_removed (GcrCollection *collection,
 
656
                       GObject *object,
 
657
                       gpointer user_data)
 
658
{
 
659
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (user_data);
 
660
        GSequenceIter *seq;
 
661
        GSequence *sequence;
 
662
 
 
663
        seq = g_hash_table_lookup (self->pv->object_to_seq, object);
 
664
        g_return_if_fail (seq != NULL);
 
665
 
 
666
        sequence = g_sequence_iter_get_sequence (seq);
 
667
        g_assert (sequence != NULL);
 
668
 
 
669
        remove_object_from_sequence (self, sequence, seq, object, TRUE);
 
670
}
 
671
 
 
672
static void
 
673
free_owned_columns (gpointer data)
 
674
{
 
675
        GcrColumn *columns;
 
676
        g_assert (data);
 
677
 
 
678
        /* Only the property column is in use */
 
679
        for (columns = data; columns->property_name; ++columns)
 
680
                g_free ((gchar*)columns->property_name);
 
681
        g_free (data);
 
682
}
 
683
 
 
684
static GtkTreeModelFlags
 
685
gcr_collection_model_real_get_flags (GtkTreeModel *model)
 
686
{
 
687
        return GTK_TREE_MODEL_ITERS_PERSIST;
 
688
}
 
689
 
 
690
static gint
 
691
gcr_collection_model_real_get_n_columns (GtkTreeModel *model)
 
692
{
 
693
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
 
694
        return self->pv->n_columns;
 
695
}
 
696
 
 
697
static GType
 
698
gcr_collection_model_real_get_column_type (GtkTreeModel *model,
 
699
                                           gint column_id)
 
700
{
 
701
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
 
702
        g_return_val_if_fail (column_id >= 0 && column_id <= self->pv->n_columns, 0);
 
703
 
 
704
        /* The last is the selected column */
 
705
        if (column_id == self->pv->n_columns)
 
706
                return G_TYPE_BOOLEAN;
 
707
 
 
708
        return self->pv->columns[column_id].column_type;
 
709
}
 
710
 
 
711
static gboolean
 
712
gcr_collection_model_real_get_iter (GtkTreeModel *model,
 
713
                                    GtkTreeIter *iter,
 
714
                                    GtkTreePath *path)
 
715
{
 
716
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
 
717
        const gint *indices;
 
718
        GSequence *sequence;
 
719
        GSequenceIter *seq;
 
720
        GcrCollectionRow *row;
 
721
        gint count;
 
722
        gint i;
 
723
 
 
724
        sequence = self->pv->root_sequence;
 
725
        seq = NULL;
 
726
 
 
727
        indices = gtk_tree_path_get_indices_with_depth (path, &count);
 
728
        if (count == 0)
 
729
                return FALSE;
 
730
 
 
731
        for (i = 0; i < count; i++) {
 
732
                if (!sequence)
 
733
                        return FALSE;
 
734
                seq = g_sequence_get_iter_at_pos (sequence, indices[i]);
 
735
                if (g_sequence_iter_is_end (seq))
 
736
                        return FALSE;
 
737
                row = g_sequence_get (seq);
 
738
                sequence = row->children;
 
739
        }
 
740
 
 
741
        return sequence_iter_to_tree (self, seq, iter);
 
742
}
 
743
 
 
744
static GtkTreePath*
 
745
gcr_collection_model_real_get_path (GtkTreeModel *model,
 
746
                                    GtkTreeIter *iter)
 
747
{
 
748
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
 
749
        GSequenceIter *seq;
 
750
 
 
751
        if (iter == NULL)
 
752
                return gtk_tree_path_new ();
 
753
 
 
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);
 
757
}
 
758
 
 
759
static void
 
760
gcr_collection_model_real_get_value (GtkTreeModel *model,
 
761
                                     GtkTreeIter *iter,
 
762
                                     gint column_id,
 
763
                                     GValue *value)
 
764
{
 
765
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
 
766
        GObject *object;
 
767
        GValue original;
 
768
        const GcrColumn *column;
 
769
        GParamSpec *spec;
 
770
 
 
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);
 
774
 
 
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));
 
779
                return;
 
780
        }
 
781
 
 
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);
 
786
 
 
787
        /* Lookup the property on the object */
 
788
        spec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), column->property_name);
 
789
        if (spec != NULL) {
 
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);
 
795
 
 
796
                        if (column->transformer) {
 
797
                                (column->transformer) (&original, value);
 
798
                        } else {
 
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));
 
804
                                spec = NULL;
 
805
                        }
 
806
 
 
807
                /* Simple, no transformation necessary */
 
808
                } else {
 
809
                        g_object_get_property (object, column->property_name, value);
 
810
                }
 
811
        }
 
812
 
 
813
        if (spec == NULL) {
 
814
 
 
815
                /* All the number types have sane defaults */
 
816
                if (column->column_type == G_TYPE_STRING)
 
817
                        g_value_set_string (value, "");
 
818
        }
 
819
}
 
820
 
 
821
static gboolean
 
822
gcr_collection_model_real_iter_next (GtkTreeModel *model,
 
823
                                     GtkTreeIter *iter)
 
824
{
 
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);
 
829
}
 
830
 
 
831
static gboolean
 
832
gcr_collection_model_real_iter_children (GtkTreeModel *model,
 
833
                                         GtkTreeIter *iter,
 
834
                                         GtkTreeIter *parent)
 
835
{
 
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);
 
839
}
 
840
 
 
841
static gboolean
 
842
gcr_collection_model_real_iter_has_child (GtkTreeModel *model,
 
843
                                          GtkTreeIter *iter)
 
844
{
 
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));
 
848
}
 
849
 
 
850
static gint
 
851
gcr_collection_model_real_iter_n_children (GtkTreeModel *model,
 
852
                                           GtkTreeIter *iter)
 
853
{
 
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;
 
857
}
 
858
 
 
859
static gboolean
 
860
gcr_collection_model_real_iter_nth_child (GtkTreeModel *model,
 
861
                                          GtkTreeIter *iter,
 
862
                                          GtkTreeIter *parent,
 
863
                                          gint n)
 
864
{
 
865
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
 
866
        GSequence *sequence;
 
867
        GSequenceIter *seq;
 
868
 
 
869
        sequence = child_sequence_for_tree (self, parent);
 
870
        if (sequence == NULL)
 
871
                return FALSE;
 
872
        seq = g_sequence_get_iter_at_pos (sequence, n);
 
873
        return sequence_iter_to_tree (self, seq, iter);
 
874
}
 
875
 
 
876
static gboolean
 
877
gcr_collection_model_real_iter_parent (GtkTreeModel *model,
 
878
                                       GtkTreeIter *iter,
 
879
                                       GtkTreeIter *child)
 
880
{
 
881
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (model);
 
882
        GSequenceIter *seq;
 
883
        GcrCollectionRow *row;
 
884
 
 
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)
 
889
                return FALSE;
 
890
        return sequence_iter_to_tree (self, row->parent, iter);
 
891
}
 
892
 
 
893
static void
 
894
gcr_collection_model_real_ref_node (GtkTreeModel *model,
 
895
                                    GtkTreeIter *iter)
 
896
{
 
897
        /* Nothing to do */
 
898
}
 
899
 
 
900
static void
 
901
gcr_collection_model_real_unref_node (GtkTreeModel *model,
 
902
                                      GtkTreeIter *iter)
 
903
{
 
904
        /* Nothing to do */
 
905
}
 
906
 
 
907
static void
 
908
gcr_collection_model_tree_model_init (GtkTreeModelIface *iface)
 
909
{
 
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;
 
924
}
 
925
 
 
926
static void
 
927
collection_resort_sequence (GcrCollectionModel *self,
 
928
                            GSequenceIter *parent,
 
929
                            GSequence *sequence)
 
930
{
 
931
        GPtrArray *previous;
 
932
        GSequenceIter *seq, *next;
 
933
        gint *new_order;
 
934
        GtkTreePath *path;
 
935
        GtkTreeIter iter;
 
936
        GcrCollectionRow *row;
 
937
        gint index;
 
938
        gint i;
 
939
 
 
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);
 
946
                if (row->children)
 
947
                        collection_resort_sequence (self, seq, row->children);
 
948
                g_ptr_array_add (previous, row->object);
 
949
        }
 
950
 
 
951
        if (previous->len == 0) {
 
952
                g_ptr_array_free (previous, TRUE);
 
953
                return;
 
954
        }
 
955
 
 
956
        /* Actually perform the sort */
 
957
        g_sequence_sort (sequence, self->pv->order_current, self);
 
958
 
 
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;
 
967
        }
 
968
 
 
969
        g_ptr_array_free (previous, TRUE);
 
970
 
 
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);
 
974
        } else {
 
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);
 
978
        }
 
979
        gtk_tree_path_free (path);
 
980
        g_free (new_order);
 
981
}
 
982
 
 
983
static gboolean
 
984
gcr_collection_model_get_sort_column_id (GtkTreeSortable *sortable,
 
985
                                         gint *sort_column_id,
 
986
                                         GtkSortType *order)
 
987
{
 
988
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (sortable);
 
989
 
 
990
        if (order)
 
991
                *order = self->pv->sort_order_type;
 
992
        if (sort_column_id)
 
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);
 
996
}
 
997
 
 
998
static void
 
999
gcr_collection_model_set_sort_column_id (GtkTreeSortable *sortable,
 
1000
                                         gint sort_column_id,
 
1001
                                         GtkSortType order)
 
1002
{
 
1003
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (sortable);
 
1004
        GCompareDataFunc func;
 
1005
        gpointer argument;
 
1006
        const GcrColumn *column;
 
1007
        gboolean reverse;
 
1008
 
 
1009
        reverse = (order == GTK_SORT_DESCENDING);
 
1010
 
 
1011
        if (sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) {
 
1012
                func = reverse ? order_sequence_as_unsorted_reverse : order_sequence_as_unsorted;
 
1013
                argument = NULL;
 
1014
 
 
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;
 
1018
 
 
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];
 
1023
                } else {
 
1024
                        column = &self->pv->columns[sort_column_id];
 
1025
                        if (!(column->flags & GCR_COLUMN_SORTABLE))
 
1026
                                return;
 
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);
 
1030
                                return;
 
1031
                        }
 
1032
 
 
1033
                        func = reverse ? order_sequence_by_property_reverse : order_sequence_by_property;
 
1034
                        argument = (gpointer)column;
 
1035
                }
 
1036
        } else {
 
1037
                g_warning ("invalid sort_column_id passed to gtk_tree_sortable_set_sort_column_id(): %d",
 
1038
                           sort_column_id);
 
1039
                return;
 
1040
        }
 
1041
 
 
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);
 
1047
        }
 
1048
 
 
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);
 
1054
        }
 
1055
}
 
1056
 
 
1057
static void
 
1058
clear_sort_closure (GcrCollectionSortClosure *closure)
 
1059
{
 
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;
 
1065
}
 
1066
 
 
1067
static void
 
1068
set_sort_closure (GcrCollectionSortClosure *closure,
 
1069
                  GtkTreeIterCompareFunc func,
 
1070
                  gpointer data,
 
1071
                  GDestroyNotify destroy)
 
1072
{
 
1073
        clear_sort_closure (closure);
 
1074
        closure->sort_func = func;
 
1075
        closure->user_data = data;
 
1076
        closure->destroy_func = destroy;
 
1077
}
 
1078
 
 
1079
static void
 
1080
gcr_collection_model_set_sort_func (GtkTreeSortable *sortable,
 
1081
                                    gint sort_column_id,
 
1082
                                    GtkTreeIterCompareFunc func,
 
1083
                                    gpointer data,
 
1084
                                    GDestroyNotify destroy)
 
1085
{
 
1086
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (sortable);
 
1087
 
 
1088
        g_return_if_fail (sort_column_id >= 0 && sort_column_id < self->pv->n_columns);
 
1089
 
 
1090
        set_sort_closure (&self->pv->column_sort_closures[sort_column_id],
 
1091
                          func, data, destroy);
 
1092
 
 
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);
 
1098
        }
 
1099
}
 
1100
 
 
1101
static void
 
1102
gcr_collection_model_set_default_sort_func (GtkTreeSortable *sortable,
 
1103
                                            GtkTreeIterCompareFunc func,
 
1104
                                            gpointer data, GDestroyNotify destroy)
 
1105
{
 
1106
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (sortable);
 
1107
 
 
1108
        set_sort_closure (&self->pv->default_sort_closure,
 
1109
                          func, data, destroy);
 
1110
 
 
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);
 
1116
        }
 
1117
}
 
1118
 
 
1119
static gboolean
 
1120
gcr_collection_model_has_default_sort_func (GtkTreeSortable *sortable)
 
1121
{
 
1122
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (sortable);
 
1123
 
 
1124
        return (self->pv->default_sort_closure.sort_func != NULL);
 
1125
}
 
1126
 
 
1127
static void
 
1128
gcr_collection_model_tree_sortable_init (GtkTreeSortableIface *iface)
 
1129
{
 
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;
 
1135
}
 
1136
 
 
1137
static void
 
1138
gcr_collection_model_init (GcrCollectionModel *self)
 
1139
{
 
1140
        self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_COLLECTION_MODEL, GcrCollectionModelPrivate);
 
1141
 
 
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;
 
1147
}
 
1148
 
 
1149
static void
 
1150
gcr_collection_model_set_property (GObject *object, guint prop_id,
 
1151
                                   const GValue *value, GParamSpec *pspec)
 
1152
{
 
1153
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (object);
 
1154
        GcrColumn *columns;
 
1155
 
 
1156
        switch (prop_id) {
 
1157
        case PROP_MODE:
 
1158
                self->pv->mode = g_value_get_enum (value);
 
1159
                break;
 
1160
        case PROP_COLLECTION:
 
1161
                gcr_collection_model_set_collection (self, g_value_get_object (value));
 
1162
                break;
 
1163
        case PROP_COLUMNS:
 
1164
                columns = g_value_get_pointer (value);
 
1165
                if (columns)
 
1166
                        gcr_collection_model_set_columns (self, columns);
 
1167
                break;
 
1168
        default:
 
1169
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
1170
                break;
 
1171
        }
 
1172
}
 
1173
 
 
1174
static void
 
1175
gcr_collection_model_get_property (GObject *object, guint prop_id,
 
1176
                                   GValue *value, GParamSpec *pspec)
 
1177
{
 
1178
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (object);
 
1179
 
 
1180
        switch (prop_id) {
 
1181
        case PROP_MODE:
 
1182
                g_value_set_enum (value, self->pv->mode);
 
1183
                break;
 
1184
        case PROP_COLLECTION:
 
1185
                g_value_set_object (value, self->pv->collection);
 
1186
                break;
 
1187
        case PROP_COLUMNS:
 
1188
                g_value_set_pointer (value, (gpointer)self->pv->columns);
 
1189
                break;
 
1190
        default:
 
1191
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
1192
                break;
 
1193
        }
 
1194
}
 
1195
 
 
1196
static void
 
1197
gcr_collection_model_dispose (GObject *object)
 
1198
{
 
1199
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (object);
 
1200
 
 
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;
 
1207
        }
 
1208
 
 
1209
        G_OBJECT_CLASS (gcr_collection_model_parent_class)->dispose (object);
 
1210
}
 
1211
 
 
1212
static void
 
1213
gcr_collection_model_finalize (GObject *object)
 
1214
{
 
1215
        GcrCollectionModel *self = GCR_COLLECTION_MODEL (object);
 
1216
        guint i;
 
1217
 
 
1218
        g_assert (!self->pv->collection);
 
1219
 
 
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);
 
1224
 
 
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;
 
1229
        }
 
1230
 
 
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);
 
1236
 
 
1237
        G_OBJECT_CLASS (gcr_collection_model_parent_class)->finalize (object);
 
1238
}
 
1239
 
 
1240
static void
 
1241
gcr_collection_model_class_init (GcrCollectionModelClass *klass)
 
1242
{
 
1243
        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
1244
        gcr_collection_model_parent_class = g_type_class_peek_parent (klass);
 
1245
 
 
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;
 
1250
 
 
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));
 
1255
 
 
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));
 
1259
 
 
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));
 
1263
 
 
1264
        g_type_class_add_private (klass, sizeof (GcrCollectionModelPrivate));
 
1265
}
 
1266
 
 
1267
/**
 
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
 
1272
 *
 
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.
 
1276
 *
 
1277
 * Returns: (transfer full): a newly allocated model, which should be released
 
1278
 *          with g_object_unref().
 
1279
 */
 
1280
GcrCollectionModel*
 
1281
gcr_collection_model_new (GcrCollection *collection,
 
1282
                          GcrCollectionModelMode mode,
 
1283
                          ...)
 
1284
{
 
1285
        GcrColumn column;
 
1286
        GcrCollectionModel *self;
 
1287
        const gchar *arg;
 
1288
        GArray *array;
 
1289
        va_list va;
 
1290
 
 
1291
        /* With a null terminator */
 
1292
        array = g_array_new (TRUE, TRUE, sizeof (GcrColumn));
 
1293
 
 
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);
 
1301
        }
 
1302
        va_end (va);
 
1303
 
 
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);
 
1307
        return self;
 
1308
}
 
1309
 
 
1310
/**
 
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
 
1315
 *
 
1316
 * Create a new #GcrCollectionModel.
 
1317
 *
 
1318
 * Returns: (transfer full): a newly allocated model, which should be released
 
1319
 *          with g_object_unref()
 
1320
 */
 
1321
GcrCollectionModel*
 
1322
gcr_collection_model_new_full (GcrCollection *collection,
 
1323
                               GcrCollectionModelMode mode,
 
1324
                               const GcrColumn *columns)
 
1325
{
 
1326
        GcrCollectionModel *self = g_object_new (GCR_TYPE_COLLECTION_MODEL,
 
1327
                                                 "collection", collection,
 
1328
                                                 "mode", mode,
 
1329
                                                 NULL);
 
1330
        gcr_collection_model_set_columns (self, columns);
 
1331
        return self;
 
1332
}
 
1333
 
 
1334
/**
 
1335
 * gcr_collection_model_set_columns: (skip)
 
1336
 * @self: The model
 
1337
 * @columns: The columns the model should contain
 
1338
 *
 
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.
 
1341
 *
 
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.
 
1345
 *
 
1346
 * The columns are accessed as static data. They should continue to remain
 
1347
 * in memory for longer than the GcrCollectionModel object.
 
1348
 *
 
1349
 * Returns: The number of columns
 
1350
 */
 
1351
guint
 
1352
gcr_collection_model_set_columns (GcrCollectionModel *self,
 
1353
                                  const GcrColumn *columns)
 
1354
{
 
1355
        const GcrColumn *col;
 
1356
        guint n_columns;
 
1357
 
 
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);
 
1361
 
 
1362
        /* Count the number of columns, extra column for selected */
 
1363
        for (col = columns, n_columns = 1; col->property_name; ++col)
 
1364
                ++n_columns;
 
1365
 
 
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);
 
1370
 
 
1371
        return n_columns - 1;
 
1372
}
 
1373
 
 
1374
/**
 
1375
 * gcr_collection_model_get_collection:
 
1376
 * @self: a collection model
 
1377
 *
 
1378
 * Get the collection which this model represents
 
1379
 *
 
1380
 * Returns: (transfer none): the collection, owned by the model
 
1381
 */
 
1382
GcrCollection *
 
1383
gcr_collection_model_get_collection (GcrCollectionModel *self)
 
1384
{
 
1385
        g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), NULL);
 
1386
        return self->pv->collection;
 
1387
}
 
1388
 
 
1389
void
 
1390
gcr_collection_model_set_collection (GcrCollectionModel *self,
 
1391
                                     GcrCollection *collection)
 
1392
{
 
1393
        GcrCollection *previous;
 
1394
        GHashTable *exclude;
 
1395
        GList *children = NULL;
 
1396
        GList *l;
 
1397
 
 
1398
        g_return_if_fail (GCR_IS_COLLECTION_MODEL (self));
 
1399
        g_return_if_fail (collection == NULL || GCR_IS_COLLECTION (collection));
 
1400
 
 
1401
        if (collection == self->pv->collection)
 
1402
                return;
 
1403
 
 
1404
        if (collection)
 
1405
                g_object_ref (collection);
 
1406
        previous = self->pv->collection;
 
1407
        self->pv->collection = collection;
 
1408
 
 
1409
        if (collection)
 
1410
                children = gcr_collection_get_objects (collection);
 
1411
 
 
1412
        if (previous) {
 
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);
 
1416
 
 
1417
                remove_children_from_sequence (self, self->pv->root_sequence,
 
1418
                                               previous, exclude, TRUE);
 
1419
 
 
1420
                g_hash_table_destroy (exclude);
 
1421
                g_object_unref (previous);
 
1422
        }
 
1423
 
 
1424
        if (collection) {
 
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);
 
1429
        }
 
1430
 
 
1431
        g_object_notify (G_OBJECT (self), "collection");
 
1432
}
 
1433
 
 
1434
/**
 
1435
 * gcr_collection_model_object_for_iter:
 
1436
 * @self: The model
 
1437
 * @iter: The row
 
1438
 *
 
1439
 * Get the object that is represented by the given row in the model.
 
1440
 *
 
1441
 * Returns: (transfer none): The object, owned by the model.
 
1442
 */
 
1443
GObject *
 
1444
gcr_collection_model_object_for_iter (GcrCollectionModel *self, const GtkTreeIter *iter)
 
1445
{
 
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);
 
1450
 
 
1451
        return G_OBJECT (iter->user_data);
 
1452
}
 
1453
 
 
1454
/**
 
1455
 * gcr_collection_model_iter_for_object:
 
1456
 * @self: The model
 
1457
 * @object: The object
 
1458
 * @iter: The row for the object
 
1459
 *
 
1460
 * Set @iter to the row for the given object. If the object is not in this
 
1461
 * model, then %FALSE will be returned.
 
1462
 *
 
1463
 * Returns: %TRUE if the object was present.
 
1464
 */
 
1465
gboolean
 
1466
gcr_collection_model_iter_for_object (GcrCollectionModel *self, GObject *object,
 
1467
                                      GtkTreeIter *iter)
 
1468
{
 
1469
        GSequenceIter *seq;
 
1470
 
 
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);
 
1474
 
 
1475
        seq = g_hash_table_lookup (self->pv->object_to_seq, object);
 
1476
        if (seq == NULL)
 
1477
                return FALSE;
 
1478
 
 
1479
        return sequence_iter_to_tree (self, seq, iter);
 
1480
}
 
1481
 
 
1482
/**
 
1483
 * gcr_collection_model_column_for_selected:
 
1484
 * @self: The model
 
1485
 *
 
1486
 * Get the column identifier for the column that contains the values
 
1487
 * of the selected state.
 
1488
 *
 
1489
 * Returns: The column identifier.
 
1490
 */
 
1491
gint
 
1492
gcr_collection_model_column_for_selected (GcrCollectionModel *self)
 
1493
{
 
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;
 
1497
}
 
1498
 
 
1499
/**
 
1500
 * gcr_collection_model_toggle_selected:
 
1501
 * @self: The model
 
1502
 * @iter: The row
 
1503
 *
 
1504
 * Toggle the selected state of a given row.
 
1505
 */
 
1506
void
 
1507
gcr_collection_model_toggle_selected (GcrCollectionModel *self, GtkTreeIter *iter)
 
1508
{
 
1509
        GObject *object;
 
1510
 
 
1511
        g_return_if_fail (GCR_IS_COLLECTION_MODEL (self));
 
1512
 
 
1513
        object = gcr_collection_model_object_for_iter (self, iter);
 
1514
        g_return_if_fail (G_IS_OBJECT (object));
 
1515
 
 
1516
        if (!self->pv->selected)
 
1517
                self->pv->selected = selected_hash_table_new ();
 
1518
 
 
1519
        if (g_hash_table_lookup (self->pv->selected, object))
 
1520
                g_hash_table_remove (self->pv->selected, object);
 
1521
        else
 
1522
                g_hash_table_insert (self->pv->selected, object, object);
 
1523
}
 
1524
 
 
1525
/**
 
1526
 * gcr_collection_model_change_selected:
 
1527
 * @self: The model
 
1528
 * @iter: The row
 
1529
 * @selected: Whether the row should be selected or not.
 
1530
 *
 
1531
 * Set whether a given row is toggled selected or not.
 
1532
 */
 
1533
void
 
1534
gcr_collection_model_change_selected (GcrCollectionModel *self, GtkTreeIter *iter, gboolean selected)
 
1535
{
 
1536
        GtkTreePath *path;
 
1537
        GObject *object;
 
1538
 
 
1539
        g_return_if_fail (GCR_IS_COLLECTION_MODEL (self));
 
1540
 
 
1541
        object = gcr_collection_model_object_for_iter (self, iter);
 
1542
        g_return_if_fail (G_IS_OBJECT (object));
 
1543
 
 
1544
        if (!self->pv->selected)
 
1545
                self->pv->selected = g_hash_table_new (g_direct_hash, g_direct_equal);
 
1546
 
 
1547
        if (selected)
 
1548
                g_hash_table_insert (self->pv->selected, object, object);
 
1549
        else
 
1550
                g_hash_table_remove (self->pv->selected, object);
 
1551
 
 
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);
 
1557
}
 
1558
 
 
1559
/**
 
1560
 * gcr_collection_model_is_selected:
 
1561
 * @self: The model
 
1562
 * @iter: The row
 
1563
 *
 
1564
 * Check whether a given row has been toggled as selected.
 
1565
 *
 
1566
 * Returns: Whether the row has been selected.
 
1567
 */
 
1568
gboolean
 
1569
gcr_collection_model_is_selected (GcrCollectionModel *self, GtkTreeIter *iter)
 
1570
{
 
1571
        GObject *object;
 
1572
 
 
1573
        g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), FALSE);
 
1574
 
 
1575
        object = gcr_collection_model_object_for_iter (self, iter);
 
1576
        g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
 
1577
 
 
1578
        if (!self->pv->selected)
 
1579
                return FALSE;
 
1580
 
 
1581
        return g_hash_table_lookup (self->pv->selected, object) ? TRUE : FALSE;
 
1582
}
 
1583
 
 
1584
/**
 
1585
 * gcr_collection_model_get_selected_objects:
 
1586
 * @self: the collection model
 
1587
 *
 
1588
 * Get a list of checked/selected objects.
 
1589
 *
 
1590
 * Returns: (transfer container) (element-type GLib.Object): a list of selected
 
1591
 *          objects, which should be freed with g_list_free()
 
1592
 */
 
1593
GList *
 
1594
gcr_collection_model_get_selected_objects (GcrCollectionModel *self)
 
1595
{
 
1596
        GHashTableIter iter;
 
1597
        GList *result = NULL;
 
1598
        gpointer key;
 
1599
 
 
1600
        g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), NULL);
 
1601
 
 
1602
        if (!self->pv->selected)
 
1603
                return NULL;
 
1604
 
 
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);
 
1608
        return result;
 
1609
}
 
1610
 
 
1611
/**
 
1612
 * gcr_collection_model_set_selected_objects:
 
1613
 * @self: the collection model
 
1614
 * @selected: (element-type GLib.Object): a list of objects to select
 
1615
 *
 
1616
 * Set the checked/selected objects.
 
1617
 */
 
1618
void
 
1619
gcr_collection_model_set_selected_objects (GcrCollectionModel *self,
 
1620
                                           GList *selected)
 
1621
{
 
1622
        GHashTable *newly_selected;
 
1623
        GList *old_selection;
 
1624
        GtkTreeIter iter;
 
1625
        GList *l;
 
1626
 
 
1627
        old_selection = gcr_collection_model_get_selected_objects (self);
 
1628
        newly_selected = selected_hash_table_new ();
 
1629
 
 
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);
 
1636
                }
 
1637
 
 
1638
                /* Note that we've seen this one */
 
1639
                g_hash_table_insert (newly_selected, l->data, l->data);
 
1640
        }
 
1641
 
 
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);
 
1648
                }
 
1649
        }
 
1650
 
 
1651
        g_list_free (old_selection);
 
1652
        g_hash_table_destroy (newly_selected);
 
1653
}