Remove marshallers that are already in GLib (#400970)
[platform/upstream/evolution-data-server.git] / libedataserver / e-source-group.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* e-source-group.c
3  *
4  * Copyright (C) 2003  Ximian, Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Ettore Perazzoli <ettore@ximian.com>
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28 #include "e-uid.h"
29 #include "e-source-group.h"
30
31 /* Private members.  */
32
33 struct _ESourceGroupPrivate {
34         char *uid;
35         char *name;
36         char *base_uri;
37
38         GSList *sources;
39
40         gboolean ignore_source_changed;
41         gboolean readonly;
42 };
43
44
45 /* Signals.  */
46
47 enum {
48         CHANGED,
49         SOURCE_REMOVED,
50         SOURCE_ADDED,
51         LAST_SIGNAL
52 };
53 static unsigned int signals[LAST_SIGNAL] = { 0 };
54
55
56 /* Callbacks.  */
57
58 static void
59 source_changed_callback (ESource *source,
60                          ESourceGroup *group)
61 {
62         if (! group->priv->ignore_source_changed)
63                 g_signal_emit (group, signals[CHANGED], 0);
64 }
65
66
67 /* GObject methods.  */
68
69 G_DEFINE_TYPE (ESourceGroup, e_source_group, G_TYPE_OBJECT);
70
71 static void
72 impl_dispose (GObject *object)
73 {
74         ESourceGroupPrivate *priv = E_SOURCE_GROUP (object)->priv;
75
76         if (priv->sources != NULL) {
77                 GSList *p;
78
79                 for (p = priv->sources; p != NULL; p = p->next) {
80                         ESource *source = E_SOURCE (p->data);
81
82                         g_signal_handlers_disconnect_by_func (source,
83                                                               G_CALLBACK (source_changed_callback),
84                                                               object);
85                         g_object_unref (source);
86                 }
87
88                 g_slist_free (priv->sources);
89                 priv->sources = NULL;
90         }
91
92         (* G_OBJECT_CLASS (e_source_group_parent_class)->dispose) (object);
93 }
94
95 static void
96 impl_finalize (GObject *object)
97 {
98         ESourceGroupPrivate *priv = E_SOURCE_GROUP (object)->priv;
99
100         g_free (priv->uid);
101         g_free (priv->name);
102         g_free (priv->base_uri);
103         g_free (priv);
104
105         (* G_OBJECT_CLASS (e_source_group_parent_class)->finalize) (object);
106 }
107
108
109 /* Initialization.  */
110
111 static void
112 e_source_group_class_init (ESourceGroupClass *class)
113 {
114         GObjectClass *object_class = G_OBJECT_CLASS (class);
115
116         object_class->dispose  = impl_dispose;
117         object_class->finalize = impl_finalize;
118
119         signals[CHANGED] = 
120                 g_signal_new ("changed",
121                               G_OBJECT_CLASS_TYPE (object_class),
122                               G_SIGNAL_RUN_LAST,
123                               G_STRUCT_OFFSET (ESourceGroupClass, changed),
124                               NULL, NULL,
125                               g_cclosure_marshal_VOID__VOID,
126                               G_TYPE_NONE, 0);
127
128         signals[SOURCE_ADDED] = 
129                 g_signal_new ("source_added",
130                               G_OBJECT_CLASS_TYPE (object_class),
131                               G_SIGNAL_RUN_LAST,
132                               G_STRUCT_OFFSET (ESourceGroupClass, source_added),
133                               NULL, NULL,
134                               g_cclosure_marshal_VOID__OBJECT,
135                               G_TYPE_NONE, 1,
136                               G_TYPE_OBJECT);
137         signals[SOURCE_REMOVED] = 
138                 g_signal_new ("source_removed",
139                               G_OBJECT_CLASS_TYPE (object_class),
140                               G_SIGNAL_RUN_LAST,
141                               G_STRUCT_OFFSET (ESourceGroupClass, source_removed),
142                               NULL, NULL,
143                               g_cclosure_marshal_VOID__OBJECT,
144                               G_TYPE_NONE, 1,
145                               G_TYPE_OBJECT);
146 }
147
148 static void
149 e_source_group_init (ESourceGroup *source_group)
150 {
151         ESourceGroupPrivate *priv;
152
153         priv = g_new0 (ESourceGroupPrivate, 1);
154         source_group->priv = priv;
155 }
156
157 /* Public methods.  */
158
159 ESourceGroup *
160 e_source_group_new (const char *name,
161                     const char *base_uri)
162 {
163         ESourceGroup *new;
164
165         g_return_val_if_fail (name != NULL, NULL);
166         g_return_val_if_fail (base_uri != NULL, NULL);
167
168         new = g_object_new (e_source_group_get_type (), NULL);
169         new->priv->uid = e_uid_new ();
170
171         e_source_group_set_name (new, name);
172         e_source_group_set_base_uri (new, base_uri);
173
174         return new;
175 }
176
177 ESourceGroup *
178 e_source_group_new_from_xml (const char *xml)
179 {
180         xmlDocPtr doc;
181         ESourceGroup *group;
182
183         doc = xmlParseDoc ((char *) xml);
184         if (doc == NULL)
185                 return NULL;
186
187         group = e_source_group_new_from_xmldoc (doc);
188         xmlFreeDoc (doc);
189
190         return group;
191 }
192
193 ESourceGroup *
194 e_source_group_new_from_xmldoc (xmlDocPtr doc)
195 {
196         xmlNodePtr root, p;
197         xmlChar *uid;
198         xmlChar *name;
199         xmlChar *base_uri;
200         xmlChar *readonly_str;
201         ESourceGroup *new = NULL;
202
203         g_return_val_if_fail (doc != NULL, NULL);
204
205         root = doc->children;
206         if (strcmp (root->name, "group") != 0)
207                 return NULL;
208
209         uid = xmlGetProp (root, "uid");
210         name = xmlGetProp (root, "name");
211         base_uri = xmlGetProp (root, "base_uri");
212         readonly_str = xmlGetProp (root, "readonly");
213
214         if (uid == NULL || name == NULL || base_uri == NULL)
215                 goto done;
216
217         new = g_object_new (e_source_group_get_type (), NULL);
218
219         if (!new)
220                 goto done;
221
222         new->priv->uid = g_strdup (uid);
223
224         e_source_group_set_name (new, name);
225         e_source_group_set_base_uri (new, base_uri);
226         
227         for (p = root->children; p != NULL; p = p->next) {
228                 ESource *new_source = e_source_new_from_xml_node (p);
229
230                 if (new_source == NULL) {
231                         g_object_unref (new);
232                         goto done;
233                 }
234                 e_source_group_add_source (new, new_source, -1);
235         }
236
237         e_source_group_set_readonly (new, readonly_str && !strcmp (readonly_str, "yes"));
238
239  done:
240         if (uid != NULL)
241                 xmlFree (uid);
242
243         if (name != NULL)
244                 xmlFree (name);
245         if (base_uri != NULL)
246                 xmlFree (base_uri);
247         if (readonly_str != NULL)
248                 xmlFree (readonly_str);
249         return new;
250 }
251
252 gboolean
253 e_source_group_update_from_xml (ESourceGroup *group,
254                                 const char *xml,
255                                 gboolean *changed_return)
256 {
257         xmlDocPtr xmldoc;
258         gboolean success;
259
260         g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
261         g_return_val_if_fail (xml != NULL, FALSE);
262
263         xmldoc = xmlParseDoc ((char *) xml);
264
265         success = e_source_group_update_from_xmldoc (group, xmldoc, changed_return);
266
267         xmlFreeDoc (xmldoc);
268
269         return success;
270 }
271
272 gboolean
273 e_source_group_update_from_xmldoc (ESourceGroup *group,
274                                    xmlDocPtr doc,
275                                    gboolean *changed_return)
276 {
277         GHashTable *new_sources_hash;
278         GSList *new_sources_list = NULL;
279         xmlNodePtr root, nodep;
280         xmlChar *name, *base_uri, *readonly_str;
281         gboolean readonly;
282         gboolean changed = FALSE;
283         GSList *p, *q;
284
285         g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
286         g_return_val_if_fail (doc != NULL, FALSE);
287
288         *changed_return = FALSE;
289
290         root = doc->children;
291         if (strcmp (root->name, "group") != 0)
292                 return FALSE;
293
294         name = xmlGetProp (root, "name");
295         if (name == NULL)
296                 return FALSE;
297
298         base_uri = xmlGetProp (root, "base_uri");
299         if (base_uri == NULL) {
300                 xmlFree (name);
301                 return FALSE;
302         }
303
304         if (strcmp (group->priv->name, name) != 0) {
305                 g_free (group->priv->name);
306                 group->priv->name = g_strdup (name);
307                 changed = TRUE;
308         }
309         xmlFree (name);
310
311         if (strcmp (group->priv->base_uri, base_uri) != 0) {
312                 g_free (group->priv->base_uri);
313                 group->priv->base_uri = g_strdup (base_uri);
314                 changed = TRUE;
315         }
316         xmlFree (base_uri);
317
318         readonly_str = xmlGetProp (root, "readonly");
319         readonly = readonly_str && !strcmp (readonly_str, "yes");
320         if (readonly != group->priv->readonly) {
321                 group->priv->readonly = readonly;
322                 changed = TRUE;
323         }
324         xmlFree (readonly_str);
325         
326         new_sources_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
327
328         for (nodep = root->children; nodep != NULL; nodep = nodep->next) {
329                 ESource *existing_source;
330                 char *uid = e_source_uid_from_xml_node (nodep);
331
332                 if (uid == NULL)
333                         continue;
334
335                 existing_source = e_source_group_peek_source_by_uid (group, uid);
336                 if (g_hash_table_lookup (new_sources_hash, existing_source) != NULL)
337                         continue;
338
339                 if (existing_source == NULL) {
340                         ESource *new_source = e_source_new_from_xml_node (nodep);
341
342                         if (new_source != NULL) {
343                                 e_source_set_group (new_source, group);
344                                 g_signal_connect (new_source, "changed", G_CALLBACK (source_changed_callback), group);
345                                 new_sources_list = g_slist_prepend (new_sources_list, new_source);
346
347                                 g_hash_table_insert (new_sources_hash, new_source, new_source);
348
349                                 g_signal_emit (group, signals[SOURCE_ADDED], 0, new_source);
350                                 changed = TRUE;
351                         }
352                 } else {
353                         gboolean source_changed;
354
355                         group->priv->ignore_source_changed ++;
356
357                         if (e_source_update_from_xml_node (existing_source, nodep, &source_changed)) {
358                                 new_sources_list = g_slist_prepend (new_sources_list, existing_source);
359                                 g_object_ref (existing_source);
360                                 g_hash_table_insert (new_sources_hash, existing_source, existing_source);
361
362                                 if (source_changed)
363                                         changed = TRUE;
364                         }
365
366                         group->priv->ignore_source_changed --;
367                 }
368
369                 g_free (uid);
370         }
371
372         new_sources_list = g_slist_reverse (new_sources_list);
373
374         /* Emit "group_removed" and disconnect the "changed" signal for all the
375            groups that we haven't found in the new list.  */
376         q = new_sources_list;
377         for (p = group->priv->sources; p != NULL; p = p->next) {
378                 ESource *source = E_SOURCE (p->data);
379
380                 if (g_hash_table_lookup (new_sources_hash, source) == NULL) {
381                         changed = TRUE;
382
383                         g_signal_emit (group, signals[SOURCE_REMOVED], 0, source);
384                         g_signal_handlers_disconnect_by_func (source, source_changed_callback, group);
385                 }
386
387                 if (! changed && q != NULL) {
388                         if (q->data != p->data)
389                                 changed = TRUE;
390                         q = q->next;
391                 }
392         }
393
394         g_hash_table_destroy (new_sources_hash);
395
396         /* Replace the original group list with the new one.  */
397         g_slist_foreach (group->priv->sources, (GFunc) g_object_unref, NULL);
398         g_slist_free (group->priv->sources);
399
400         group->priv->sources = new_sources_list;
401
402         /* FIXME if the order changes, the function doesn't notice.  */
403
404         if (changed) {
405                 g_signal_emit (group, signals[CHANGED], 0);
406                 *changed_return = TRUE;
407         }
408
409         return TRUE;            /* Success. */
410 }
411
412 char *
413 e_source_group_uid_from_xmldoc (xmlDocPtr doc)
414 {
415         xmlNodePtr root = doc->children;
416         xmlChar *name;
417         char *retval;
418         
419         if (root && root->name) {
420                 if (strcmp (root->name, "group") != 0)
421                         return NULL;
422         }
423         else 
424                 return NULL;
425
426         name = xmlGetProp (root, "uid");
427         if (name == NULL)
428                 return NULL;
429
430         retval = g_strdup (name);
431         xmlFree (name);
432         return retval;
433 }
434
435 void
436 e_source_group_set_name (ESourceGroup *group,
437                          const char *name)
438 {
439         g_return_if_fail (E_IS_SOURCE_GROUP (group));
440         g_return_if_fail (name != NULL);
441
442         if (group->priv->readonly)
443                 return;
444         
445         if (group->priv->name != NULL &&
446             strcmp (group->priv->name, name) == 0)
447                 return;
448
449         g_free (group->priv->name);
450         group->priv->name = g_strdup (name);
451
452         g_signal_emit (group, signals[CHANGED], 0);
453 }
454
455 void e_source_group_set_base_uri (ESourceGroup *group,
456                                   const char *base_uri)
457 {
458         g_return_if_fail (E_IS_SOURCE_GROUP (group));
459         g_return_if_fail (base_uri != NULL);
460         
461         if (group->priv->readonly)
462                 return;
463         
464         if (group->priv->base_uri == base_uri)
465                 return;
466
467         g_free (group->priv->base_uri);
468         group->priv->base_uri = g_strdup (base_uri);
469
470         g_signal_emit (group, signals[CHANGED], 0);
471 }
472
473 void e_source_group_set_readonly (ESourceGroup *group,
474                                   gboolean      readonly)
475 {
476         GSList *i;
477         
478         g_return_if_fail (E_IS_SOURCE_GROUP (group));
479         
480         if (group->priv->readonly)
481                 return;
482         
483         if (group->priv->readonly == readonly)
484                 return;
485
486         group->priv->readonly = readonly;
487         for (i = group->priv->sources; i != NULL; i = i->next)
488                 e_source_set_readonly (E_SOURCE (i->data), readonly);   
489
490         g_signal_emit (group, signals[CHANGED], 0);
491 }
492
493 const char *
494 e_source_group_peek_uid (ESourceGroup *group)
495 {
496         g_return_val_if_fail (E_IS_SOURCE_GROUP (group), NULL);
497
498         return group->priv->uid;
499 }
500
501 const char *
502 e_source_group_peek_name (ESourceGroup *group)
503 {
504         g_return_val_if_fail (E_IS_SOURCE_GROUP (group), NULL);
505
506         return group->priv->name;
507 }
508
509 const char *
510 e_source_group_peek_base_uri (ESourceGroup *group)
511 {
512         g_return_val_if_fail (E_IS_SOURCE_GROUP (group), NULL);
513
514         return group->priv->base_uri;
515 }
516
517 gboolean
518 e_source_group_get_readonly (ESourceGroup *group)
519 {
520         g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
521
522         return group->priv->readonly;
523 }
524
525 GSList *
526 e_source_group_peek_sources (ESourceGroup *group)
527 {
528         g_return_val_if_fail (E_IS_SOURCE_GROUP (group), NULL);
529
530         return group->priv->sources;
531 }
532
533 ESource *
534 e_source_group_peek_source_by_uid (ESourceGroup *group,
535                                    const char *uid)
536 {
537         GSList *p;
538
539         for (p = group->priv->sources; p != NULL; p = p->next) {
540                 if (strcmp (e_source_peek_uid (E_SOURCE (p->data)), uid) == 0)
541                         return E_SOURCE (p->data);
542         }
543
544         return NULL;
545 }
546
547 ESource *
548 e_source_group_peek_source_by_name (ESourceGroup *group,
549                                     const char *name)
550 {
551         GSList *p;
552
553         for (p = group->priv->sources; p != NULL; p = p->next) {
554                 if (strcmp (e_source_peek_name (E_SOURCE (p->data)), name) == 0)
555                         return E_SOURCE (p->data);
556         }
557
558         return NULL;
559 }
560
561 gboolean
562 e_source_group_add_source (ESourceGroup *group,
563                            ESource *source,
564                            int position)
565 {
566         g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
567
568         if (group->priv->readonly)
569                 return FALSE;
570         
571         if (e_source_group_peek_source_by_uid (group, e_source_peek_uid (source)) != NULL)
572                 return FALSE;
573
574         e_source_set_group (source, group);
575         e_source_set_readonly (source, group->priv->readonly);
576         g_object_ref (source);
577
578         g_signal_connect (source, "changed", G_CALLBACK (source_changed_callback), group);
579
580         group->priv->sources = g_slist_insert (group->priv->sources, source, position);
581         g_signal_emit (group, signals[SOURCE_ADDED], 0, source);
582         g_signal_emit (group, signals[CHANGED], 0);
583
584         return TRUE;
585 }
586
587 gboolean
588 e_source_group_remove_source (ESourceGroup *group,
589                               ESource *source)
590 {
591         GSList *p;
592
593         g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
594         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
595
596         if (group->priv->readonly)
597                 return FALSE;
598
599         for (p = group->priv->sources; p != NULL; p = p->next) {
600                 if (E_SOURCE (p->data) == source) {
601                         group->priv->sources = g_slist_remove_link (group->priv->sources, p);
602                         g_signal_handlers_disconnect_by_func (source,
603                                                               G_CALLBACK (source_changed_callback),
604                                                               group);
605                         g_signal_emit (group, signals[SOURCE_REMOVED], 0, source);
606                         g_signal_emit (group, signals[CHANGED], 0);
607                         return TRUE;
608                 }
609         }
610
611         return FALSE;
612 }
613
614 gboolean
615 e_source_group_remove_source_by_uid (ESourceGroup *group,
616                                      const char *uid)
617 {
618         GSList *p;
619
620         g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
621         g_return_val_if_fail (uid != NULL, FALSE);
622
623         if (group->priv->readonly)
624                 return FALSE;
625         
626         for (p = group->priv->sources; p != NULL; p = p->next) {
627                 ESource *source = E_SOURCE (p->data);
628
629                 if (strcmp (e_source_peek_uid (source), uid) == 0) {
630                         group->priv->sources = g_slist_remove_link (group->priv->sources, p);
631                         g_signal_handlers_disconnect_by_func (source,
632                                                               G_CALLBACK (source_changed_callback),
633                                                               group);
634                         g_signal_emit (group, signals[SOURCE_REMOVED], 0, source);
635                         g_signal_emit (group, signals[CHANGED], 0);
636                         return TRUE;
637                 }
638         }
639
640         return FALSE;
641 }
642
643
644 char *
645 e_source_group_to_xml (ESourceGroup *group)
646 {
647         xmlDocPtr doc;
648         xmlNodePtr root;
649         xmlChar *xml_buffer;
650         char *returned_buffer;
651         int xml_buffer_size;
652         GSList *p;
653
654         doc = xmlNewDoc ("1.0");
655
656         root = xmlNewDocNode (doc, NULL, "group", NULL);
657         xmlSetProp (root, "uid", e_source_group_peek_uid (group));
658         xmlSetProp (root, "name", e_source_group_peek_name (group));
659         xmlSetProp (root, "base_uri", e_source_group_peek_base_uri (group));
660         xmlSetProp (root, "readonly", group->priv->readonly ? "yes" : "no");
661         
662         xmlDocSetRootElement (doc, root);
663
664         for (p = group->priv->sources; p != NULL; p = p->next)
665                 e_source_dump_to_xml_node (E_SOURCE (p->data), root);
666
667         xmlDocDumpMemory (doc, &xml_buffer, &xml_buffer_size);
668         xmlFreeDoc (doc);
669
670         returned_buffer = g_malloc (xml_buffer_size + 1);
671         memcpy (returned_buffer, xml_buffer, xml_buffer_size);
672         returned_buffer [xml_buffer_size] = '\0';
673         xmlFree (xml_buffer);
674
675         return returned_buffer;
676 }