Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / libedataserverui / e-contact-store.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* e-contact-store.c - Contacts store with GtkTreeModel interface.
4  *
5  * Copyright (C) 2004 Novell, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * Authors: Hans Petter Jansson <hpj@novell.com>
21  */
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <string.h>
28 #include <gtk/gtktreemodel.h>
29 #include <gtk/gtksignal.h>
30 #include <gtk/gtktreednd.h>
31 #include <glib/gi18n-lib.h>
32 #include "e-contact-store.h"
33
34 #define ITER_IS_VALID(contact_store, iter) ((iter)->stamp == (contact_store)->stamp)
35 #define ITER_GET(iter)                     GPOINTER_TO_INT (iter->user_data)
36 #define ITER_SET(contact_store, iter, index)              \
37 G_STMT_START {                                            \
38         (iter)->stamp = (contact_store)->stamp;               \
39         (iter)->user_data = GINT_TO_POINTER (index);        \
40 } G_STMT_END
41
42 static void         e_contact_store_init            (EContactStore      *contact_store);
43 static void         e_contact_store_class_init      (EContactStoreClass *class);
44 static void         e_contact_store_tree_model_init (GtkTreeModelIface  *iface);
45 static void         e_contact_store_finalize        (GObject            *object);
46 static GtkTreeModelFlags e_contact_store_get_flags       (GtkTreeModel       *tree_model);
47 static gint         e_contact_store_get_n_columns   (GtkTreeModel       *tree_model);
48 static GType        e_contact_store_get_column_type (GtkTreeModel       *tree_model,
49                                                      gint                index);
50 static gboolean     e_contact_store_get_iter        (GtkTreeModel       *tree_model,
51                                                      GtkTreeIter        *iter,
52                                                      GtkTreePath        *path);
53 static GtkTreePath *e_contact_store_get_path        (GtkTreeModel       *tree_model,
54                                                      GtkTreeIter        *iter);
55 static void         e_contact_store_get_value       (GtkTreeModel       *tree_model,
56                                                      GtkTreeIter        *iter,
57                                                      gint                column,
58                                                      GValue             *value);
59 static gboolean     e_contact_store_iter_next       (GtkTreeModel       *tree_model,
60                                                      GtkTreeIter        *iter);
61 static gboolean     e_contact_store_iter_children   (GtkTreeModel       *tree_model,
62                                                      GtkTreeIter        *iter,
63                                                      GtkTreeIter        *parent);
64 static gboolean     e_contact_store_iter_has_child  (GtkTreeModel       *tree_model,
65                                                      GtkTreeIter        *iter);
66 static gint         e_contact_store_iter_n_children (GtkTreeModel       *tree_model,
67                                                      GtkTreeIter        *iter);
68 static gboolean     e_contact_store_iter_nth_child  (GtkTreeModel       *tree_model,
69                                                      GtkTreeIter        *iter,
70                                                      GtkTreeIter        *parent,
71                                                      gint                n);
72 static gboolean     e_contact_store_iter_parent     (GtkTreeModel       *tree_model,
73                                                      GtkTreeIter        *iter,
74                                                      GtkTreeIter        *child);
75
76 typedef struct
77 {
78         EBook     *book;
79
80         EBookView *book_view;
81         GPtrArray *contacts;
82
83         EBookView *book_view_pending;
84         GPtrArray *contacts_pending;
85 }
86 ContactSource;
87
88 static void free_contact_ptrarray (GPtrArray *contacts);
89 static void clear_contact_source  (EContactStore *contact_store, ContactSource *source);
90 static void stop_view             (EContactStore *contact_store, EBookView *view);
91
92 /* ------------------ *
93  * Class/object setup *
94  * ------------------ */
95
96 static GObjectClass *parent_class = NULL;
97
98 GType
99 e_contact_store_get_type (void)
100 {
101         static GType contact_store_type = 0;
102
103         if (!contact_store_type) {
104                 static const GTypeInfo contact_store_info =
105                 {
106                         sizeof (EContactStoreClass),
107                         NULL,           /* base_init */
108                         NULL,           /* base_finalize */
109                         (GClassInitFunc) e_contact_store_class_init,
110                         NULL,           /* class_finalize */
111                         NULL,           /* class_data */
112                         sizeof (EContactStore),
113                         0,
114                         (GInstanceInitFunc) e_contact_store_init,
115                 };
116
117                 static const GInterfaceInfo tree_model_info =
118                 {
119                         (GInterfaceInitFunc) e_contact_store_tree_model_init,
120                         NULL,
121                         NULL
122                 };
123
124                 contact_store_type = g_type_register_static (G_TYPE_OBJECT, "EContactStore",
125                                                              &contact_store_info, 0);
126                 g_type_add_interface_static (contact_store_type,
127                                              GTK_TYPE_TREE_MODEL,
128                                              &tree_model_info);
129         }
130
131         return contact_store_type;
132 }
133
134 static void
135 e_contact_store_class_init (EContactStoreClass *class)
136 {
137         GObjectClass *object_class;
138
139         parent_class = g_type_class_peek_parent (class);
140         object_class = (GObjectClass *) class;
141
142         object_class->finalize = e_contact_store_finalize;
143 }
144
145 static void
146 e_contact_store_tree_model_init (GtkTreeModelIface *iface)
147 {
148         iface->get_flags       = e_contact_store_get_flags;
149         iface->get_n_columns   = e_contact_store_get_n_columns;
150         iface->get_column_type = e_contact_store_get_column_type;
151         iface->get_iter        = e_contact_store_get_iter;
152         iface->get_path        = e_contact_store_get_path;
153         iface->get_value       = e_contact_store_get_value;
154         iface->iter_next       = e_contact_store_iter_next;
155         iface->iter_children   = e_contact_store_iter_children;
156         iface->iter_has_child  = e_contact_store_iter_has_child;
157         iface->iter_n_children = e_contact_store_iter_n_children;
158         iface->iter_nth_child  = e_contact_store_iter_nth_child;
159         iface->iter_parent     = e_contact_store_iter_parent;
160 }
161
162 static void
163 e_contact_store_init (EContactStore *contact_store)
164 {
165         contact_store->stamp           = g_random_int ();
166         contact_store->query           = NULL;
167         contact_store->contact_sources = g_array_new (FALSE, FALSE, sizeof (ContactSource));
168 }
169
170 static void
171 e_contact_store_finalize (GObject *object)
172 {
173         EContactStore *contact_store = E_CONTACT_STORE (object);
174         gint           i;
175
176         /* Free sources and cached contacts */
177
178         for (i = 0; i < contact_store->contact_sources->len; i++) {
179                 ContactSource *source = &g_array_index (contact_store->contact_sources, ContactSource, i);
180
181                 clear_contact_source (contact_store, source);
182
183                 free_contact_ptrarray (source->contacts);
184                 g_object_unref (source->book);
185         }
186
187         g_array_free (contact_store->contact_sources, TRUE);
188         if (contact_store->query)
189                 e_book_query_unref (contact_store->query);
190
191         if (G_OBJECT_CLASS (parent_class)->finalize)
192                 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
193 }
194
195 /**
196  * e_contact_store_new:
197  *
198  * Creates a new #EContactStore.
199  *
200  * Return value: A new #EContactStore.
201  **/
202 EContactStore *
203 e_contact_store_new (void)
204 {
205         return E_CONTACT_STORE (g_object_new (E_TYPE_CONTACT_STORE, NULL));
206 }
207
208 /* ------------------ *
209  * Row update helpers *
210  * ------------------ */
211
212 static void
213 row_deleted (EContactStore *contact_store, gint n)
214 {
215         GtkTreePath *path;
216
217         path = gtk_tree_path_new ();
218         gtk_tree_path_append_index (path, n);
219         gtk_tree_model_row_deleted (GTK_TREE_MODEL (contact_store), path);
220         gtk_tree_path_free (path);
221 }
222
223 static void
224 row_inserted (EContactStore *contact_store, gint n)
225 {
226         GtkTreePath *path;
227         GtkTreeIter  iter;
228
229         path = gtk_tree_path_new ();
230         gtk_tree_path_append_index (path, n);
231
232         if (gtk_tree_model_get_iter (GTK_TREE_MODEL (contact_store), &iter, path))
233                 gtk_tree_model_row_inserted (GTK_TREE_MODEL (contact_store), path, &iter);
234
235         gtk_tree_path_free (path);
236 }
237
238 static void
239 row_changed (EContactStore *contact_store, gint n)
240 {
241         GtkTreePath *path;
242         GtkTreeIter  iter;
243
244         path = gtk_tree_path_new ();
245         gtk_tree_path_append_index (path, n);
246
247         if (gtk_tree_model_get_iter (GTK_TREE_MODEL (contact_store), &iter, path))
248                 gtk_tree_model_row_changed (GTK_TREE_MODEL (contact_store), path, &iter);
249
250         gtk_tree_path_free (path);
251 }
252
253 /* ---------------------- *
254  * Contact source helpers *
255  * ---------------------- */
256
257 static gint
258 find_contact_source_by_book (EContactStore *contact_store, EBook *book)
259 {
260         gint i;
261
262         for (i = 0; i < contact_store->contact_sources->len; i++) {
263                 ContactSource *source;
264
265                 source = &g_array_index (contact_store->contact_sources, ContactSource, i);
266                 if (source->book == book)
267                         return i;
268         }
269
270         return -1;
271 }
272
273 EBookView *
274 find_contact_source_by_book_return_view(EContactStore *contact_store, EBook *book)
275 {
276         gint i;
277
278         ContactSource *source = NULL;
279
280         for(i = 0; i < contact_store->contact_sources->len; i++) {
281                 source = &g_array_index (contact_store->contact_sources, ContactSource, i);
282                 if (source->book == book)
283                         break;
284         }
285         return source->book_view;;
286 }
287
288 static gint
289 find_contact_source_by_view (EContactStore *contact_store, EBookView *book_view)
290 {
291         gint i;
292
293         for (i = 0; i < contact_store->contact_sources->len; i++) {
294                 ContactSource *source;
295
296                 source = &g_array_index (contact_store->contact_sources, ContactSource, i);
297                 if (source->book_view         == book_view ||
298                     source->book_view_pending == book_view)
299                         return i;
300         }
301
302         return -1;
303 }
304
305 static gint
306 find_contact_source_by_offset (EContactStore *contact_store, gint offset)
307 {
308         gint i;
309
310         for (i = 0; i < contact_store->contact_sources->len; i++) {
311                 ContactSource *source;
312
313                 source = &g_array_index (contact_store->contact_sources, ContactSource, i);
314                 if (source->contacts->len > offset)
315                         return i;
316
317                 offset -= source->contacts->len;
318         }
319
320         return -1;
321 }
322
323 static gint
324 find_contact_source_by_pointer (EContactStore *contact_store, ContactSource *source)
325 {
326         gint i;
327
328         i = ((gchar *) source - (gchar *) contact_store->contact_sources->data) / sizeof (ContactSource);
329
330         if (i < 0 || i >= contact_store->contact_sources->len)
331                 return -1;
332
333         return i;
334 }
335
336 static gint
337 get_contact_source_offset (EContactStore *contact_store, gint contact_source_index)
338 {
339         gint offset = 0;
340         gint i;
341
342         g_assert (contact_source_index < contact_store->contact_sources->len);
343
344         for (i = 0; i < contact_source_index; i++) {
345                 ContactSource *source;
346
347                 source = &g_array_index (contact_store->contact_sources, ContactSource, i);
348                 offset += source->contacts->len;
349         }
350
351         return offset;
352 }
353
354 static gint
355 count_contacts (EContactStore *contact_store)
356 {
357         gint count = 0;
358         gint i;
359
360         for (i = 0; i < contact_store->contact_sources->len; i++) {
361                 ContactSource *source;
362
363                 source = &g_array_index (contact_store->contact_sources, ContactSource, i);
364                 count += source->contacts->len;
365         }
366
367         return count;
368 }
369
370 static gint
371 find_contact_by_view_and_uid (EContactStore *contact_store, EBookView *find_view, const gchar *find_uid)
372 {
373         gint           source_index;
374         ContactSource *source;
375         GPtrArray     *contacts;
376         gint           i;
377
378         source_index = find_contact_source_by_view (contact_store, find_view);
379         if (source_index < 0)
380                 return -1;
381
382         source = &g_array_index (contact_store->contact_sources, ContactSource, source_index);
383
384         if (find_view == source->book_view)
385                 contacts = source->contacts;          /* Current view */
386         else
387                 contacts = source->contacts_pending;  /* Pending view */
388
389         for (i = 0; i < contacts->len; i++) {
390                 EContact    *contact = g_ptr_array_index (contacts, i);
391                 const gchar *uid     = e_contact_get_const (contact, E_CONTACT_UID);
392
393                 if (!strcmp (find_uid, uid))
394                         return i;
395         }
396
397         return -1;
398 }
399
400 static gint
401 find_contact_by_uid (EContactStore *contact_store, const gchar *find_uid)
402 {
403         gint i;
404
405         for (i = 0; i < contact_store->contact_sources->len; i++) {
406                 ContactSource *source = &g_array_index (contact_store->contact_sources, ContactSource, i);
407                 gint           j;
408
409                 for (j = 0; j < source->contacts->len; j++) {
410                         EContact    *contact = g_ptr_array_index (source->contacts, j);
411                         const gchar *uid     = e_contact_get_const (contact, E_CONTACT_UID);
412
413                         if (!strcmp (find_uid, uid))
414                                 return get_contact_source_offset (contact_store, i) + j;
415                 }
416         }
417
418         return -1;
419 }
420
421 static EBook *
422 get_book_at_row (EContactStore *contact_store, gint row)
423 {
424         ContactSource *source;
425         gint           source_index;
426
427         source_index = find_contact_source_by_offset (contact_store, row);
428         if (source_index < 0)
429                 return NULL;
430
431         source = &g_array_index (contact_store->contact_sources, ContactSource, source_index);
432         return source->book;
433 }
434
435 static EContact *
436 get_contact_at_row (EContactStore *contact_store, gint row)
437 {
438         ContactSource *source;
439         gint           source_index;
440         gint           offset;
441
442         source_index = find_contact_source_by_offset (contact_store, row);
443         if (source_index < 0)
444                 return NULL;
445
446         source = &g_array_index (contact_store->contact_sources, ContactSource, source_index);
447         offset = get_contact_source_offset (contact_store, source_index);
448         row -= offset;
449
450         g_assert (row < source->contacts->len);
451
452         return g_ptr_array_index (source->contacts, row);
453 }
454
455 static gboolean
456 find_contact_source_details_by_view (EContactStore *contact_store, EBookView *book_view,
457                                      ContactSource **contact_source, gint *offset)
458 {
459         gint           source_index;
460
461         source_index = find_contact_source_by_view (contact_store, book_view);
462         if (source_index < 0)
463                 return FALSE;
464
465         *contact_source = &g_array_index (contact_store->contact_sources, ContactSource, source_index);
466         *offset = get_contact_source_offset (contact_store, source_index);
467
468         return TRUE;
469 }
470
471 /* ------------------------- *
472  * EBookView signal handlers *
473  * ------------------------- */
474
475 static void
476 view_contacts_added (EContactStore *contact_store, const GList *contacts, EBookView *book_view)
477 {
478         ContactSource *source;
479         gint           offset;
480         const GList   *l;
481
482         if (!find_contact_source_details_by_view (contact_store, book_view, &source, &offset)) {
483                 g_warning ("EContactStore got 'contacts_added' signal from unknown EBookView!");
484                 return;
485         }
486
487         for (l = contacts; l; l = g_list_next (l)) {
488                 EContact *contact = l->data;
489
490                 g_object_ref (contact);
491
492                 if (book_view == source->book_view) {
493                         /* Current view */
494                         g_ptr_array_add (source->contacts, contact);
495                         row_inserted (contact_store, offset + source->contacts->len - 1);
496                 } else {
497                         /* Pending view */
498                         g_ptr_array_add (source->contacts_pending, contact);
499                 }
500         }
501 }
502
503 static void
504 view_contacts_removed (EContactStore *contact_store, const GList *uids, EBookView *book_view)
505 {
506         ContactSource *source;
507         gint           offset;
508         const GList   *l;
509
510         if (!find_contact_source_details_by_view (contact_store, book_view, &source, &offset)) {
511                 g_warning ("EContactStore got 'contacts_removed' signal from unknown EBookView!");
512                 return;
513         }
514
515         for (l = uids; l; l = g_list_next (l)) {
516                 const gchar *uid = l->data;
517                 gint         n   = find_contact_by_view_and_uid (contact_store, book_view, uid);
518                 EContact    *contact;
519
520                 if (n < 0) {
521                         g_warning ("EContactStore got 'contacts_removed' on unknown contact!");
522                         continue;
523                 }
524
525                 if (book_view == source->book_view) {
526                         /* Current view */
527                         contact = g_ptr_array_index (source->contacts, n);
528                         g_object_unref (contact);
529                         g_ptr_array_remove_index (source->contacts, n);
530                         row_deleted (contact_store, offset + n);
531                 } else {
532                         /* Pending view */
533                         contact = g_ptr_array_index (source->contacts_pending, n);
534                         g_object_unref (contact);
535                         g_ptr_array_remove_index (source->contacts_pending, n);
536                 }
537         }
538 }
539
540 static void
541 view_contacts_changed (EContactStore *contact_store, const GList *contacts, EBookView *book_view)
542 {
543         GPtrArray     *cached_contacts;
544         ContactSource *source;
545         gint           offset;
546         const GList   *l;
547
548         if (!find_contact_source_details_by_view (contact_store, book_view, &source, &offset)) {
549                 g_warning ("EContactStore got 'contacts_changed' signal from unknown EBookView!");
550                 return;
551         }
552
553         if (book_view == source->book_view)
554                 cached_contacts = source->contacts;
555         else
556                 cached_contacts = source->contacts_pending;
557
558         for (l = contacts; l; l = g_list_next (l)) {
559                 EContact    *cached_contact;
560                 EContact    *contact = l->data;
561                 const gchar *uid     = e_contact_get_const (contact, E_CONTACT_UID);
562                 gint         n       = find_contact_by_view_and_uid (contact_store, book_view, uid);
563
564                 if (n < 0) {
565                         g_warning ("EContactStore got change notification on unknown contact!");
566                         continue;
567                 }
568
569                 cached_contact = g_ptr_array_index (cached_contacts, n);
570
571                 /* Update cached contact */
572                 if (cached_contact != contact) {
573                         g_object_unref (cached_contact);
574                         cached_contacts->pdata [n] = g_object_ref (contact);
575                 }
576
577                 /* Emit changes for current view only */
578                 if (book_view == source->book_view)
579                         row_changed (contact_store, offset + n);
580         }
581 }
582
583 static void
584 view_sequence_complete (EContactStore *contact_store, EBookViewStatus status, EBookView *book_view)
585 {
586         ContactSource *source;
587         gint           offset;
588         gint           i;
589
590         if (!find_contact_source_details_by_view (contact_store, book_view, &source, &offset)) {
591                 g_warning ("EContactStore got 'sequence_complete' signal from unknown EBookView!");
592                 return;
593         }
594
595         /* If current view finished, do nothing */
596         if (book_view == source->book_view) {
597                 stop_view (contact_store, source->book_view);
598                 return;
599         }
600
601         g_assert (book_view == source->book_view_pending);
602
603         /* However, if it was a pending view, calculate and emit the differences between that
604          * and the current view, and move the pending view up to current.
605          * 
606          * This is O(m * n), and can be sped up with a temporary hash table if needed. */
607
608         /* Deletions */
609         for (i = 0; i < source->contacts->len; i++) {
610                 EContact    *old_contact = g_ptr_array_index (source->contacts, i);
611                 const gchar *old_uid     = e_contact_get_const (old_contact, E_CONTACT_UID);
612                 gint         result;
613
614                 result = find_contact_by_view_and_uid (contact_store, source->book_view_pending, old_uid);
615                 if (result < 0) {
616                         /* Contact is not in new view; removed */
617                         g_object_unref (old_contact);
618                         g_ptr_array_remove_index (source->contacts, i);
619                         row_deleted (contact_store, offset + i);
620                         i--;  /* Stay in place */
621                 }
622         }
623
624         /* Insertions */
625         for (i = 0; i < source->contacts_pending->len; i++) {
626                 EContact    *new_contact = g_ptr_array_index (source->contacts_pending, i);
627                 const gchar *new_uid     = e_contact_get_const (new_contact, E_CONTACT_UID);
628                 gint         result;
629
630                 result = find_contact_by_view_and_uid (contact_store, source->book_view, new_uid);
631                 if (result < 0) {
632                         /* Contact is not in old view; inserted */
633                         g_ptr_array_add (source->contacts, new_contact);
634                         row_inserted (contact_store, offset + source->contacts->len - 1);
635                 } else {
636                         /* Contact already in old view; drop the new one */
637                         g_object_unref (new_contact);
638                 }
639         }
640
641         /* Move pending view up to current */
642         stop_view (contact_store, source->book_view);
643         g_object_unref (source->book_view);
644         source->book_view = source->book_view_pending;
645         source->book_view_pending = NULL;
646
647         /* Free array of pending contacts (members have been either moved or unreffed) */
648         g_ptr_array_free (source->contacts_pending, TRUE);
649         source->contacts_pending = NULL;
650 }
651
652 /* --------------------- *
653  * View/Query management *
654  * --------------------- */
655
656 static void
657 start_view (EContactStore *contact_store, EBookView *view)
658 {
659         g_signal_connect_swapped (view, "contacts_added",
660                                   G_CALLBACK (view_contacts_added), contact_store);
661         g_signal_connect_swapped (view, "contacts_removed",
662                                   G_CALLBACK (view_contacts_removed), contact_store);
663         g_signal_connect_swapped (view, "contacts_changed",
664                                   G_CALLBACK (view_contacts_changed), contact_store);
665         g_signal_connect_swapped (view, "sequence_complete",
666                                   G_CALLBACK (view_sequence_complete), contact_store);
667
668         e_book_view_start (view);
669 }
670
671 static void
672 stop_view (EContactStore *contact_store, EBookView *view)
673 {
674         e_book_view_stop (view);
675
676         g_signal_handlers_disconnect_matched (view, G_SIGNAL_MATCH_DATA,
677                                               0, 0, NULL, NULL, contact_store);
678 }
679
680 static void
681 clear_contact_ptrarray (GPtrArray *contacts)
682 {
683         gint i;
684
685         for (i = 0; i < contacts->len; i++) {
686                 EContact *contact = g_ptr_array_index (contacts, i);
687                 g_object_unref (contact);
688         }
689
690         g_ptr_array_set_size (contacts, 0);
691 }
692
693 static void
694 free_contact_ptrarray (GPtrArray *contacts)
695 {
696         clear_contact_ptrarray (contacts);
697         g_ptr_array_free (contacts, TRUE);
698 }
699
700 static void
701 clear_contact_source (EContactStore *contact_store, ContactSource *source)
702 {
703         gint source_index;
704         gint offset;
705
706         source_index = find_contact_source_by_pointer (contact_store, source);
707         g_assert (source_index >= 0);
708
709         offset = get_contact_source_offset (contact_store, source_index);
710         g_assert (offset >= 0);
711
712         /* Inform listeners that contacts went away */
713
714         if (source->contacts && source->contacts->len > 0) {
715                 GtkTreePath *path = gtk_tree_path_new ();
716                 gint         i;
717
718                 gtk_tree_path_append_index (path, source->contacts->len);
719
720                 for (i = source->contacts->len - 1; i >= 0; i--) {
721                         EContact *contact = g_ptr_array_index (source->contacts, i);
722
723                         g_object_unref (contact);
724                         g_ptr_array_remove_index_fast (source->contacts, i);
725
726                         gtk_tree_path_prev (path);
727                         gtk_tree_model_row_deleted (GTK_TREE_MODEL (contact_store), path);
728                 }
729
730                 gtk_tree_path_free (path);
731         }
732
733         /* Free main and pending views, clear cached contacts */
734
735         if (source->book_view) {
736                 stop_view (contact_store, source->book_view);
737                 g_object_unref (source->book_view);
738
739                 source->book_view = NULL;
740         }
741
742         if (source->book_view_pending) {
743                 stop_view (contact_store, source->book_view_pending);
744                 g_object_unref (source->book_view_pending);
745                 free_contact_ptrarray (source->contacts_pending);
746
747                 source->book_view_pending = NULL;
748                 source->contacts_pending  = NULL;
749         }
750 }
751
752 static void
753 query_contact_source (EContactStore *contact_store, ContactSource *source)
754 {
755         EBookView *view;
756
757         g_assert (source->book != NULL);
758
759         if (!contact_store->query) {
760                 clear_contact_source (contact_store, source);
761                 return;
762         }
763
764         if (!e_book_is_opened (source->book) ||
765             !e_book_get_book_view (source->book, contact_store->query, NULL, -1, &view, NULL))
766                 view = NULL;
767
768         if (source->book_view) {
769                 if (source->book_view_pending) {
770                         stop_view (contact_store, source->book_view_pending);
771                         g_object_unref (source->book_view_pending);
772                         free_contact_ptrarray (source->contacts_pending);
773                 }
774
775                 source->book_view_pending = view;
776
777                 if (source->book_view_pending) {
778                         source->contacts_pending = g_ptr_array_new ();
779                         start_view (contact_store, view);
780                 } else {
781                         source->contacts_pending = NULL;
782                 }
783         } else {
784                 source->book_view = view;
785
786                 if (source->book_view) {
787                         start_view (contact_store, view);
788                 }
789         }
790 }
791
792 /* ----------------- *
793  * EContactStore API *
794  * ----------------- */
795
796 /**
797  * e_contact_store_get_book:
798  * @contact_store: an #EContactStore
799  * @iter: a #GtkTreeIter from @contact_store
800  *
801  * Gets the #EBook that provided the contact at @iter.
802  *
803  * Return value: An #EBook.
804  **/
805 EBook *
806 e_contact_store_get_book (EContactStore *contact_store, GtkTreeIter *iter)
807 {
808         gint index;
809
810         g_return_val_if_fail (E_IS_CONTACT_STORE (contact_store), NULL);
811         g_return_val_if_fail (ITER_IS_VALID (contact_store, iter), NULL);
812
813         index = ITER_GET (iter);
814
815         return get_book_at_row (contact_store, index);
816 }
817
818 /**
819  * e_contact_store_get_contact:
820  * @contact_store: an #EContactStore
821  * @iter: a #GtkTreeIter from @contact_store
822  *
823  * Gets the #EContact at @iter.
824  *
825  * Return value: An #EContact.
826  **/
827 EContact *
828 e_contact_store_get_contact (EContactStore *contact_store, GtkTreeIter *iter)
829 {
830         gint index;
831
832         g_return_val_if_fail (E_IS_CONTACT_STORE (contact_store), NULL);
833         g_return_val_if_fail (ITER_IS_VALID (contact_store, iter), NULL);
834
835         index = ITER_GET (iter);
836
837         return get_contact_at_row (contact_store, index);
838 }
839
840 /**
841  * e_contact_store_find_contact:
842  * @contact_store: an #EContactStore
843  * @uid: a unique contact identifier
844  * @iter: a destination #GtkTreeIter to set
845  *
846  * Sets @iter to point to the contact row matching @uid.
847  * 
848  * Return value: %TRUE if the contact was found, and @iter was set. %FALSE otherwise.
849  **/
850 gboolean
851 e_contact_store_find_contact (EContactStore *contact_store, const gchar *uid,
852                               GtkTreeIter *iter)
853 {
854         gint index;
855
856         g_return_val_if_fail (E_IS_CONTACT_STORE (contact_store), FALSE);
857         g_return_val_if_fail (uid != NULL, FALSE);
858
859         index = find_contact_by_uid (contact_store, uid);
860         if (index < 0)
861                 return FALSE;
862
863         ITER_SET (contact_store, iter, index);
864         return TRUE;
865 }
866
867 /**
868  * e_contact_store_get_books:
869  * @contact_store: an #EContactStore
870  *
871  * Gets the list of books that provide contacts for @contact_store.
872  *
873  * Return value: A #GList of pointers to #EBook. The caller owns the list,
874  * but not the books.
875  **/
876 GList *
877 e_contact_store_get_books (EContactStore *contact_store)
878 {
879         GList *book_list = NULL;
880         gint   i;
881
882         g_return_val_if_fail (E_IS_CONTACT_STORE (contact_store), NULL);
883
884         for (i = 0; i < contact_store->contact_sources->len; i++) {
885                 ContactSource *source;
886
887                 source = &g_array_index (contact_store->contact_sources, ContactSource, i);
888                 book_list = g_list_prepend (book_list, source->book);
889         }
890
891         return book_list;
892 }
893
894 /**
895  * e_contact_store_add_book:
896  * @contact_store: an #EContactStore
897  * @book: an #EBook
898  *
899  * Adds @book to the list of books that provide contacts for @contact_store.
900  **/
901 void
902 e_contact_store_add_book (EContactStore *contact_store, EBook *book)
903 {
904         ContactSource  source;
905         ContactSource *indexed_source;
906
907         g_return_if_fail (E_IS_CONTACT_STORE (contact_store));
908         g_return_if_fail (E_IS_BOOK (book));
909
910         if (find_contact_source_by_book (contact_store, book) >= 0) {
911                 g_warning ("Same book added more than once to EContactStore!");
912                 return;
913         }
914
915         memset (&source, 0, sizeof (ContactSource));
916         source.book     = g_object_ref (book);
917         source.contacts = g_ptr_array_new ();
918         g_array_append_val (contact_store->contact_sources, source);
919
920         indexed_source = &g_array_index (contact_store->contact_sources, ContactSource,
921                                          contact_store->contact_sources->len - 1);
922
923         query_contact_source (contact_store, indexed_source);
924 }
925
926 /**
927  * e_contact_store_remove_book:
928  * @contact_store: an #EContactStore
929  * @book: an #EBook
930  *
931  * Removes @book from the list of books that provide contacts for @contact_store.
932  **/
933 void
934 e_contact_store_remove_book (EContactStore *contact_store, EBook *book)
935 {
936         ContactSource *source;
937         gint           source_index;
938
939         g_return_if_fail (E_IS_CONTACT_STORE (contact_store));
940         g_return_if_fail (E_IS_BOOK (book));
941
942         source_index = find_contact_source_by_book (contact_store, book);
943         if (source_index < 0) {
944                 g_warning ("Tried to remove unknown book from EContactStore!");
945                 return;
946         }
947
948         source = &g_array_index (contact_store->contact_sources, ContactSource, source_index);
949         clear_contact_source (contact_store, source);
950         free_contact_ptrarray (source->contacts);
951         g_object_unref (book);
952
953         g_array_remove_index (contact_store->contact_sources, source_index);  /* Preserve order */
954 }
955
956 /**
957  * e_contact_store_set_query:
958  * @contact_store: an #EContactStore
959  * @book_query: an #EBookQuery
960  *
961  * Sets @book_query to be the query used to fetch contacts from the books
962  * assigned to @contact_store.
963  **/
964 void
965 e_contact_store_set_query (EContactStore *contact_store, EBookQuery *book_query)
966 {
967         gint i;
968
969         g_return_if_fail (E_IS_CONTACT_STORE (contact_store));
970
971         if (book_query == contact_store->query)
972                 return;
973
974         if (contact_store->query)
975                 e_book_query_unref (contact_store->query);
976
977         contact_store->query = book_query;
978         if (book_query)
979                 e_book_query_ref (book_query);
980
981         /* Query books */
982         for (i = 0; i < contact_store->contact_sources->len; i++) {
983                 ContactSource *contact_source;
984
985                 contact_source = &g_array_index (contact_store->contact_sources, ContactSource, i);
986                 query_contact_source (contact_store, contact_source);
987         }
988 }
989
990 /**
991  * e_contact_store_peek_query:
992  * @contact_store: an #EContactStore
993  *
994  * Gets the query that's being used to fetch contacts from the books
995  * assigned to @contact_store.
996  *
997  * Return value: The #EBookQuery being used.
998  **/
999 EBookQuery *
1000 e_contact_store_peek_query (EContactStore *contact_store)
1001 {
1002         g_return_val_if_fail (E_IS_CONTACT_STORE (contact_store), NULL);
1003
1004         return contact_store->query;
1005 }
1006
1007 /* ---------------- *
1008  * GtkTreeModel API *
1009  * ---------------- */
1010
1011 static GtkTreeModelFlags
1012 e_contact_store_get_flags (GtkTreeModel *tree_model)
1013 {
1014         g_return_val_if_fail (E_IS_CONTACT_STORE (tree_model), 0);
1015
1016         return GTK_TREE_MODEL_LIST_ONLY;
1017 }
1018
1019 static gint
1020 e_contact_store_get_n_columns (GtkTreeModel *tree_model)
1021 {
1022         g_return_val_if_fail (E_IS_CONTACT_STORE (tree_model), 0);
1023
1024         return E_CONTACT_FIELD_LAST;
1025 }
1026
1027 static GType
1028 get_column_type (EContactStore *contact_store, gint column)
1029 {
1030         const gchar  *field_name;
1031         GObjectClass *contact_class;
1032         GParamSpec   *param_spec;
1033         GType         value_type;
1034
1035         /* Silently suppress requests for columns lower than the first EContactField.
1036          * GtkTreeView automatically queries the type of all columns up to the maximum
1037          * provided, and we have to return a valid value type, so let it be a generic
1038          * pointer. */
1039         if (column < E_CONTACT_FIELD_FIRST) {
1040                 return G_TYPE_POINTER;
1041         }
1042
1043         field_name = e_contact_field_name (column);
1044         contact_class = g_type_class_ref (E_TYPE_CONTACT);
1045         param_spec = g_object_class_find_property (contact_class, field_name);
1046         value_type = G_PARAM_SPEC_VALUE_TYPE (param_spec);
1047         g_type_class_unref (contact_class);
1048
1049         return value_type;
1050 }
1051
1052 static GType
1053 e_contact_store_get_column_type (GtkTreeModel *tree_model,
1054                                  gint          index)
1055 {
1056         EContactStore *contact_store = E_CONTACT_STORE (tree_model);
1057
1058         g_return_val_if_fail (E_IS_CONTACT_STORE (tree_model), G_TYPE_INVALID);
1059         g_return_val_if_fail (index >= 0 && index < E_CONTACT_FIELD_LAST, G_TYPE_INVALID);
1060
1061         return get_column_type (contact_store, index);
1062 }
1063
1064 static gboolean
1065 e_contact_store_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path)
1066 {
1067         EContactStore *contact_store = E_CONTACT_STORE (tree_model);
1068         gint           index;
1069
1070         g_return_val_if_fail (E_IS_CONTACT_STORE (tree_model), FALSE);
1071         g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
1072
1073         index = gtk_tree_path_get_indices (path)[0];
1074         if (index >= count_contacts (contact_store))
1075                 return FALSE;
1076
1077         ITER_SET (contact_store, iter, index);
1078         return TRUE;
1079 }
1080
1081 static GtkTreePath *
1082 e_contact_store_get_path (GtkTreeModel *tree_model,
1083                           GtkTreeIter  *iter)
1084 {
1085         EContactStore *contact_store = E_CONTACT_STORE (tree_model);
1086         GtkTreePath   *path;
1087         gint           index;
1088
1089         g_return_val_if_fail (E_IS_CONTACT_STORE (tree_model), NULL);
1090         g_return_val_if_fail (ITER_IS_VALID (contact_store, iter), NULL);
1091
1092         index = ITER_GET (iter);
1093         path = gtk_tree_path_new ();
1094         gtk_tree_path_append_index (path, index);
1095
1096         return path;
1097 }
1098
1099 static gboolean
1100 e_contact_store_iter_next (GtkTreeModel  *tree_model,
1101                            GtkTreeIter   *iter)
1102 {
1103         EContactStore *contact_store = E_CONTACT_STORE (tree_model);
1104         gint           index;
1105
1106         g_return_val_if_fail (E_IS_CONTACT_STORE (tree_model), FALSE);
1107         g_return_val_if_fail (ITER_IS_VALID (contact_store, iter), FALSE);
1108
1109         index = ITER_GET (iter);
1110
1111         if (index + 1 < count_contacts (contact_store)) {
1112                 ITER_SET (contact_store, iter, index + 1);
1113                 return TRUE;
1114         }
1115
1116         return FALSE;
1117 }
1118
1119 static gboolean
1120 e_contact_store_iter_children (GtkTreeModel *tree_model,
1121                                GtkTreeIter  *iter,
1122                                GtkTreeIter  *parent)
1123 {
1124         EContactStore *contact_store = E_CONTACT_STORE (tree_model);
1125
1126         g_return_val_if_fail (E_IS_CONTACT_STORE (tree_model), FALSE);
1127
1128         /* This is a list, nodes have no children. */
1129         if (parent)
1130                 return FALSE;
1131
1132         /* But if parent == NULL we return the list itself as children of the root. */
1133         if (count_contacts (contact_store) <= 0)
1134                 return FALSE;
1135
1136         ITER_SET (contact_store, iter, 0);
1137         return TRUE;
1138 }
1139
1140 static gboolean
1141 e_contact_store_iter_has_child (GtkTreeModel *tree_model,
1142                                 GtkTreeIter  *iter)
1143 {
1144         g_return_val_if_fail (E_IS_CONTACT_STORE (tree_model), FALSE);
1145
1146         if (iter == NULL)
1147                 return TRUE;
1148
1149         return FALSE;
1150 }
1151
1152 static gint
1153 e_contact_store_iter_n_children (GtkTreeModel *tree_model,
1154                                  GtkTreeIter  *iter)
1155 {
1156         EContactStore *contact_store = E_CONTACT_STORE (tree_model);
1157
1158         g_return_val_if_fail (E_IS_CONTACT_STORE (tree_model), -1);
1159
1160         if (iter == NULL)
1161                 return count_contacts (contact_store);
1162
1163         g_return_val_if_fail (ITER_IS_VALID (contact_store, iter), -1);
1164         return 0;
1165 }
1166
1167 static gboolean
1168 e_contact_store_iter_nth_child (GtkTreeModel *tree_model,
1169                                 GtkTreeIter  *iter,
1170                                 GtkTreeIter  *parent,
1171                                 gint          n)
1172 {
1173         EContactStore *contact_store = E_CONTACT_STORE (tree_model);
1174
1175         g_return_val_if_fail (E_IS_CONTACT_STORE (tree_model), FALSE);
1176
1177         if (parent)
1178                 return FALSE;
1179
1180         if (n < count_contacts (contact_store)) {
1181                 ITER_SET (contact_store, iter, n);
1182                 return TRUE;
1183         }
1184
1185         return FALSE;
1186 }
1187
1188 static gboolean
1189 e_contact_store_iter_parent (GtkTreeModel *tree_model,
1190                              GtkTreeIter  *iter,
1191                              GtkTreeIter  *child)
1192 {
1193         return FALSE;
1194 }
1195
1196 static void
1197 e_contact_store_get_value (GtkTreeModel *tree_model,
1198                            GtkTreeIter  *iter,
1199                            gint          column,
1200                            GValue       *value)
1201 {
1202         EContactStore *contact_store = E_CONTACT_STORE (tree_model);
1203         EContact      *contact;
1204         const gchar   *field_name;
1205         gint           row;
1206
1207         g_return_if_fail (E_IS_CONTACT_STORE (tree_model));
1208         g_return_if_fail (column < E_CONTACT_FIELD_LAST);
1209         g_return_if_fail (ITER_IS_VALID (contact_store, iter));
1210
1211         g_value_init (value, get_column_type (contact_store, column));
1212
1213         row = ITER_GET (iter);
1214         contact = get_contact_at_row (contact_store, row);
1215         if (!contact || column < E_CONTACT_FIELD_FIRST)
1216                 return;
1217
1218         field_name = e_contact_field_name (column);
1219         g_object_get_property (G_OBJECT (contact), field_name, value);
1220 }