Add e_book_client_view_ref_client().
[platform/upstream/evolution-data-server.git] / addressbook / libebook / e-book-client-view.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  * Copyright (C) 2006 OpenedHand Ltd
5  * Copyright (C) 2009 Intel Corporation
6  *
7  * This library is free software; you can redistribute it and/or modify it under
8  * the terms of version 2.1 of the GNU Lesser General Public License as
9  * published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  *
20  * Author: Ross Burton <ross@linux.intel.com>
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <glib/gi18n-lib.h>
28
29 #include <libedataserver/libedataserver.h>
30 #include <libedata-book/libedata-book.h>
31
32 #include "e-book-client.h"
33 #include "e-book-client-view.h"
34 #include "e-gdbus-book-view.h"
35
36 #define E_BOOK_CLIENT_VIEW_GET_PRIVATE(obj) \
37         (G_TYPE_INSTANCE_GET_PRIVATE \
38         ((obj), E_TYPE_BOOK_CLIENT_VIEW, EBookClientViewPrivate))
39
40 typedef struct _SignalClosure SignalClosure;
41
42 struct _EBookClientViewPrivate {
43         EBookClient *client;
44         GDBusProxy *dbus_proxy;
45         GDBusConnection *connection;
46         gchar *object_path;
47         guint running : 1;
48         guint complete : 1;
49
50         EDataBook *direct_book;
51
52         gulong objects_added_handler_id;
53         gulong objects_modified_handler_id;
54         gulong objects_removed_handler_id;
55         gulong progress_handler_id;
56         gulong complete_handler_id;
57 };
58
59 struct _SignalClosure {
60         GWeakRef client_view;
61         GSList *object_list;
62         GSList *string_list;
63         gchar *message;
64         guint percent;
65         GError *error;
66 };
67
68 enum {
69         PROP_0,
70         PROP_CLIENT,
71         PROP_CONNECTION,
72         PROP_DIRECT_BOOK,
73         PROP_OBJECT_PATH
74 };
75
76 enum {
77         OBJECTS_ADDED,
78         OBJECTS_MODIFIED,
79         OBJECTS_REMOVED,
80         PROGRESS,
81         COMPLETE,
82         LAST_SIGNAL
83 };
84
85 /* Forward Declarations */
86 static void     e_book_client_view_initable_init
87                                                 (GInitableIface *interface);
88
89 static guint signals[LAST_SIGNAL];
90
91 G_DEFINE_TYPE_WITH_CODE (
92         EBookClientView,
93         e_book_client_view,
94         G_TYPE_OBJECT,
95         G_IMPLEMENT_INTERFACE (
96                 G_TYPE_INITABLE,
97                 e_book_client_view_initable_init))
98
99 typedef struct {
100         EBookClientView *view;
101         guint signum;
102 } NotificationData;
103
104 static void
105 signal_closure_free (SignalClosure *signal_closure)
106 {
107         g_weak_ref_set (&signal_closure->client_view, NULL);
108
109         g_slist_free_full (
110                 signal_closure->object_list,
111                 (GDestroyNotify) g_object_unref);
112
113         g_slist_free_full (
114                 signal_closure->string_list,
115                 (GDestroyNotify) g_free);
116
117         g_free (signal_closure->message);
118
119         if (signal_closure->error != NULL)
120                 g_error_free (signal_closure->error);
121
122         g_slice_free (SignalClosure, signal_closure);
123 }
124
125 static gboolean
126 book_client_view_emit_objects_added_idle_cb (gpointer user_data)
127 {
128         SignalClosure *signal_closure = user_data;
129         EBookClientView *client_view;
130
131         client_view = g_weak_ref_get (&signal_closure->client_view);
132
133         if (client_view != NULL) {
134                 g_signal_emit (
135                         client_view,
136                         signals[OBJECTS_ADDED], 0,
137                         signal_closure->object_list);
138                 g_object_unref (client_view);
139         }
140
141         return FALSE;
142 }
143
144 static gboolean
145 book_client_view_emit_objects_modified_idle_cb (gpointer user_data)
146 {
147         SignalClosure *signal_closure = user_data;
148         EBookClientView *client_view;
149
150         client_view = g_weak_ref_get (&signal_closure->client_view);
151
152         if (client_view != NULL) {
153                 g_signal_emit (
154                         client_view,
155                         signals[OBJECTS_MODIFIED], 0,
156                         signal_closure->object_list);
157                 g_object_unref (client_view);
158         }
159
160         return FALSE;
161 }
162
163 static gboolean
164 book_client_view_emit_objects_removed_idle_cb (gpointer user_data)
165 {
166         SignalClosure *signal_closure = user_data;
167         EBookClientView *client_view;
168
169         client_view = g_weak_ref_get (&signal_closure->client_view);
170
171         if (client_view != NULL) {
172                 g_signal_emit (
173                         client_view,
174                         signals[OBJECTS_REMOVED], 0,
175                         signal_closure->string_list);
176                 g_object_unref (client_view);
177         }
178
179         return FALSE;
180 }
181
182 static gboolean
183 book_client_view_emit_progress_idle_cb (gpointer user_data)
184 {
185         SignalClosure *signal_closure = user_data;
186         EBookClientView *client_view;
187
188         client_view = g_weak_ref_get (&signal_closure->client_view);
189
190         if (client_view != NULL) {
191                 g_signal_emit (
192                         client_view,
193                         signals[PROGRESS], 0,
194                         signal_closure->percent,
195                         signal_closure->message);
196                 g_object_unref (client_view);
197         }
198
199         return FALSE;
200 }
201
202 static gboolean
203 book_client_view_emit_complete_idle_cb (gpointer user_data)
204 {
205         SignalClosure *signal_closure = user_data;
206         EBookClientView *client_view;
207
208         client_view = g_weak_ref_get (&signal_closure->client_view);
209
210         if (client_view != NULL) {
211                 g_signal_emit (
212                         client_view,
213                         signals[COMPLETE], 0,
214                         signal_closure->error);
215                 g_object_unref (client_view);
216         }
217
218         return FALSE;
219 }
220
221 static void
222 book_client_view_emit_objects_added (EBookClientView *client_view,
223                                      GSList *object_list)
224 {
225         EBookClient *client;
226         GSource *idle_source;
227         GMainContext *main_context;
228         SignalClosure *signal_closure;
229
230         client = e_book_client_view_ref_client (client_view);
231
232         /* Suppress any further signal emissions if
233          * our EBookClient has already been finalized. */
234         if (client == NULL)
235                 return;
236
237         signal_closure = g_slice_new0 (SignalClosure);
238         g_weak_ref_set (&signal_closure->client_view, client_view);
239         signal_closure->object_list = object_list;  /* takes ownership */
240
241         main_context = e_client_ref_main_context (E_CLIENT (client));
242
243         idle_source = g_idle_source_new ();
244         g_source_set_callback (
245                 idle_source,
246                 book_client_view_emit_objects_added_idle_cb,
247                 signal_closure,
248                 (GDestroyNotify) signal_closure_free);
249         g_source_attach (idle_source, main_context);
250         g_source_unref (idle_source);
251
252         g_main_context_unref (main_context);
253
254         g_object_unref (client);
255 }
256
257 static void
258 book_client_view_emit_objects_modified (EBookClientView *client_view,
259                                         GSList *object_list)
260 {
261         EBookClient *client;
262         GSource *idle_source;
263         GMainContext *main_context;
264         SignalClosure *signal_closure;
265
266         client = e_book_client_view_ref_client (client_view);
267
268         /* Suppress any further signal emissions if
269          * our EBookClient has already been finalized. */
270         if (client == NULL)
271                 return;
272
273         signal_closure = g_slice_new0 (SignalClosure);
274         g_weak_ref_set (&signal_closure->client_view, client_view);
275         signal_closure->object_list = object_list;  /* takes ownership */
276
277         main_context = e_client_ref_main_context (E_CLIENT (client));
278
279         idle_source = g_idle_source_new ();
280         g_source_set_callback (
281                 idle_source,
282                 book_client_view_emit_objects_modified_idle_cb,
283                 signal_closure,
284                 (GDestroyNotify) signal_closure_free);
285         g_source_attach (idle_source, main_context);
286         g_source_unref (idle_source);
287
288         g_main_context_unref (main_context);
289 }
290
291 static gchar *
292 direct_contacts_query (const gchar * const *uids)
293 {
294         EBookQuery *query, **qs;
295         gchar *sexp;
296         gint i, len;
297
298         len = g_strv_length ((gchar **) uids);
299         qs = g_new0 (EBookQuery *, len);
300
301         for (i = 0; uids[i] != NULL; i++) {
302                 const gchar *uid = uids[i];
303
304                 qs[i] = e_book_query_field_test (E_CONTACT_UID, E_BOOK_QUERY_IS, uid);
305         }
306
307         query = e_book_query_or (len, qs, TRUE);
308         sexp = e_book_query_to_string (query);
309         e_book_query_unref (query);
310
311         return sexp;
312 }
313
314 static void
315 direct_contacts_ready (GObject *source_object,
316                        GAsyncResult *result,
317                        gpointer user_data)
318 {
319         NotificationData *data = (NotificationData *) user_data;
320         GSList *contacts = NULL;
321         GError *error = NULL;
322
323         e_data_book_get_contacts_finish (
324                 E_DATA_BOOK (source_object),
325                 result, &contacts, &error);
326
327         if (error != NULL) {
328                 g_warn_if_fail (contacts == NULL);
329                 g_warning (
330                         "Error fetching contacts directly: %s\n",
331                         error->message);
332                 g_error_free (error);
333
334         } else if (data->signum == OBJECTS_ADDED) {
335                 /* Takes ownership of the linked list. */
336                 book_client_view_emit_objects_added (data->view, contacts);
337
338         } else if (data->signum == OBJECTS_MODIFIED) {
339                 /* Takes ownership of the linked list. */
340                 book_client_view_emit_objects_modified (data->view, contacts);
341
342         } else {
343                 g_slist_free_full (contacts, (GDestroyNotify) g_object_unref);
344         }
345
346         g_object_unref (data->view);
347         g_slice_free (NotificationData, data);
348 }
349
350 static void
351 direct_contacts_fetch (EBookClientView *view,
352                        const gchar * const *uids,
353                        guint signum)
354 {
355         NotificationData *data;
356         gchar *sexp = direct_contacts_query (uids);
357
358         /* Until the view has completely loaded, we need to make
359          * sync calls to the backend
360          */
361         if (!view->priv->complete) {
362                 GSList *contacts = NULL;
363                 GError *error = NULL;
364
365                 e_data_book_get_contacts_sync (
366                         view->priv->direct_book,
367                         sexp, &contacts, NULL, &error);
368
369                 if (error != NULL) {
370                         g_warn_if_fail (contacts == NULL);
371                         g_warning (
372                                 "Error fetching contacts directly: %s\n",
373                                 error->message);
374                         g_error_free (error);
375
376                 } else if (signum == OBJECTS_ADDED) {
377                         /* Takes ownership of the linked list. */
378                         book_client_view_emit_objects_added (view, contacts);
379
380                 } else if (signum == OBJECTS_MODIFIED) {
381                         /* Takes ownership of the linked list. */
382                         book_client_view_emit_objects_modified (view, contacts);
383
384                 } else {
385                         g_slist_free_full (
386                                 contacts, (GDestroyNotify) g_object_unref);
387                 }
388
389         } else {
390                 /* Make async calls, avoid blocking the thread owning the view
391                  * as much as possible
392                  */
393                 data = g_slice_new (NotificationData);
394                 data->view = g_object_ref (view);
395                 data->signum = signum;
396
397                 e_data_book_get_contacts (
398                         view->priv->direct_book,
399                         sexp, NULL, direct_contacts_ready, data);
400         }
401
402         g_free (sexp);
403 }
404
405 static void
406 book_client_view_objects_added_cb (EGdbusBookView *object,
407                                    const gchar * const *vcards,
408                                    EBookClientView *client_view)
409 {
410         GSList *list = NULL;
411         gint ii;
412
413         if (!client_view->priv->running)
414                 return;
415
416         /* array contains UIDs only */
417         if (client_view->priv->direct_book != NULL) {
418                 direct_contacts_fetch (client_view, vcards, OBJECTS_ADDED);
419                 return;
420         }
421
422         /* array contains both UID and vcard */
423         for (ii = 0; vcards[ii] != NULL && vcards[ii + 1] != NULL; ii += 2) {
424                 EContact *contact;
425                 const gchar *vcard = vcards[ii];
426                 const gchar *uid = vcards[ii + 1];
427
428                 contact = e_contact_new_from_vcard_with_uid (vcard, uid);
429                 list = g_slist_prepend (list, contact);
430         }
431
432         list = g_slist_reverse (list);
433
434         /* Takes ownership of the linked list. */
435         book_client_view_emit_objects_added (client_view, list);
436 }
437
438 static void
439 book_client_view_objects_modified_cb (EGdbusBookView *object,
440                                       const gchar * const *vcards,
441                                       EBookClientView *client_view)
442 {
443         GSList *list = NULL;
444         gint ii;
445
446         if (!client_view->priv->running)
447                 return;
448
449         /* array contains UIDs only */
450         if (client_view->priv->direct_book != NULL) {
451                 direct_contacts_fetch (client_view, vcards, OBJECTS_MODIFIED);
452                 return;
453         }
454
455         /* array contains both UID and vcard */
456         for (ii = 0; vcards[ii] != NULL && vcards[ii + 1] != NULL; ii += 2) {
457                 EContact *contact;
458                 const gchar *vcard = vcards[ii];
459                 const gchar *uid = vcards[ii + 1];
460
461                 contact = e_contact_new_from_vcard_with_uid (vcard, uid);
462                 list = g_slist_prepend (list, contact);
463         }
464
465         list = g_slist_reverse (list);
466
467         /* Takes ownership of the linked list. */
468         book_client_view_emit_objects_modified (client_view, list);
469 }
470
471 static void
472 book_client_view_objects_removed_cb (EGdbusBookView *object,
473                                      const gchar * const *ids,
474                                      EBookClientView *client_view)
475 {
476         EBookClient *client;
477         GSource *idle_source;
478         GMainContext *main_context;
479         SignalClosure *signal_closure;
480         GSList *list = NULL;
481         gint ii;
482
483         if (!client_view->priv->running)
484                 return;
485
486         client = e_book_client_view_ref_client (client_view);
487
488         /* Suppress any further signal emissions if
489          * our EBookClient has already been finalized. */
490         if (client == NULL)
491                 return;
492
493         for (ii = 0; ids[ii] != NULL; ii++)
494                 list = g_slist_prepend (list, g_strdup (ids[ii]));
495
496         signal_closure = g_slice_new0 (SignalClosure);
497         g_weak_ref_set (&signal_closure->client_view, client_view);
498         signal_closure->string_list = g_slist_reverse (list);
499
500         main_context = e_client_ref_main_context (E_CLIENT (client));
501
502         idle_source = g_idle_source_new ();
503         g_source_set_callback (
504                 idle_source,
505                 book_client_view_emit_objects_removed_idle_cb,
506                 signal_closure,
507                 (GDestroyNotify) signal_closure_free);
508         g_source_attach (idle_source, main_context);
509         g_source_unref (idle_source);
510
511         g_main_context_unref (main_context);
512
513         g_object_unref (client);
514 }
515
516 static void
517 book_client_view_progress_cb (EGdbusBookView *object,
518                               guint percent,
519                               const gchar *message,
520                               EBookClientView *client_view)
521 {
522         EBookClient *client;
523         GSource *idle_source;
524         GMainContext *main_context;
525         SignalClosure *signal_closure;
526
527         if (!client_view->priv->running)
528                 return;
529
530         client = e_book_client_view_ref_client (client_view);
531
532         /* Suppress any further signal emissions if
533          * our EBookClient has already been finalized. */
534         if (client == NULL)
535                 return;
536
537         signal_closure = g_slice_new0 (SignalClosure);
538         g_weak_ref_set (&signal_closure->client_view, client_view);
539         signal_closure->message = g_strdup (message);
540         signal_closure->percent = percent;
541
542         main_context = e_client_ref_main_context (E_CLIENT (client));
543
544         idle_source = g_idle_source_new ();
545         g_source_set_callback (
546                 idle_source,
547                 book_client_view_emit_progress_idle_cb,
548                 signal_closure,
549                 (GDestroyNotify) signal_closure_free);
550         g_source_attach (idle_source, main_context);
551         g_source_unref (idle_source);
552
553         g_main_context_unref (main_context);
554
555         g_object_unref (client);
556 }
557
558 static void
559 book_client_view_complete_cb (EGdbusBookView *object,
560                               const gchar * const *in_error_strv,
561                               EBookClientView *client_view)
562 {
563         EBookClient *client;
564         GSource *idle_source;
565         GMainContext *main_context;
566         SignalClosure *signal_closure;
567
568         if (!client_view->priv->running)
569                 return;
570
571         client = e_book_client_view_ref_client (client_view);
572
573         /* Suppress any further signal emissions if
574          * our EBookClient has already been finalized. */
575         if (client == NULL)
576                 return;
577
578         signal_closure = g_slice_new0 (SignalClosure);
579         g_weak_ref_set (&signal_closure->client_view, client_view);
580         e_gdbus_templates_decode_error (in_error_strv, &signal_closure->error);
581
582         main_context = e_client_ref_main_context (E_CLIENT (client));
583
584         idle_source = g_idle_source_new ();
585         g_source_set_callback (
586                 idle_source,
587                 book_client_view_emit_complete_idle_cb,
588                 signal_closure,
589                 (GDestroyNotify) signal_closure_free);
590         g_source_attach (idle_source, main_context);
591         g_source_unref (idle_source);
592
593         g_main_context_unref (main_context);
594
595         client_view->priv->complete = TRUE;
596
597         g_object_unref (client);
598 }
599
600 static void
601 book_client_view_dispose_cb (GObject *source_object,
602                              GAsyncResult *result,
603                              gpointer user_data)
604 {
605         GError *error = NULL;
606
607         e_gdbus_book_view_call_dispose_finish (
608                 G_DBUS_PROXY (source_object), result, &error);
609
610         if (error != NULL) {
611                 g_warning ("%s: %s", G_STRFUNC, error->message);
612                 g_error_free (error);
613         }
614 }
615
616 static void
617 book_client_view_set_client (EBookClientView *view,
618                              EBookClient *client)
619 {
620         g_return_if_fail (E_IS_BOOK_CLIENT (client));
621         g_return_if_fail (view->priv->client == NULL);
622
623         view->priv->client = g_object_ref (client);
624 }
625
626 static void
627 book_client_view_set_connection (EBookClientView *view,
628                                  GDBusConnection *connection)
629 {
630         g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
631         g_return_if_fail (view->priv->connection == NULL);
632
633         view->priv->connection = g_object_ref (connection);
634 }
635
636 static void
637 book_client_view_set_direct_book (EBookClientView *view,
638                                   EDataBook *book)
639 {
640         g_return_if_fail (book == NULL || E_IS_DATA_BOOK (book));
641         g_return_if_fail (view->priv->direct_book == NULL);
642
643         if (book != NULL)
644                 view->priv->direct_book = g_object_ref (book);
645 }
646
647 static void
648 book_client_view_set_object_path (EBookClientView *view,
649                                   const gchar *object_path)
650 {
651         g_return_if_fail (object_path != NULL);
652         g_return_if_fail (view->priv->object_path == NULL);
653
654         view->priv->object_path = g_strdup (object_path);
655 }
656
657 static void
658 book_client_view_set_property (GObject *object,
659                                guint property_id,
660                                const GValue *value,
661                                GParamSpec *pspec)
662 {
663         switch (property_id) {
664                 case PROP_CLIENT:
665                         book_client_view_set_client (
666                                 E_BOOK_CLIENT_VIEW (object),
667                                 g_value_get_object (value));
668                         return;
669
670                 case PROP_CONNECTION:
671                         book_client_view_set_connection (
672                                 E_BOOK_CLIENT_VIEW (object),
673                                 g_value_get_object (value));
674                         return;
675
676                 case PROP_DIRECT_BOOK:
677                         book_client_view_set_direct_book (
678                                 E_BOOK_CLIENT_VIEW (object),
679                                 g_value_get_object (value));
680                         return;
681
682                 case PROP_OBJECT_PATH:
683                         book_client_view_set_object_path (
684                                 E_BOOK_CLIENT_VIEW (object),
685                                 g_value_get_string (value));
686                         return;
687         }
688
689         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
690 }
691
692 static void
693 book_client_view_get_property (GObject *object,
694                                guint property_id,
695                                GValue *value,
696                                GParamSpec *pspec)
697 {
698         switch (property_id) {
699                 case PROP_CLIENT:
700                         g_value_take_object (
701                                 value,
702                                 e_book_client_view_ref_client (
703                                 E_BOOK_CLIENT_VIEW (object)));
704                         return;
705
706                 case PROP_CONNECTION:
707                         g_value_set_object (
708                                 value,
709                                 e_book_client_view_get_connection (
710                                 E_BOOK_CLIENT_VIEW (object)));
711                         return;
712
713                 case PROP_OBJECT_PATH:
714                         g_value_set_string (
715                                 value,
716                                 e_book_client_view_get_object_path (
717                                 E_BOOK_CLIENT_VIEW (object)));
718                         return;
719         }
720
721         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
722 }
723
724 static void
725 book_client_view_dispose (GObject *object)
726 {
727         EBookClientViewPrivate *priv;
728
729         priv = E_BOOK_CLIENT_VIEW_GET_PRIVATE (object);
730
731         if (priv->client != NULL) {
732                 g_object_unref (priv->client);
733                 priv->client = NULL;
734         }
735
736         if (priv->connection != NULL) {
737                 g_object_unref (priv->connection);
738                 priv->connection = NULL;
739         }
740
741         if (priv->direct_book != NULL) {
742                 g_object_unref (priv->direct_book);
743                 priv->direct_book = NULL;
744         }
745
746         if (priv->dbus_proxy != NULL) {
747                 g_signal_handler_disconnect (
748                         priv->dbus_proxy,
749                         priv->objects_added_handler_id);
750                 g_signal_handler_disconnect (
751                         priv->dbus_proxy,
752                         priv->objects_modified_handler_id);
753                 g_signal_handler_disconnect (
754                         priv->dbus_proxy,
755                         priv->objects_removed_handler_id);
756                 g_signal_handler_disconnect (
757                         priv->dbus_proxy,
758                         priv->progress_handler_id);
759                 g_signal_handler_disconnect (
760                         priv->dbus_proxy,
761                         priv->complete_handler_id);
762
763                 /* Call D-Bus dispose() asynchronously
764                  * so we don't block this dispose() .*/
765                 e_gdbus_book_view_call_dispose (
766                         priv->dbus_proxy, NULL,
767                         book_client_view_dispose_cb, NULL);
768                 g_object_unref (priv->dbus_proxy);
769                 priv->dbus_proxy = NULL;
770         }
771
772         /* Chain up to parent's dispose() method. */
773         G_OBJECT_CLASS (e_book_client_view_parent_class)->dispose (object);
774 }
775
776 static void
777 book_client_view_finalize (GObject *object)
778 {
779         EBookClientViewPrivate *priv;
780
781         priv = E_BOOK_CLIENT_VIEW_GET_PRIVATE (object);
782
783         g_free (priv->object_path);
784
785         /* Chain up to parent's finalize() method. */
786         G_OBJECT_CLASS (e_book_client_view_parent_class)->finalize (object);
787 }
788
789 static gboolean
790 book_client_view_initable_init (GInitable *initable,
791                                 GCancellable *cancellable,
792                                 GError **error)
793 {
794         EBookClientViewPrivate *priv;
795         EGdbusBookView *gdbus_bookview;
796         gulong handler_id;
797
798         priv = E_BOOK_CLIENT_VIEW_GET_PRIVATE (initable);
799
800         gdbus_bookview = e_gdbus_book_view_proxy_new_sync (
801                 priv->connection,
802                 G_DBUS_PROXY_FLAGS_NONE,
803                 ADDRESS_BOOK_DBUS_SERVICE_NAME,
804                 priv->object_path,
805                 cancellable, error);
806
807         if (gdbus_bookview == NULL)
808                 return FALSE;
809
810         priv->dbus_proxy = G_DBUS_PROXY (gdbus_bookview);
811
812         handler_id = g_signal_connect (
813                 priv->dbus_proxy, "objects-added",
814                 G_CALLBACK (book_client_view_objects_added_cb), initable);
815         priv->objects_added_handler_id = handler_id;
816
817         handler_id = g_signal_connect (
818                 priv->dbus_proxy, "objects-modified",
819                 G_CALLBACK (book_client_view_objects_modified_cb), initable);
820         priv->objects_modified_handler_id = handler_id;
821
822         handler_id = g_signal_connect (
823                 priv->dbus_proxy, "objects-removed",
824                 G_CALLBACK (book_client_view_objects_removed_cb), initable);
825         priv->objects_removed_handler_id = handler_id;
826
827         handler_id = g_signal_connect (
828                 priv->dbus_proxy, "progress",
829                 G_CALLBACK (book_client_view_progress_cb), initable);
830         priv->progress_handler_id = handler_id;
831
832         handler_id = g_signal_connect (
833                 priv->dbus_proxy, "complete",
834                 G_CALLBACK (book_client_view_complete_cb), initable);
835         priv->complete_handler_id = handler_id;
836
837         /* When in direct read access mode, we add a special field
838          * to fields-of-interest indicating we only want uids sent
839          */
840         if (priv->direct_book)
841                 e_book_client_view_set_fields_of_interest (
842                         E_BOOK_CLIENT_VIEW (initable), NULL, NULL);
843
844         return TRUE;
845 }
846
847 static void
848 e_book_client_view_class_init (EBookClientViewClass *class)
849 {
850         GObjectClass *object_class;
851
852         g_type_class_add_private (class, sizeof (EBookClientViewPrivate));
853
854         object_class = G_OBJECT_CLASS (class);
855         object_class->set_property = book_client_view_set_property;
856         object_class->get_property = book_client_view_get_property;
857         object_class->dispose = book_client_view_dispose;
858         object_class->finalize = book_client_view_finalize;
859
860         g_object_class_install_property (
861                 object_class,
862                 PROP_CLIENT,
863                 g_param_spec_object (
864                         "client",
865                         "Client",
866                         "The EBookClient for the view",
867                         E_TYPE_BOOK_CLIENT,
868                         G_PARAM_READWRITE |
869                         G_PARAM_CONSTRUCT_ONLY |
870                         G_PARAM_STATIC_STRINGS));
871
872         g_object_class_install_property (
873                 object_class,
874                 PROP_CONNECTION,
875                 g_param_spec_object (
876                         "connection",
877                         "Connection",
878                         "The GDBusConnection used "
879                         "to create the D-Bus proxy",
880                         G_TYPE_DBUS_CONNECTION,
881                         G_PARAM_READWRITE |
882                         G_PARAM_CONSTRUCT_ONLY |
883                         G_PARAM_STATIC_STRINGS));
884
885         g_object_class_install_property (
886                 object_class,
887                 PROP_DIRECT_BOOK,
888                 g_param_spec_object (
889                         "direct-book",
890                         "Direct Book",
891                         "The EDataBook to fetch contact "
892                         "data from, if direct read access "
893                         "is enabled",
894                         E_TYPE_DATA_BOOK,
895                         G_PARAM_WRITABLE |
896                         G_PARAM_CONSTRUCT_ONLY |
897                         G_PARAM_STATIC_STRINGS));
898
899         g_object_class_install_property (
900                 object_class,
901                 PROP_OBJECT_PATH,
902                 g_param_spec_string (
903                         "object-path",
904                         "Object Path",
905                         "The object path used "
906                         "to create the D-Bus proxy",
907                         NULL,
908                         G_PARAM_READWRITE |
909                         G_PARAM_CONSTRUCT_ONLY |
910                         G_PARAM_STATIC_STRINGS));
911
912         signals[OBJECTS_ADDED] = g_signal_new (
913                 "objects-added",
914                 G_OBJECT_CLASS_TYPE (object_class),
915                 G_SIGNAL_RUN_LAST,
916                 G_STRUCT_OFFSET (EBookClientViewClass, objects_added),
917                 NULL, NULL, NULL,
918                 G_TYPE_NONE, 1,
919                 G_TYPE_POINTER);
920
921         signals[OBJECTS_MODIFIED] = g_signal_new (
922                 "objects-modified",
923                 G_OBJECT_CLASS_TYPE (object_class),
924                 G_SIGNAL_RUN_LAST,
925                 G_STRUCT_OFFSET (EBookClientViewClass, objects_modified),
926                 NULL, NULL, NULL,
927                 G_TYPE_NONE, 1,
928                 G_TYPE_POINTER);
929
930         signals[OBJECTS_REMOVED] = g_signal_new (
931                 "objects-removed",
932                 G_OBJECT_CLASS_TYPE (object_class),
933                 G_SIGNAL_RUN_LAST,
934                 G_STRUCT_OFFSET (EBookClientViewClass, objects_removed),
935                 NULL, NULL, NULL,
936                 G_TYPE_NONE, 1,
937                 G_TYPE_POINTER);
938
939         signals[PROGRESS] = g_signal_new (
940                 "progress",
941                 G_OBJECT_CLASS_TYPE (object_class),
942                 G_SIGNAL_RUN_LAST,
943                 G_STRUCT_OFFSET (EBookClientViewClass, progress),
944                 NULL, NULL, NULL,
945                 G_TYPE_NONE, 2,
946                 G_TYPE_UINT,
947                 G_TYPE_STRING);
948
949         signals[COMPLETE] = g_signal_new (
950                 "complete",
951                 G_OBJECT_CLASS_TYPE (object_class),
952                 G_SIGNAL_RUN_LAST,
953                 G_STRUCT_OFFSET (EBookClientViewClass, complete),
954                 NULL, NULL, NULL,
955                 G_TYPE_NONE, 1,
956                 G_TYPE_ERROR);
957 }
958
959 static void
960 e_book_client_view_initable_init (GInitableIface *interface)
961 {
962         interface->init = book_client_view_initable_init;
963 }
964
965 static void
966 e_book_client_view_init (EBookClientView *view)
967 {
968         view->priv = E_BOOK_CLIENT_VIEW_GET_PRIVATE (view);
969 }
970
971 /**
972  * e_book_client_view_ref_client:
973  * @view: an #EBookClientView
974  *
975  * Returns the #EBookClientView:client associated with @view.
976  *
977  * The returned #EBookClient is referenced for thread-safety.  Unreference
978  * the #EBookClient with g_object_unref() when finished with it.
979  *
980  * Returns: an #EBookClient
981  *
982  * Since: 3.10
983  **/
984 EBookClient *
985 e_book_client_view_ref_client (EBookClientView *view)
986 {
987         g_return_val_if_fail (E_IS_BOOK_CLIENT_VIEW (view), NULL);
988
989         return g_object_ref (view->priv->client);
990 }
991
992 /**
993  * e_book_client_view_get_client:
994  * @view: an #EBookClientView
995  *
996  * Returns the #EBookClientView:client associated with @view.
997  *
998  * Returns: (transfer none): an #EBookClient
999  *
1000  * Deprecated: 3.10: Use e_book_client_view_ref_client() instead.
1001  **/
1002 EBookClient *
1003 e_book_client_view_get_client (EBookClientView *view)
1004 {
1005         g_return_val_if_fail (E_IS_BOOK_CLIENT_VIEW (view), NULL);
1006
1007         return view->priv->client;
1008 }
1009
1010 /**
1011  * e_book_client_view_get_connection:
1012  * @view: an #EBookClientView
1013  *
1014  * Returns the #GDBusConnection used to create the D-Bus proxy.
1015  *
1016  * Returns: (transfer none): the #GDBusConnection
1017  *
1018  * Since: 3.8
1019  **/
1020 GDBusConnection *
1021 e_book_client_view_get_connection (EBookClientView *view)
1022 {
1023         g_return_val_if_fail (E_IS_BOOK_CLIENT_VIEW (view), NULL);
1024
1025         return view->priv->connection;
1026 }
1027
1028 /**
1029  * e_book_client_view_get_object_path:
1030  * @view: an #EBookClientView
1031  *
1032  * Returns the object path used to create the D-Bus proxy.
1033  *
1034  * Returns: the object path
1035  *
1036  * Since: 3.8
1037  **/
1038 const gchar *
1039 e_book_client_view_get_object_path (EBookClientView *view)
1040 {
1041         g_return_val_if_fail (E_IS_BOOK_CLIENT_VIEW (view), NULL);
1042
1043         return view->priv->object_path;
1044 }
1045
1046 /**
1047  * e_book_client_view_start:
1048  * @view: an #EBookClientView
1049  * @error: return location for a #GError, or %NULL
1050  *
1051  * Tells @view to start processing events.
1052  */
1053 void
1054 e_book_client_view_start (EBookClientView *view,
1055                           GError **error)
1056 {
1057         EBookClient *client;
1058         gboolean success;
1059         GError *local_error = NULL;
1060
1061         g_return_if_fail (E_IS_BOOK_CLIENT_VIEW (view));
1062
1063         client = e_book_client_view_ref_client (view);
1064         g_return_if_fail (client != NULL);
1065
1066         view->priv->running = TRUE;
1067
1068         success = e_gdbus_book_view_call_start_sync (
1069                 view->priv->dbus_proxy, NULL, &local_error);
1070         if (!success)
1071                 view->priv->running = FALSE;
1072
1073         e_client_unwrap_dbus_error (
1074                 E_CLIENT (client), local_error, error);
1075
1076         g_object_unref (client);
1077 }
1078
1079 /**
1080  * e_book_client_view_stop:
1081  * @view: an #EBookClientView
1082  * @error: return location for a #GError, or %NULL
1083  *
1084  * Tells @view to stop processing events.
1085  **/
1086 void
1087 e_book_client_view_stop (EBookClientView *view,
1088                          GError **error)
1089 {
1090         EBookClient *client;
1091         GError *local_error = NULL;
1092
1093         g_return_if_fail (E_IS_BOOK_CLIENT_VIEW (view));
1094
1095         client = e_book_client_view_ref_client (view);
1096         g_return_if_fail (client != NULL);
1097
1098         view->priv->running = FALSE;
1099
1100         e_gdbus_book_view_call_stop_sync (
1101                 view->priv->dbus_proxy, NULL, &local_error);
1102
1103         e_client_unwrap_dbus_error (
1104                 E_CLIENT (client), local_error, error);
1105
1106         g_object_unref (client);
1107 }
1108
1109 /**
1110  * e_book_client_view_set_flags:
1111  * @view: an #EBookClientView
1112  * @flags: the #EBookClientViewFlags for @view
1113  * @error: return location for a #GError, or %NULL
1114  *
1115  * Sets the @flags which control the behaviour of @view.
1116  *
1117  * Since: 3.4
1118  */
1119 void
1120 e_book_client_view_set_flags (EBookClientView *view,
1121                               EBookClientViewFlags flags,
1122                               GError **error)
1123 {
1124         EBookClient *client;
1125         GError *local_error = NULL;
1126
1127         g_return_if_fail (E_IS_BOOK_CLIENT_VIEW (view));
1128
1129         client = e_book_client_view_ref_client (view);
1130         g_return_if_fail (client != NULL);
1131
1132         e_gdbus_book_view_call_set_flags_sync (
1133                 view->priv->dbus_proxy, flags, NULL, &local_error);
1134
1135         e_client_unwrap_dbus_error (
1136                 E_CLIENT (client), local_error, error);
1137
1138         g_object_unref (client);
1139 }
1140
1141 /**
1142  * e_book_client_view_set_fields_of_interest:
1143  * @view: An #EBookClientView object
1144  * @fields_of_interest: (element-type utf8): List of field names in which
1145  *                      the client is interested
1146  * @error: return location for a #GError, or %NULL
1147  *
1148  * Client can instruct server to which fields it is interested in only, thus
1149  * the server can return less data over the wire. The server can still return
1150  * complete objects, this is just a hint to it that the listed fields will
1151  * be used only. The UID field is returned always. Initial views has no fields
1152  * of interest and using %NULL for @fields_of_interest will unset any previous
1153  * changes.
1154  *
1155  * Some backends can use summary information of its cache to create artifical
1156  * objects, which will omit stored object parsing. If this cannot be done then
1157  * it will simply return object as is stored in the cache.
1158  **/
1159 void
1160 e_book_client_view_set_fields_of_interest (EBookClientView *view,
1161                                            const GSList *fields_of_interest,
1162                                            GError **error)
1163 {
1164         EBookClient *client;
1165         gchar **strv;
1166         GError *local_error = NULL;
1167
1168         g_return_if_fail (E_IS_BOOK_CLIENT_VIEW (view));
1169
1170         client = e_book_client_view_ref_client (view);
1171         g_return_if_fail (client != NULL);
1172
1173         /* When in direct read access mode, ensure that the
1174          * backend is configured to only send us UIDs for everything,
1175          *
1176          * Just ignore the fields_of_interest and use them locally
1177          * when filtering cards to be returned in direct reads.
1178          */
1179         if (view->priv->direct_book) {
1180                 GSList uid_field = { 0, };
1181
1182                 uid_field.data = (gpointer)"x-evolution-uids-only";
1183                 strv = e_client_util_slist_to_strv (&uid_field);
1184         } else
1185                 strv = e_client_util_slist_to_strv (fields_of_interest);
1186
1187         e_gdbus_book_view_call_set_fields_of_interest_sync (
1188                 view->priv->dbus_proxy,
1189                 (const gchar * const *) strv,
1190                 NULL, &local_error);
1191         g_strfreev (strv);
1192
1193         e_client_unwrap_dbus_error (
1194                 E_CLIENT (client), local_error, error);
1195
1196         g_object_unref (client);
1197 }
1198