Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / calendar / libedata-cal / e-data-cal-view.c
1 /* Evolution calendar - Live search query implementation
2  *
3  * Copyright (C) 2001 Ximian, Inc.
4  *
5  * Author: Federico Mena-Quintero <federico@ximian.com>
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
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <string.h>
26 #include <glib.h>
27 #include <bonobo/bonobo-exception.h>
28 #include "libedataserver/e-component-listener.h"
29 #include "e-cal-backend-sexp.h"
30 #include "e-data-cal-view.h"
31
32 \f
33
34 typedef struct {
35         GNOME_Evolution_Calendar_CalViewListener listener;
36         EComponentListener *component_listener;
37
38         gboolean notified_start;
39         gboolean notified_done;
40 } ListenerData;
41
42 /* Private part of the Query structure */
43 struct _EDataCalViewPrivate {
44         /* The backend we are monitoring */
45         ECalBackend *backend;
46
47         gboolean started;
48         gboolean done;
49         GNOME_Evolution_Calendar_CallStatus done_status;
50
51         GHashTable *matched_objects;
52
53         /* The listener we report to */
54         GList *listeners;
55
56         /* Sexp that defines the query */
57         ECalBackendSExp *sexp;
58 };
59
60
61 \f
62
63 static void e_data_cal_view_class_init (EDataCalViewClass *class);
64 static void e_data_cal_view_init (EDataCalView *query, EDataCalViewClass *class);
65 static void e_data_cal_view_finalize (GObject *object);
66
67 static BonoboObjectClass *parent_class;
68
69 \f
70
71 BONOBO_TYPE_FUNC_FULL (EDataCalView,
72                        GNOME_Evolution_Calendar_CalView,
73                        BONOBO_TYPE_OBJECT,
74                        e_data_cal_view);
75
76 /* Property IDs */
77 enum props {
78         PROP_0,
79         PROP_BACKEND,
80         PROP_LISTENER,
81         PROP_SEXP
82 };
83
84
85 static void
86 add_object_to_cache (EDataCalView *query, const char *calobj)
87 {
88         ECalComponent *comp;
89         char *real_uid;
90         const char *uid;
91         EDataCalViewPrivate *priv;
92
93         priv = query->priv;
94
95         comp = e_cal_component_new_from_string (calobj);
96         if (!comp)
97                 return;
98
99         e_cal_component_get_uid (comp, &uid);
100         if (!uid || !*uid) {
101                 g_object_unref (comp);
102                 return;
103         }
104
105         if (e_cal_component_is_instance (comp))
106                 real_uid = g_strdup_printf ("%s@%s", uid, e_cal_component_get_recurid_as_string (comp));
107         else
108                 real_uid = g_strdup (uid);
109
110         if (g_hash_table_lookup (priv->matched_objects, real_uid))
111                 g_hash_table_replace (priv->matched_objects, real_uid, g_strdup (calobj));
112         else
113                 g_hash_table_insert (priv->matched_objects, real_uid, g_strdup (calobj));
114
115         /* free memory */
116         g_object_unref (comp);
117 }
118
119 static gboolean
120 uncache_with_id_cb (gpointer key, gpointer value, gpointer user_data)
121 {
122         ECalComponent *comp;
123         ECalComponentId *id;
124         const char *this_uid;
125         char *object;
126         gboolean remove = FALSE;
127
128         id = user_data;
129         object = value;
130
131         comp = e_cal_component_new_from_string (object);
132         if (comp) {
133                 e_cal_component_get_uid (comp, &this_uid);
134                 if (this_uid && !strcmp (id->uid, this_uid)) {
135                         if (id->rid && *id->rid) {
136                                 const char *rid = e_cal_component_get_recurid_as_string (comp); 
137
138                                 if (rid && !strcmp (id->rid, rid))
139                                         remove = TRUE;
140                         } else
141                                 remove = TRUE;
142                 }
143
144                 g_object_unref (comp);
145         }
146
147         return remove;
148 }
149
150 static void
151 remove_object_from_cache (EDataCalView *query, const ECalComponentId *id)
152 {
153         EDataCalViewPrivate *priv;
154
155         priv = query->priv;
156
157         g_hash_table_foreach_remove (priv->matched_objects, (GHRFunc) uncache_with_id_cb, (gpointer) id);
158 }
159
160 static void
161 listener_died_cb (EComponentListener *cl, gpointer data)
162 {
163         EDataCalView *query = QUERY (data);
164         EDataCalViewPrivate *priv;
165         GList *l;
166
167         priv = query->priv;
168
169         for (l = priv->listeners; l != NULL; l = l->next) {
170                 ListenerData *ld = l->data;
171
172                 if (ld->component_listener == cl) {
173                         g_object_unref (ld->component_listener);
174                         ld->component_listener = NULL;
175         
176                         bonobo_object_release_unref (ld->listener, NULL);
177                         ld->listener = NULL;
178
179                         priv->listeners = g_list_remove_link (priv->listeners, l);
180                         g_list_free (l);
181                         g_free (ld);
182                         break;
183                 }
184         }
185 }
186
187 static void
188 notify_matched_object_cb (gpointer key, gpointer value, gpointer user_data)
189 {
190         char *uid, *object;
191         EDataCalView *query;
192         EDataCalViewPrivate *priv;
193         GList *l;
194
195         uid = key;
196         object = value;
197         query = user_data;
198         priv = query->priv;
199
200         for (l = priv->listeners; l != NULL; l = l->next) {
201                 ListenerData *ld = l->data;
202
203                 if (!ld->notified_start) {
204                         GNOME_Evolution_Calendar_stringlist obj_list;
205                         CORBA_Environment ev;
206
207                         obj_list._buffer = GNOME_Evolution_Calendar_stringlist_allocbuf (1);
208                         obj_list._maximum = 1;
209                         obj_list._length = 1;
210                         obj_list._buffer[0] = CORBA_string_dup (object);
211
212                         CORBA_exception_init (&ev);
213                         GNOME_Evolution_Calendar_CalViewListener_notifyObjectsAdded (ld->listener, &obj_list, &ev);
214                         CORBA_exception_free (&ev);
215
216                         CORBA_free (obj_list._buffer);
217                 }
218         }
219 }
220
221 static void
222 impl_EDataCalView_start (PortableServer_Servant servant, CORBA_Environment *ev)
223 {
224         EDataCalView *query;
225         EDataCalViewPrivate *priv;
226         GList *l;
227
228         query = QUERY (bonobo_object_from_servant (servant));
229         priv = query->priv;
230
231         if (priv->started) {
232                 g_hash_table_foreach (priv->matched_objects, (GHFunc) notify_matched_object_cb, query);
233
234                 /* notify all listeners correctly if the query is already done */
235                 for (l = priv->listeners; l != NULL; l = l->next) {
236                         ListenerData *ld = l->data;
237
238                         if (!ld->notified_start) {
239                                 ld->notified_start = TRUE;
240
241                                 if (priv->done && !ld->notified_done) {
242
243                                         ld->notified_done = TRUE;
244
245                                         CORBA_exception_init (ev);
246                                         GNOME_Evolution_Calendar_CalViewListener_notifyQueryDone (
247                                                 ld->listener, priv->done_status, ev);
248                                         CORBA_exception_free (ev);
249                                 }
250                         }
251                 }
252         } else {
253                 priv->started = TRUE;
254                 e_cal_backend_start_query (priv->backend, query);
255
256                 for (l = priv->listeners; l != NULL; l = l->next) {
257                         ListenerData *ld = l->data;
258
259                         ld->notified_start = TRUE;
260                 }
261         }
262 }
263
264 static void
265 e_data_cal_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
266 {
267         EDataCalView *query;
268         EDataCalViewPrivate *priv;
269
270         query = QUERY (object);
271         priv = query->priv;
272         
273         switch (property_id) {
274         case PROP_BACKEND:
275                 priv->backend = E_CAL_BACKEND (g_value_get_object (value));
276                 break;
277         case PROP_LISTENER:
278                 e_data_cal_view_add_listener (query, g_value_get_pointer (value));
279                 break;
280         case PROP_SEXP:
281                 priv->sexp = E_CAL_BACKEND_SEXP (g_value_dup_object (value));
282                 break;
283         default:
284                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
285                 break;
286         }
287 }
288
289 static void
290 e_data_cal_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
291 {
292         EDataCalView *query;
293         EDataCalViewPrivate *priv;
294         
295         query = QUERY (object);
296         priv = query->priv;
297
298         switch (property_id) {
299         case PROP_BACKEND:
300                 g_value_set_object (value, priv->backend);
301         case PROP_LISTENER:
302
303                 if (priv->listeners) {
304                         ListenerData *ld;
305
306                         ld = priv->listeners->data;
307                         g_value_set_pointer (value, ld->listener);
308                 }
309                 break;
310         case PROP_SEXP:
311                 g_value_set_object (value, priv->sexp);
312                 break;
313         default:
314                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
315                 break;
316         }
317 }
318
319 /* Class initialization function for the live search query */
320 static void
321 e_data_cal_view_class_init (EDataCalViewClass *klass)
322 {
323         GObjectClass *object_class;
324         POA_GNOME_Evolution_Calendar_CalView__epv *epv = &klass->epv;
325         GParamSpec *param;
326         
327         object_class = (GObjectClass *) klass;
328
329         parent_class = g_type_class_peek_parent (klass);
330
331         object_class->set_property = e_data_cal_view_set_property;
332         object_class->get_property = e_data_cal_view_get_property;
333         object_class->finalize = e_data_cal_view_finalize;
334
335         epv->start = impl_EDataCalView_start;
336
337         param =  g_param_spec_object ("backend", NULL, NULL, E_TYPE_CAL_BACKEND,
338                                       G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
339         g_object_class_install_property (object_class, PROP_BACKEND, param);
340         param =  g_param_spec_pointer ("listener", NULL, NULL,
341                                       G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
342         g_object_class_install_property (object_class, PROP_LISTENER, param);
343         param =  g_param_spec_object ("sexp", NULL, NULL, E_TYPE_CAL_BACKEND_SEXP,
344                                       G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
345         g_object_class_install_property (object_class, PROP_SEXP, param);
346 }
347
348 /* Object initialization function for the live search query */
349 static void
350 e_data_cal_view_init (EDataCalView *query, EDataCalViewClass *class)
351 {
352         EDataCalViewPrivate *priv;
353
354         priv = g_new0 (EDataCalViewPrivate, 1);
355         query->priv = priv;
356
357         priv->backend = NULL;
358         priv->started = FALSE;
359         priv->done = FALSE;
360         priv->done_status = GNOME_Evolution_Calendar_Success;
361         priv->matched_objects = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
362         priv->listeners = NULL;
363         priv->sexp = NULL;
364 }
365
366 /* Finalize handler for the live search query */
367 static void
368 e_data_cal_view_finalize (GObject *object)
369 {
370         EDataCalView *query;
371         EDataCalViewPrivate *priv;
372
373         g_return_if_fail (object != NULL);
374         g_return_if_fail (IS_QUERY (object));
375
376         query = QUERY (object);
377         priv = query->priv;
378
379         if (priv->backend)
380                 g_object_unref (priv->backend);
381
382         while (priv->listeners) {
383                 ListenerData *ld = priv->listeners->data;
384
385                 if (ld->listener)
386                         bonobo_object_release_unref (ld->listener, NULL);
387                 if (ld->component_listener)
388                         g_object_unref (ld->component_listener);
389                 priv->listeners = g_list_remove (priv->listeners, ld);
390                 g_free (ld);
391         }
392
393         if (priv->matched_objects)
394                 g_hash_table_destroy (priv->matched_objects);
395
396         if (priv->sexp)
397                 g_object_unref (priv->sexp);
398
399         g_free (priv);
400
401         if (G_OBJECT_CLASS (parent_class)->finalize)
402                 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
403 }
404
405 /**
406  * e_data_cal_view_new:
407  * @backend: Calendar backend that the query object will monitor.
408  * @ql: Listener for query results.
409  * @sexp: Sexp that defines the query.
410  * 
411  * Creates a new query engine object that monitors a calendar backend.
412  * 
413  * Return value: A newly-created query object, or NULL on failure.
414  **/
415 EDataCalView *
416 e_data_cal_view_new (ECalBackend *backend,
417                      GNOME_Evolution_Calendar_CalViewListener ql,
418                      ECalBackendSExp *sexp)
419 {
420         EDataCalView *query;
421
422         query = g_object_new (E_DATA_CAL_VIEW_TYPE, "backend", backend, "listener", ql,
423                               "sexp", sexp, NULL);
424
425         return query;
426 }
427
428 /**
429  * e_data_cal_view_add_listener:
430  * @query: A #EDataCalView object.
431  * @ql: A CORBA query listener to add to the list of listeners.
432  *
433  * Adds the given CORBA listener to a #EDataCalView object. This makes the view
434  * object notify that listener when notifying the other listeners already attached
435  * to the view.
436  */
437 void
438 e_data_cal_view_add_listener (EDataCalView *query, GNOME_Evolution_Calendar_CalViewListener ql)
439 {
440         ListenerData *ld;
441         EDataCalViewPrivate *priv;
442         CORBA_Environment ev;
443
444         g_return_if_fail (IS_QUERY (query));
445         g_return_if_fail (ql != CORBA_OBJECT_NIL);
446
447         priv = query->priv;
448
449         ld = g_new0 (ListenerData, 1);
450
451         CORBA_exception_init (&ev);
452         ld->listener = CORBA_Object_duplicate (ql, &ev);
453         CORBA_exception_free (&ev);
454
455         ld->component_listener = e_component_listener_new (ld->listener);
456         g_signal_connect (G_OBJECT (ld->component_listener), "component_died",
457                           G_CALLBACK (listener_died_cb), query);
458
459         priv->listeners = g_list_prepend (priv->listeners, ld);
460 }
461
462 /**
463  * e_data_cal_view_get_text:
464  * @query: A #EDataCalView object.
465  *
466  * Get the expression used for the given query.
467  *
468  * Return value: the query expression used to search.
469  */
470 const char *
471 e_data_cal_view_get_text (EDataCalView *query)
472 {
473         g_return_val_if_fail (IS_QUERY (query), NULL);
474
475         return e_cal_backend_sexp_text (query->priv->sexp);
476 }
477
478 /**
479  * e_data_cal_view_get_object_sexp:
480  * @query: A query object.
481  *
482  * Get the #ECalBackendSExp object used for the given query.
483  *
484  * Return value: The expression object used to search.
485  */
486 ECalBackendSExp *
487 e_data_cal_view_get_object_sexp (EDataCalView *query)
488 {
489         g_return_val_if_fail (IS_QUERY (query), NULL);
490
491         return query->priv->sexp;
492 }
493
494 /**
495  * e_data_cal_view_object_matches:
496  * @query: A query object.
497  * @object: Object to match.
498  *
499  * Compares the given @object to the regular expression used for the
500  * given query.
501  *
502  * Return value: TRUE if the object matches the expression, FALSE if not.
503  */
504 gboolean
505 e_data_cal_view_object_matches (EDataCalView *query, const char *object)
506 {
507         EDataCalViewPrivate *priv;
508         
509         g_return_val_if_fail (query != NULL, FALSE);
510         g_return_val_if_fail (IS_QUERY (query), FALSE);
511         g_return_val_if_fail (object != NULL, FALSE);
512
513         priv = query->priv;
514         
515         return e_cal_backend_sexp_match_object (priv->sexp, object, priv->backend);
516 }
517
518 static void
519 add_object_to_list (gpointer key, gpointer value, gpointer user_data)
520 {
521         GList **list = user_data;
522
523         *list = g_list_append (*list, value);
524 }
525
526 /**
527  * e_data_cal_view_get_matched_objects:
528  * @query: A query object.
529  *
530  * Gets the list of objects already matched for the given query.
531  *
532  * Return value: A list of matched objects.
533  */
534 GList *
535 e_data_cal_view_get_matched_objects (EDataCalView *query)
536 {
537         EDataCalViewPrivate *priv;
538         GList *list = NULL;
539
540         g_return_val_if_fail (IS_QUERY (query), NULL);
541
542         priv = query->priv;
543
544         g_hash_table_foreach (priv->matched_objects, (GHFunc) add_object_to_list, &list);
545
546         return list;
547 }
548
549 /**
550  * e_data_cal_view_is_started:
551  * @query: A query object.
552  *
553  * Checks whether the given query has already been started.
554  *
555  * Return value: TRUE if the query has already been started, FALSE otherwise.
556  */
557 gboolean
558 e_data_cal_view_is_started (EDataCalView *query)
559 {
560         EDataCalViewPrivate *priv;
561
562         g_return_val_if_fail (IS_QUERY (query), FALSE);
563
564         priv = query->priv;
565
566         return priv->started;
567 }
568
569 /**
570  * e_data_cal_view_is_done:
571  * @query: A query object.
572  *
573  * Checks whether the given query is already done. Being done means the initial
574  * matching of objects have been finished, not that no more notifications about
575  * changes will be sent. In fact, even after done, notifications will still be sent
576  * if there are changes in the objects matching the query search expression.
577  *
578  * Return value: TRUE if the query is done, FALSE if still in progress.
579  */
580 gboolean
581 e_data_cal_view_is_done (EDataCalView *query)
582 {
583         EDataCalViewPrivate *priv;
584
585         g_return_val_if_fail (IS_QUERY (query), FALSE);
586
587         priv = query->priv;
588
589         return priv->done;
590 }
591
592 /**
593  * e_data_cal_view_get_done_status:
594  * @query: A query object.
595  *
596  * Gets the status code obtained when the initial matching of objects was done
597  * for the given query.
598  *
599  * Return value: The query status.
600  */
601 GNOME_Evolution_Calendar_CallStatus
602 e_data_cal_view_get_done_status (EDataCalView *query)
603 {
604         EDataCalViewPrivate *priv;
605
606         g_return_val_if_fail (IS_QUERY (query), FALSE);
607
608         priv = query->priv;
609
610         if (priv->done)
611                 return priv->done_status;
612
613         return GNOME_Evolution_Calendar_Success;
614 }
615
616 /**
617  * e_data_cal_view_notify_objects_added:
618  * @query: A query object.
619  * @objects: List of objects that have been added.
620  *
621  * Notifies all query listeners of the addition of a list of objects.
622  */
623 void
624 e_data_cal_view_notify_objects_added (EDataCalView *query, const GList *objects)
625 {
626         EDataCalViewPrivate *priv;
627         GNOME_Evolution_Calendar_stringlist obj_list;
628         CORBA_Environment ev;
629         const GList *l;
630         int num_objs, i;
631         
632         g_return_if_fail (query != NULL);
633         g_return_if_fail (IS_QUERY (query));
634
635         priv = query->priv;
636         g_return_if_fail (priv->listeners != CORBA_OBJECT_NIL);
637
638         num_objs = g_list_length ((GList*)objects);
639         if (num_objs <= 0)
640                 return;
641
642         obj_list._buffer = GNOME_Evolution_Calendar_stringlist_allocbuf (num_objs);
643         obj_list._maximum = num_objs;
644         obj_list._length = num_objs;
645
646         for (l = objects, i = 0; l; l = l->next, i++) {
647                 obj_list._buffer[i] = CORBA_string_dup (l->data);
648
649                 /* update our cache */
650                 add_object_to_cache (query, l->data);
651         }
652
653         for (l = priv->listeners; l != NULL; l = l->next) {
654                 ListenerData *ld = l->data;
655
656                 CORBA_exception_init (&ev);
657
658                 GNOME_Evolution_Calendar_CalViewListener_notifyObjectsAdded (ld->listener, &obj_list, &ev);
659                 if (BONOBO_EX (&ev))
660                         g_warning (G_STRLOC ": could not notify the listener of object addition");
661
662                 CORBA_exception_free (&ev);
663         }
664
665         CORBA_free (obj_list._buffer);
666 }
667
668 /**
669  * e_data_cal_view_notify_objects_added_1:
670  * @query: A query object.
671  * @object: The object that has been added.
672  *
673  * Notifies all the query listeners of the addition of a single object.
674  */
675 void
676 e_data_cal_view_notify_objects_added_1 (EDataCalView *query, const char *object)
677 {
678         EDataCalViewPrivate *priv;
679         GList objects;
680         
681         g_return_if_fail (query != NULL);
682         g_return_if_fail (IS_QUERY (query));
683         g_return_if_fail (object != NULL);
684
685         priv = query->priv;
686         g_return_if_fail (priv->listeners != CORBA_OBJECT_NIL);
687
688         objects.next = objects.prev = NULL;
689         objects.data = (gpointer)object;
690
691         e_data_cal_view_notify_objects_added (query, &objects);
692 }
693
694 /**
695  * e_data_cal_view_notify_objects_modified:
696  * @query: A query object.
697  * @objects: List of modified objects.
698  *
699  * Notifies all query listeners of the modification of a list of objects.
700  */
701 void
702 e_data_cal_view_notify_objects_modified (EDataCalView *query, const GList *objects)
703 {
704         EDataCalViewPrivate *priv;
705         GNOME_Evolution_Calendar_CalObjUIDSeq obj_list;
706         CORBA_Environment ev;
707         const GList *l;
708         int num_objs, i;
709         
710         g_return_if_fail (query != NULL);
711         g_return_if_fail (IS_QUERY (query));
712
713         priv = query->priv;
714         g_return_if_fail (priv->listeners != CORBA_OBJECT_NIL);
715
716         num_objs = g_list_length ((GList*)objects);
717         if (num_objs <= 0)
718                 return;
719
720         obj_list._buffer = GNOME_Evolution_Calendar_stringlist_allocbuf (num_objs);
721         obj_list._maximum = num_objs;
722         obj_list._length = num_objs;
723
724         for (l = objects, i = 0; l; l = l->next, i++) {
725                 obj_list._buffer[i] = CORBA_string_dup (l->data);
726
727                 /* update our cache */
728                 add_object_to_cache (query, l->data);
729         }
730
731         for (l = priv->listeners; l != NULL; l = l->next) {
732                 ListenerData *ld = l->data;
733
734                 CORBA_exception_init (&ev);
735
736                 GNOME_Evolution_Calendar_CalViewListener_notifyObjectsModified (ld->listener, &obj_list, &ev);
737                 if (BONOBO_EX (&ev))
738                         g_warning (G_STRLOC ": could not notify the listener of object modification");
739
740                 CORBA_exception_free (&ev);
741         }
742
743         CORBA_free (obj_list._buffer);
744 }
745
746 /**
747  * e_data_cal_view_notify_objects_modified_1:
748  * @query: A query object.
749  * @object: The modified object.
750  *
751  * Notifies all query listeners of the modification of a single object.
752  */
753 void
754 e_data_cal_view_notify_objects_modified_1 (EDataCalView *query, const char *object)
755 {
756         EDataCalViewPrivate *priv;
757         GList objects;
758         
759         g_return_if_fail (query != NULL);
760         g_return_if_fail (IS_QUERY (query));
761         g_return_if_fail (object != NULL);
762
763         priv = query->priv;
764         g_return_if_fail (priv->listeners != CORBA_OBJECT_NIL);
765
766         objects.next = objects.prev = NULL;
767         objects.data = (gpointer)object;
768         
769         e_data_cal_view_notify_objects_modified (query, &objects);
770 }
771
772 /**
773  * e_data_cal_view_notify_objects_removed:
774  * @query: A query object.
775  * @ids: List of IDs for the objects that have been removed.
776  *
777  * Notifies all query listener of the removal of a list of objects.
778  */
779 void
780 e_data_cal_view_notify_objects_removed (EDataCalView *query, const GList *ids)
781 {
782         EDataCalViewPrivate *priv;
783         GNOME_Evolution_Calendar_CalObjIDSeq id_list;
784         CORBA_Environment ev;
785         const GList *l;
786         int num_ids, i;
787         
788         g_return_if_fail (query != NULL);
789         g_return_if_fail (IS_QUERY (query));
790
791         priv = query->priv;
792         g_return_if_fail (priv->listeners != CORBA_OBJECT_NIL);
793
794         num_ids = g_list_length ((GList*)ids);
795         if (num_ids <= 0)
796                 return;
797         
798         id_list._buffer = GNOME_Evolution_Calendar_CalObjIDSeq_allocbuf (num_ids);
799         id_list._maximum = num_ids;
800         id_list._length = num_ids;
801         
802         i = 0;
803         for (l = ids; l; l = l->next, i++) {
804                 ECalComponentId *id = l->data;
805                 GNOME_Evolution_Calendar_CalObjID *c_id = &id_list._buffer[i];
806
807                 c_id->uid = CORBA_string_dup (id->uid);
808                 
809                 if (id->rid)
810                         c_id->rid = CORBA_string_dup (id->rid);
811                 else
812                         c_id->rid = CORBA_string_dup ("");      
813
814                 /* update our cache */
815                 remove_object_from_cache (query, l->data);
816         }
817  
818         for (l = priv->listeners; l != NULL; l = l->next) {
819                 ListenerData *ld = l->data;
820
821                 CORBA_exception_init (&ev);
822
823                 GNOME_Evolution_Calendar_CalViewListener_notifyObjectsRemoved (ld->listener, &id_list, &ev);
824                 if (BONOBO_EX (&ev))
825                         g_warning (G_STRLOC ": could not notify the listener of object removal");
826
827                 CORBA_exception_free (&ev);
828         }
829
830         CORBA_free (id_list._buffer);
831 }
832
833 /**
834  * e_data_cal_view_notify_objects_removed:
835  * @query: A query object.
836  * @id: Id of the removed object.
837  *
838  * Notifies all query listener of the removal of a single object.
839  */
840 void
841 e_data_cal_view_notify_objects_removed_1 (EDataCalView *query, const ECalComponentId *id)
842 {
843         EDataCalViewPrivate *priv;
844         GList ids;
845         
846         g_return_if_fail (query != NULL);
847         g_return_if_fail (IS_QUERY (query));
848         g_return_if_fail (id != NULL);
849
850         priv = query->priv;
851         g_return_if_fail (priv->listeners != CORBA_OBJECT_NIL);
852
853         ids.next = ids.prev = NULL;
854         ids.data = (gpointer)id;
855         
856         e_data_cal_view_notify_objects_removed (query, &ids);
857 }
858
859 /**
860  * e_data_cal_view_notify_progress:
861  * @query: A query object.
862  * @message: Progress message to send to listeners.
863  * @percent: Percentage completed.
864  *
865  * Notifies all query listeners of progress messages.
866  */
867 void
868 e_data_cal_view_notify_progress (EDataCalView *query, const char *message, int percent)
869 {
870         EDataCalViewPrivate *priv;      
871         CORBA_Environment ev;
872         GList *l;
873
874         g_return_if_fail (query != NULL);
875         g_return_if_fail (IS_QUERY (query));
876
877         priv = query->priv;
878         g_return_if_fail (priv->listeners != CORBA_OBJECT_NIL);
879
880         for (l = priv->listeners; l != NULL; l = l->next) {
881                 ListenerData *ld = l->data;
882
883                 CORBA_exception_init (&ev);
884
885                 GNOME_Evolution_Calendar_CalViewListener_notifyQueryProgress (ld->listener, message, percent, &ev);
886                 if (BONOBO_EX (&ev))
887                         g_warning (G_STRLOC ": could not notify the listener of query progress");
888
889                 CORBA_exception_free (&ev);
890         }
891 }
892
893 /**
894  * e_data_cal_view_notify_done:
895  * @query: A query object.
896  * @status: Query completion status code.
897  *
898  * Notifies all query listeners of the completion of the query, including a
899  * status code.
900  */
901 void
902 e_data_cal_view_notify_done (EDataCalView *query, GNOME_Evolution_Calendar_CallStatus status)
903 {
904         EDataCalViewPrivate *priv;      
905         CORBA_Environment ev;
906         GList *l;
907
908         g_return_if_fail (query != NULL);
909         g_return_if_fail (IS_QUERY (query));
910
911         priv = query->priv;
912         g_return_if_fail (priv->listeners != CORBA_OBJECT_NIL);
913
914         priv->done = TRUE;
915         priv->done_status = status;
916
917         for (l = priv->listeners; l != NULL; l = l->next) {
918                 ListenerData *ld = l->data;
919
920                 CORBA_exception_init (&ev);
921
922                 GNOME_Evolution_Calendar_CalViewListener_notifyQueryDone (ld->listener, status, &ev);
923                 if (BONOBO_EX (&ev))
924                         g_warning (G_STRLOC ": could not notify the listener of query completion");
925
926                 CORBA_exception_free (&ev);
927         }
928 }