Whitespace cleanups.
[platform/upstream/evolution-data-server.git] / calendar / libecal / e-cal-client-view.c
1 /*-*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar - Live view client object
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5  * Copyright (C) 2009 Intel Corporation
6  *
7  * Authors: Federico Mena-Quintero <federico@ximian.com>
8  *          Ross Burton <ross@linux.intel.com>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU Lesser General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <glib/gi18n-lib.h>
29
30 #include <string.h>
31 #include "e-cal-client.h"
32 #include "e-cal-client-view.h"
33 #include "e-cal-client-view-private.h"
34
35 #include "libedataserver/e-gdbus-marshallers.h"
36
37 #include "e-gdbus-cal-view.h"
38
39 #define E_CAL_CLIENT_VIEW_GET_PRIVATE(obj) \
40         (G_TYPE_INSTANCE_GET_PRIVATE \
41         ((obj), E_TYPE_CAL_CLIENT_VIEW, ECalClientViewPrivate))
42
43 G_DEFINE_TYPE (ECalClientView, e_cal_client_view, G_TYPE_OBJECT);
44
45 /* Private part of the ECalClientView structure */
46 struct _ECalClientViewPrivate {
47         GDBusProxy *gdbus_calview;
48         ECalClient *client;
49         gboolean running;
50 };
51
52 /* Property IDs */
53 enum props {
54         PROP_0,
55         PROP_VIEW,
56         PROP_CLIENT
57 };
58
59 /* Signal IDs */
60 enum {
61         OBJECTS_ADDED,
62         OBJECTS_MODIFIED,
63         OBJECTS_REMOVED,
64         PROGRESS,
65         COMPLETE,
66         LAST_SIGNAL
67 };
68
69 static guint signals[LAST_SIGNAL];
70
71 static GSList *
72 build_object_list (const gchar * const *seq)
73 {
74         GSList *list;
75         gint i;
76
77         list = NULL;
78         for (i = 0; seq[i]; i++) {
79                 icalcomponent *comp;
80
81                 comp = icalcomponent_new_from_string ((gchar *) seq[i]);
82                 if (!comp)
83                         continue;
84
85                 list = g_slist_prepend (list, comp);
86         }
87
88         return g_slist_reverse (list);
89 }
90
91 static GSList *
92 build_id_list (const gchar * const *seq)
93 {
94         GSList *list;
95         const gchar *eol;
96         gint i;
97
98         list = NULL;
99         for (i = 0; seq[i]; i++) {
100                 ECalComponentId *id;
101                 id = g_new (ECalComponentId, 1);
102
103                 /* match encoding as in notify_remove() in e-data-cal-view.c: <uid>[\n<rid>] */
104                 eol = strchr (seq[i], '\n');
105                 if (eol) {
106                         id->uid = g_strndup (seq[i], eol - seq[i]);
107                         id->rid = g_strdup (eol + 1);
108                 } else {
109                         id->uid = g_strdup (seq[i]);
110                         id->rid = NULL;
111                 }
112
113                 list = g_slist_prepend (list, id);
114         }
115
116         return g_slist_reverse (list);
117 }
118
119 static void
120 objects_added_cb (EGdbusCalView *gdbus_calview,
121                   const gchar * const *objects,
122                   ECalClientView *view)
123 {
124         GSList *list;
125
126         g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view));
127
128         if (!view->priv->running)
129                 return;
130
131         g_object_ref (view);
132
133         list = build_object_list (objects);
134
135         g_signal_emit (G_OBJECT (view), signals[OBJECTS_ADDED], 0, list);
136
137         g_slist_foreach (list, (GFunc) icalcomponent_free, NULL);
138         g_slist_free (list);
139
140         g_object_unref (view);
141 }
142
143 static void
144 objects_modified_cb (EGdbusCalView *gdbus_calview,
145                      const gchar * const *objects,
146                      ECalClientView *view)
147 {
148         GSList *list;
149
150         g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view));
151
152         if (!view->priv->running)
153                 return;
154
155         g_object_ref (view);
156
157         list = build_object_list (objects);
158
159         g_signal_emit (G_OBJECT (view), signals[OBJECTS_MODIFIED], 0, list);
160
161         g_slist_foreach (list, (GFunc) icalcomponent_free, NULL);
162         g_slist_free (list);
163
164         g_object_unref (view);
165 }
166
167 static void
168 objects_removed_cb (EGdbusCalView *gdbus_calview,
169                     const gchar * const *uids,
170                     ECalClientView *view)
171 {
172         GSList *list;
173
174         g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view));
175
176         if (!view->priv->running)
177                 return;
178
179         g_object_ref (view);
180
181         list = build_id_list (uids);
182
183         g_signal_emit (G_OBJECT (view), signals[OBJECTS_REMOVED], 0, list);
184
185         g_slist_foreach (list, (GFunc) e_cal_component_free_id, NULL);
186         g_slist_free (list);
187
188         g_object_unref (view);
189 }
190
191 static void
192 progress_cb (EGdbusCalView *gdbus_calview,
193              guint percent,
194              const gchar *message,
195              ECalClientView *view)
196 {
197         g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view));
198
199         if (!view->priv->running)
200                 return;
201
202         g_signal_emit (G_OBJECT (view), signals[PROGRESS], 0, percent, message);
203 }
204
205 static void
206 complete_cb (EGdbusCalView *gdbus_calview,
207              const gchar * const *arg_error,
208              ECalClientView *view)
209 {
210         GError *error = NULL;
211
212         g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view));
213
214         if (!view->priv->running)
215                 return;
216
217         g_return_if_fail (e_gdbus_templates_decode_error (arg_error, &error));
218
219         g_signal_emit (G_OBJECT (view), signals[COMPLETE], 0, error);
220
221         if (error)
222                 g_error_free (error);
223 }
224
225 /* Object initialization function for the calendar view */
226 static void
227 e_cal_client_view_init (ECalClientView *view)
228 {
229         view->priv = E_CAL_CLIENT_VIEW_GET_PRIVATE (view);
230         view->priv->running = FALSE;
231 }
232
233 static void
234 cal_client_view_set_property (GObject *object,
235                               guint property_id,
236                               const GValue *value,
237                               GParamSpec *pspec)
238 {
239         ECalClientView *view;
240         ECalClientViewPrivate *priv;
241
242         view = E_CAL_CLIENT_VIEW (object);
243         priv = view->priv;
244
245         switch (property_id) {
246         case PROP_VIEW:
247                 /* gdbus_calview can be set only once */
248                 g_return_if_fail (priv->gdbus_calview == NULL);
249
250                 priv->gdbus_calview = g_object_ref (g_value_get_pointer (value));
251                 g_signal_connect (priv->gdbus_calview, "objects-added", G_CALLBACK (objects_added_cb), view);
252                 g_signal_connect (priv->gdbus_calview, "objects-modified", G_CALLBACK (objects_modified_cb), view);
253                 g_signal_connect (priv->gdbus_calview, "objects-removed", G_CALLBACK (objects_removed_cb), view);
254                 g_signal_connect (priv->gdbus_calview, "progress", G_CALLBACK (progress_cb), view);
255                 g_signal_connect (priv->gdbus_calview, "complete", G_CALLBACK (complete_cb), view);
256                 break;
257         case PROP_CLIENT:
258                 priv->client = E_CAL_CLIENT (g_value_dup_object (value));
259                 break;
260         default:
261                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
262                 break;
263         }
264 }
265
266 static void
267 cal_client_view_get_property (GObject *object,
268                               guint property_id,
269                               GValue *value,
270                               GParamSpec *pspec)
271 {
272         ECalClientView *view;
273         ECalClientViewPrivate *priv;
274
275         view = E_CAL_CLIENT_VIEW (object);
276         priv = view->priv;
277
278         switch (property_id) {
279         case PROP_VIEW:
280                 g_value_set_pointer (value, priv->gdbus_calview);
281                 break;
282         case PROP_CLIENT:
283                 g_value_set_object (value, priv->client);
284                 break;
285         default:
286                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
287                 break;
288         }
289 }
290
291 /* Finalize handler for the calendar view */
292 static void
293 cal_client_view_finalize (GObject *object)
294 {
295         ECalClientView *view;
296         ECalClientViewPrivate *priv;
297
298         g_return_if_fail (E_IS_CAL_CLIENT_VIEW (object));
299
300         view = E_CAL_CLIENT_VIEW (object);
301         priv = view->priv;
302
303         if (priv->gdbus_calview != NULL) {
304                 GError *error = NULL;
305
306                 g_signal_handlers_disconnect_matched (priv->gdbus_calview, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, view);
307                 e_gdbus_cal_view_call_dispose_sync (priv->gdbus_calview, NULL, &error);
308                 g_object_unref (priv->gdbus_calview);
309                 priv->gdbus_calview = NULL;
310
311                 if (error) {
312                         g_warning ("Failed to dispose cal view: %s", error->message);
313                         g_error_free (error);
314                 }
315         }
316
317         if (priv->client) {
318                 g_object_unref (priv->client);
319                 priv->client = NULL;
320         }
321
322         /* Chain up to parent's finalize() method. */
323         G_OBJECT_CLASS (e_cal_client_view_parent_class)->finalize (object);
324 }
325
326 /* Class initialization function for the calendar view */
327 static void
328 e_cal_client_view_class_init (ECalClientViewClass *class)
329 {
330         GObjectClass *object_class;
331
332         object_class = (GObjectClass *) class;
333
334         object_class->set_property = cal_client_view_set_property;
335         object_class->get_property = cal_client_view_get_property;
336         object_class->finalize = cal_client_view_finalize;
337
338         g_type_class_add_private (class, sizeof (ECalClientViewPrivate));
339
340         g_object_class_install_property (object_class, PROP_VIEW,
341                 g_param_spec_pointer ("view", "The GDBus view proxy", NULL,
342                                       G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
343
344         g_object_class_install_property (object_class, PROP_CLIENT,
345                 g_param_spec_object ("client", "The e-cal-client for the view", NULL, E_TYPE_CAL_CLIENT,
346                                       G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
347         /**
348          * ECalClientView::objects-added:
349          * @view:: self
350          * @objects: (type GSList) (transfer none) (element-type long):
351          */
352         signals[OBJECTS_ADDED] =
353                 g_signal_new ("objects-added",
354                               G_TYPE_FROM_CLASS (class),
355                               G_SIGNAL_RUN_FIRST,
356                               G_STRUCT_OFFSET (ECalClientViewClass, objects_added),
357                               NULL, NULL,
358                               g_cclosure_marshal_VOID__POINTER,
359                               G_TYPE_NONE, 1, G_TYPE_POINTER);
360         /**
361          * ECalClientView::objects-modified:
362          * @view:: self
363          * @objects: (type GSList) (transfer none) (element-type long):
364          */
365         signals[OBJECTS_MODIFIED] =
366                 g_signal_new ("objects-modified",
367                               G_TYPE_FROM_CLASS (class),
368                               G_SIGNAL_RUN_FIRST,
369                               G_STRUCT_OFFSET (ECalClientViewClass, objects_modified),
370                               NULL, NULL,
371                               g_cclosure_marshal_VOID__POINTER,
372                               G_TYPE_NONE, 1, G_TYPE_POINTER);
373         /**
374          * ECalClientView::objects-removed:
375          * @view:: self
376          * @objects: (type GSList) (transfer none) (element-type ECalComponentId):
377          */
378         signals[OBJECTS_REMOVED] =
379                 g_signal_new ("objects-removed",
380                               G_TYPE_FROM_CLASS (class),
381                               G_SIGNAL_RUN_FIRST,
382                               G_STRUCT_OFFSET (ECalClientViewClass, objects_removed),
383                               NULL, NULL,
384                               g_cclosure_marshal_VOID__POINTER,
385                               G_TYPE_NONE, 1, G_TYPE_POINTER);
386
387         signals[PROGRESS] =
388                 g_signal_new ("progress",
389                               G_TYPE_FROM_CLASS (class),
390                               G_SIGNAL_RUN_FIRST,
391                               G_STRUCT_OFFSET (ECalClientViewClass, progress),
392                               NULL, NULL,
393                               e_gdbus_marshallers_VOID__UINT_STRING,
394                               G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
395
396         signals[COMPLETE] =
397                 g_signal_new ("complete",
398                               G_TYPE_FROM_CLASS (class),
399                               G_SIGNAL_RUN_FIRST,
400                               G_STRUCT_OFFSET (ECalClientViewClass, complete),
401                               NULL, NULL,
402                               g_cclosure_marshal_VOID__BOXED,
403                               G_TYPE_NONE, 1, G_TYPE_ERROR);
404 }
405
406 /**
407  * _e_cal_client_view_new:
408  * @client: An #ECalClient object.
409  * @gdbuc_calview: The GDBus object for the view.
410  *
411  * Creates a new view object by issuing the view creation request to the
412  * calendar server.
413  *
414  * Returns: A newly-created view object, or NULL if the request failed.
415  **/
416 ECalClientView *
417 _e_cal_client_view_new (ECalClient *client,
418                         EGdbusCalView *gdbus_calview)
419 {
420         ECalClientView *view;
421
422         view = g_object_new (E_TYPE_CAL_CLIENT_VIEW,
423                 "client", client,
424                 "view", gdbus_calview,
425                 NULL);
426
427         return view;
428 }
429
430 /**
431  * e_cal_client_view_get_client
432  * @view: A #ECalClientView object.
433  *
434  * Get the #ECalClient associated with this view.
435  *
436  * Returns: the associated client.
437  *
438  * Since: 3.2
439  **/
440 ECalClient *
441 e_cal_client_view_get_client (ECalClientView *view)
442 {
443         g_return_val_if_fail (E_IS_CAL_CLIENT_VIEW (view), NULL);
444
445         return view->priv->client;
446 }
447
448 /**
449  * e_cal_client_view_is_running:
450  * @view: an #ECalClientView
451  *
452  * Retunrs: Whether view is running. Not running views are ignoring
453  * all events sent from the server.
454  *
455  * Since: 3.2
456  **/
457 gboolean
458 e_cal_client_view_is_running (ECalClientView *view)
459 {
460         g_return_val_if_fail (E_IS_CAL_CLIENT_VIEW (view), FALSE);
461
462         return view->priv->running;
463 }
464
465 /**
466  * e_cal_client_view_start:
467  * @view: An #ECalClientView object.
468  * @error: A #Gerror
469  *
470  * Starts a live query to the calendar/tasks backend.
471  *
472  * Since: 3.2
473  **/
474 void
475 e_cal_client_view_start (ECalClientView *view,
476                          GError **error)
477 {
478         ECalClientViewPrivate *priv;
479
480         g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view));
481
482         priv = view->priv;
483
484         if (priv->gdbus_calview) {
485                 GError *local_error = NULL;
486
487                 priv->running = TRUE;
488                 if (!e_gdbus_cal_view_call_start_sync (priv->gdbus_calview, NULL, &local_error))
489                         priv->running = FALSE;
490
491                 e_client_unwrap_dbus_error (E_CLIENT (priv->client), local_error, error);
492         } else {
493                 /* do not translate this string, it should ideally never happen */
494                 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR, "Cannot start view, D-Bus proxy gone");
495         }
496 }
497
498 /**
499  * e_cal_client_view_stop:
500  * @view: An #ECalClientView object.
501  * @error: A #GError
502  *
503  * Stops a live query to the calendar/tasks backend.
504  *
505  * Since: 3.2
506  */
507 void
508 e_cal_client_view_stop (ECalClientView *view,
509                         GError **error)
510 {
511         ECalClientViewPrivate *priv;
512
513         g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view));
514
515         priv = view->priv;
516         priv->running = FALSE;
517
518         if (priv->gdbus_calview) {
519                 GError *local_error = NULL;
520
521                 e_gdbus_cal_view_call_stop_sync (priv->gdbus_calview, NULL, &local_error);
522
523                 e_client_unwrap_dbus_error (E_CLIENT (priv->client), local_error, error);
524         } else {
525                 /* do not translate this string, it should ideally never happen */
526                 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR, "Cannot stop view, D-Bus proxy gone");
527         }
528 }
529
530 /**
531  * e_cal_client_view_set_fields_of_interest:
532  * @view: An #ECalClientView object
533  * @fields_of_interest: List of field names in which the client is interested
534  * @error: A #GError
535  *
536  * Client can instruct server to which fields it is interested in only, thus
537  * the server can return less data over the wire. The server can still return
538  * complete objects, this is just a hint to it that the listed fields will
539  * be used only. The UID/RID fields are returned always. Initial views has no fields
540  * of interest and using %NULL for @fields_of_interest will unset any previous
541  * changes.
542  *
543  * Some backends can use summary information of its cache to create artifical
544  * objects, which will omit stored object parsing. If this cannot be done then
545  * it will simply return object as is stored in the cache.
546  **/
547 void
548 e_cal_client_view_set_fields_of_interest (ECalClientView *view,
549                                           const GSList *fields_of_interest,
550                                           GError **error)
551 {
552         ECalClientViewPrivate *priv;
553
554         g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view));
555
556         priv = view->priv;
557
558         if (priv->gdbus_calview) {
559                 GError *local_error = NULL;
560                 gchar **strv;
561
562                 strv = e_client_util_slist_to_strv (fields_of_interest);
563                 e_gdbus_cal_view_call_set_fields_of_interest_sync (priv->gdbus_calview, (const gchar * const *) strv, NULL, &local_error);
564                 g_strfreev (strv);
565
566                 e_client_unwrap_dbus_error (E_CLIENT (priv->client), local_error, error);
567         } else {
568                 /* do not translate this string, it should ideally never happen */
569                 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR, "Cannot set fields of interest, D-Bus proxy gone");
570         }
571 }
572
573 /**
574  * e_cal_client_view_set_flags:
575  * @view: an #ECalClientView
576  * @flags: the #ECalClientViewFlags for @view.
577  * @error: a return location for a #GError, or %NULL.
578  *
579  * Sets the @flags which control the behaviour of @view.
580  *
581  * Since: 3.6
582  */
583 void
584 e_cal_client_view_set_flags (ECalClientView *view,
585                              ECalClientViewFlags flags,
586                              GError **error)
587 {
588         ECalClientViewPrivate *priv;
589
590         g_return_if_fail (E_IS_CAL_CLIENT_VIEW (view));
591
592         priv = view->priv;
593
594         if (priv->gdbus_calview) {
595                 GError *local_error = NULL;
596
597                 e_gdbus_cal_view_call_set_flags_sync (priv->gdbus_calview, flags, NULL, &local_error);
598
599                 e_client_unwrap_dbus_error (E_CLIENT (priv->client), local_error, error);
600         } else {
601                 /* do not translate this string, it should ideally never happen */
602                 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR, "Cannot set fields of interest, D-Bus proxy gone");
603         }
604 }