~ubuntu-branches/ubuntu/natty/evolution-data-server/natty

« back to all changes in this revision

Viewing changes to .pc/02_fix_sources_migration.patch/libedataserver/e-source-group.c

  • Committer: Bazaar Package Importer
  • Author(s): Robert Ancell
  • Date: 2011-02-07 14:11:56 UTC
  • mfrom: (1.1.84 upstream)
  • Revision ID: james.westby@ubuntu.com-20110207141156-jakojbbkzee62447
Tags: 2.32.2-0ubuntu1
* New upstream release
* debian/patches/01_various_linking_issues.patch:
* debian/patches/02_fix_sources_migration.patch:
* debian/patches/199-git-backport-2.32.1-b08a6a1_to_2d0f4a3.patch:
* debian/patches/199-git-backport-2.32.1-to-b08a6a1.patch:
* debian/patches/199-git-skip-empty-cache-files-ee21a86.patch:
  - Fixed upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
 
/* e-source-group.c
3
 
 *
4
 
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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., 51 Franklin Street, Fifth Floor,
18
 
 * Boston, MA 02110-1301, 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
 
#define XC (const xmlChar *)
32
 
#define GC (const gchar *)
33
 
 
34
 
/* Private members.  */
35
 
 
36
 
struct _ESourceGroupPrivate {
37
 
        gchar *uid;
38
 
        gchar *name;
39
 
        gchar *base_uri;
40
 
 
41
 
        GSList *sources;
42
 
 
43
 
        gboolean ignore_source_changed;
44
 
        gboolean readonly;
45
 
 
46
 
        GHashTable *properties;
47
 
};
48
 
 
49
 
/* Signals.  */
50
 
 
51
 
enum {
52
 
        CHANGED,
53
 
        SOURCE_REMOVED,
54
 
        SOURCE_ADDED,
55
 
        LAST_SIGNAL
56
 
};
57
 
static guint signals[LAST_SIGNAL] = { 0 };
58
 
 
59
 
/* Callbacks.  */
60
 
 
61
 
static void
62
 
source_changed_callback (ESource *source,
63
 
                         ESourceGroup *group)
64
 
{
65
 
        if (!group->priv->ignore_source_changed)
66
 
                g_signal_emit (group, signals[CHANGED], 0);
67
 
}
68
 
 
69
 
/* GObject methods.  */
70
 
 
71
 
G_DEFINE_TYPE (ESourceGroup, e_source_group, G_TYPE_OBJECT)
72
 
 
73
 
static void
74
 
impl_dispose (GObject *object)
75
 
{
76
 
        ESourceGroupPrivate *priv = E_SOURCE_GROUP (object)->priv;
77
 
 
78
 
        if (priv->sources != NULL) {
79
 
                GSList *p;
80
 
 
81
 
                for (p = priv->sources; p != NULL; p = p->next) {
82
 
                        ESource *source = E_SOURCE (p->data);
83
 
 
84
 
                        g_signal_handlers_disconnect_by_func (source,
85
 
                                                              G_CALLBACK (source_changed_callback),
86
 
                                                              object);
87
 
                        g_object_unref (source);
88
 
                }
89
 
 
90
 
                g_slist_free (priv->sources);
91
 
                priv->sources = NULL;
92
 
        }
93
 
 
94
 
        (* G_OBJECT_CLASS (e_source_group_parent_class)->dispose) (object);
95
 
}
96
 
 
97
 
static void
98
 
impl_finalize (GObject *object)
99
 
{
100
 
        ESourceGroupPrivate *priv = E_SOURCE_GROUP (object)->priv;
101
 
 
102
 
        g_free (priv->uid);
103
 
        g_free (priv->name);
104
 
        g_free (priv->base_uri);
105
 
 
106
 
        g_hash_table_destroy (priv->properties);
107
 
 
108
 
        g_free (priv);
109
 
 
110
 
        (* G_OBJECT_CLASS (e_source_group_parent_class)->finalize) (object);
111
 
}
112
 
 
113
 
/* Initialization.  */
114
 
 
115
 
static void
116
 
e_source_group_class_init (ESourceGroupClass *class)
117
 
{
118
 
        GObjectClass *object_class = G_OBJECT_CLASS (class);
119
 
 
120
 
        object_class->dispose  = impl_dispose;
121
 
        object_class->finalize = impl_finalize;
122
 
 
123
 
        signals[CHANGED] =
124
 
                g_signal_new ("changed",
125
 
                              G_OBJECT_CLASS_TYPE (object_class),
126
 
                              G_SIGNAL_RUN_LAST,
127
 
                              G_STRUCT_OFFSET (ESourceGroupClass, changed),
128
 
                              NULL, NULL,
129
 
                              g_cclosure_marshal_VOID__VOID,
130
 
                              G_TYPE_NONE, 0);
131
 
 
132
 
        signals[SOURCE_ADDED] =
133
 
                g_signal_new ("source_added",
134
 
                              G_OBJECT_CLASS_TYPE (object_class),
135
 
                              G_SIGNAL_RUN_LAST,
136
 
                              G_STRUCT_OFFSET (ESourceGroupClass, source_added),
137
 
                              NULL, NULL,
138
 
                              g_cclosure_marshal_VOID__OBJECT,
139
 
                              G_TYPE_NONE, 1,
140
 
                              G_TYPE_OBJECT);
141
 
        signals[SOURCE_REMOVED] =
142
 
                g_signal_new ("source_removed",
143
 
                              G_OBJECT_CLASS_TYPE (object_class),
144
 
                              G_SIGNAL_RUN_LAST,
145
 
                              G_STRUCT_OFFSET (ESourceGroupClass, source_removed),
146
 
                              NULL, NULL,
147
 
                              g_cclosure_marshal_VOID__OBJECT,
148
 
                              G_TYPE_NONE, 1,
149
 
                              G_TYPE_OBJECT);
150
 
}
151
 
 
152
 
static void
153
 
e_source_group_init (ESourceGroup *source_group)
154
 
{
155
 
        ESourceGroupPrivate *priv;
156
 
 
157
 
        priv = g_new0 (ESourceGroupPrivate, 1);
158
 
        source_group->priv = priv;
159
 
 
160
 
        priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
161
 
                                                  g_free, g_free);
162
 
}
163
 
 
164
 
static void
165
 
import_properties (ESourceGroup *source_group,
166
 
                   xmlNodePtr prop_root)
167
 
{
168
 
        ESourceGroupPrivate *priv = source_group->priv;
169
 
        xmlNodePtr prop_node;
170
 
 
171
 
        for (prop_node = prop_root->children; prop_node; prop_node = prop_node->next) {
172
 
                xmlChar *name, *value;
173
 
 
174
 
                if (!prop_node->name || strcmp (GC prop_node->name, "property"))
175
 
                        continue;
176
 
 
177
 
                name = xmlGetProp (prop_node, XC "name");
178
 
                value = xmlGetProp (prop_node, XC "value");
179
 
 
180
 
                if (name && value) {
181
 
                        g_hash_table_insert (priv->properties, g_strdup (GC name), g_strdup (GC value));
182
 
                }
183
 
 
184
 
                if (name)
185
 
                        xmlFree (name);
186
 
                if (value)
187
 
                        xmlFree (value);
188
 
        }
189
 
}
190
 
 
191
 
typedef struct
192
 
{
193
 
        gboolean equal;
194
 
        GHashTable *table2;
195
 
} hash_compare_data;
196
 
 
197
 
static void
198
 
compare_str_hash (gpointer key, gpointer value, hash_compare_data *cd)
199
 
{
200
 
        gpointer value2 = g_hash_table_lookup (cd->table2, key);
201
 
        if (value2 == NULL || g_str_equal (value, value2) == FALSE)
202
 
                cd->equal = FALSE;
203
 
}
204
 
 
205
 
static gboolean
206
 
compare_str_hashes (GHashTable *table1, GHashTable *table2)
207
 
{
208
 
        hash_compare_data cd;
209
 
 
210
 
        if (g_hash_table_size (table1) != g_hash_table_size (table2))
211
 
                return FALSE;
212
 
 
213
 
        cd.equal = TRUE;
214
 
        cd.table2 = table2;
215
 
        g_hash_table_foreach (table1, (GHFunc) compare_str_hash, &cd);
216
 
        return cd.equal;
217
 
}
218
 
 
219
 
static void
220
 
property_dump_cb (const gchar *key, const gchar *value, xmlNodePtr root)
221
 
{
222
 
        xmlNodePtr node;
223
 
 
224
 
        node = xmlNewChild (root, NULL, XC "property", NULL);
225
 
        xmlSetProp (node, XC "name", XC key);
226
 
        xmlSetProp (node, XC "value", XC value);
227
 
}
228
 
 
229
 
/* Public methods.  */
230
 
 
231
 
ESourceGroup *
232
 
e_source_group_new (const gchar *name,
233
 
                    const gchar *base_uri)
234
 
{
235
 
        ESourceGroup *new;
236
 
 
237
 
        g_return_val_if_fail (name != NULL, NULL);
238
 
        g_return_val_if_fail (base_uri != NULL, NULL);
239
 
 
240
 
        new = g_object_new (e_source_group_get_type (), NULL);
241
 
        new->priv->uid = e_uid_new ();
242
 
 
243
 
        e_source_group_set_name (new, name);
244
 
        e_source_group_set_base_uri (new, base_uri);
245
 
 
246
 
        return new;
247
 
}
248
 
 
249
 
ESourceGroup *
250
 
e_source_group_new_from_xml (const gchar *xml)
251
 
{
252
 
        xmlDocPtr doc;
253
 
        ESourceGroup *group;
254
 
 
255
 
        doc = xmlParseDoc (XC xml);
256
 
        if (doc == NULL)
257
 
                return NULL;
258
 
 
259
 
        group = e_source_group_new_from_xmldoc (doc);
260
 
        xmlFreeDoc (doc);
261
 
 
262
 
        return group;
263
 
}
264
 
 
265
 
ESourceGroup *
266
 
e_source_group_new_from_xmldoc (xmlDocPtr doc)
267
 
{
268
 
        xmlNodePtr root, p;
269
 
        xmlChar *uid;
270
 
        xmlChar *name;
271
 
        xmlChar *base_uri;
272
 
        xmlChar *readonly_str;
273
 
        ESourceGroup *new = NULL;
274
 
 
275
 
        g_return_val_if_fail (doc != NULL, NULL);
276
 
 
277
 
        root = doc->children;
278
 
        if (strcmp (GC root->name, "group") != 0)
279
 
                return NULL;
280
 
 
281
 
        uid = xmlGetProp (root, XC "uid");
282
 
        name = xmlGetProp (root, XC "name");
283
 
        base_uri = xmlGetProp (root, XC "base_uri");
284
 
        readonly_str = xmlGetProp (root, XC "readonly");
285
 
 
286
 
        if (uid == NULL || name == NULL || base_uri == NULL)
287
 
                goto done;
288
 
 
289
 
        new = g_object_new (e_source_group_get_type (), NULL);
290
 
 
291
 
        if (!new)
292
 
                goto done;
293
 
 
294
 
        new->priv->uid = g_strdup (GC uid);
295
 
 
296
 
        e_source_group_set_name (new, GC name);
297
 
 
298
 
        /* XXX The "On This Computer" group used to specify an
299
 
         *     absolute "file:" URI pointing to its local storage
300
 
         *     directory, but that caused all kinds of portability
301
 
         *     issues so now we just use "local:" and leave the
302
 
         *     absolute file system path implicit. */
303
 
        if (g_str_has_prefix (GC base_uri, "file:"))
304
 
                e_source_group_set_base_uri (new, "local:");
305
 
        else
306
 
                e_source_group_set_base_uri (new, GC base_uri);
307
 
 
308
 
        for (p = root->children; p != NULL; p = p->next) {
309
 
                ESource *new_source;
310
 
 
311
 
                if (p->name && !strcmp (GC p->name, "properties")) {
312
 
                        import_properties (new, p);
313
 
                        continue;
314
 
                }
315
 
 
316
 
                new_source = e_source_new_from_xml_node (p);
317
 
 
318
 
                if (new_source == NULL) {
319
 
                        g_object_unref (new);
320
 
                        new = NULL;
321
 
                        goto done;
322
 
                }
323
 
                e_source_group_add_source (new, new_source, -1);
324
 
                g_object_unref (new_source);
325
 
        }
326
 
 
327
 
        e_source_group_set_readonly (new, readonly_str && !strcmp (GC readonly_str, "yes"));
328
 
 
329
 
 done:
330
 
        if (uid != NULL)
331
 
                xmlFree (uid);
332
 
 
333
 
        if (name != NULL)
334
 
                xmlFree (name);
335
 
        if (base_uri != NULL)
336
 
                xmlFree (base_uri);
337
 
        if (readonly_str != NULL)
338
 
                xmlFree (readonly_str);
339
 
        return new;
340
 
}
341
 
 
342
 
gboolean
343
 
e_source_group_update_from_xml (ESourceGroup *group,
344
 
                                const gchar *xml,
345
 
                                gboolean *changed_return)
346
 
{
347
 
        xmlDocPtr xmldoc;
348
 
        gboolean success;
349
 
 
350
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
351
 
        g_return_val_if_fail (xml != NULL, FALSE);
352
 
 
353
 
        xmldoc = xmlParseDoc (XC xml);
354
 
 
355
 
        success = e_source_group_update_from_xmldoc (group, xmldoc, changed_return);
356
 
 
357
 
        xmlFreeDoc (xmldoc);
358
 
 
359
 
        return success;
360
 
}
361
 
 
362
 
gboolean
363
 
e_source_group_update_from_xmldoc (ESourceGroup *group,
364
 
                                   xmlDocPtr doc,
365
 
                                   gboolean *changed_return)
366
 
{
367
 
        GHashTable *new_sources_hash;
368
 
        GSList *new_sources_list = NULL;
369
 
        xmlNodePtr root, nodep;
370
 
        xmlChar *name, *base_uri, *readonly_str;
371
 
        gboolean readonly;
372
 
        gboolean changed = FALSE;
373
 
        GSList *p, *q;
374
 
 
375
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
376
 
        g_return_val_if_fail (doc != NULL, FALSE);
377
 
 
378
 
        *changed_return = FALSE;
379
 
 
380
 
        root = doc->children;
381
 
        if (strcmp (GC root->name, "group") != 0)
382
 
                return FALSE;
383
 
 
384
 
        name = xmlGetProp (root, XC "name");
385
 
        if (name == NULL)
386
 
                return FALSE;
387
 
 
388
 
        base_uri = xmlGetProp (root, XC "base_uri");
389
 
        if (base_uri == NULL) {
390
 
                xmlFree (name);
391
 
                return FALSE;
392
 
        }
393
 
 
394
 
        if (strcmp (group->priv->name, GC name) != 0) {
395
 
                g_free (group->priv->name);
396
 
                group->priv->name = g_strdup (GC name);
397
 
                changed = TRUE;
398
 
        }
399
 
        xmlFree (name);
400
 
 
401
 
        /* XXX The "On This Computer" group used to specify an
402
 
         *     absolute "file:" URI pointing to its local storage
403
 
         *     directory, but that caused all kinds of portability
404
 
         *     issues so now we just use "local:" and leave the
405
 
         *     absolute file system path implicit. */
406
 
        if (g_str_has_prefix (GC base_uri, "file:")) {
407
 
                g_free (group->priv->base_uri);
408
 
                group->priv->base_uri = g_strdup ("local:");
409
 
                changed = TRUE;
410
 
        } else if (strcmp (group->priv->base_uri, GC base_uri) != 0) {
411
 
                g_free (group->priv->base_uri);
412
 
                group->priv->base_uri = g_strdup (GC base_uri);
413
 
                changed = TRUE;
414
 
        }
415
 
        xmlFree (base_uri);
416
 
 
417
 
        readonly_str = xmlGetProp (root, XC "readonly");
418
 
        readonly = readonly_str && !strcmp (GC readonly_str, "yes");
419
 
        if (readonly != group->priv->readonly) {
420
 
                group->priv->readonly = readonly;
421
 
                changed = TRUE;
422
 
        }
423
 
        xmlFree (readonly_str);
424
 
 
425
 
        if (g_hash_table_size (group->priv->properties) && !root->children) {
426
 
                g_hash_table_destroy (group->priv->properties);
427
 
                group->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
428
 
                                                                  g_free, g_free);
429
 
                changed = TRUE;
430
 
        }
431
 
 
432
 
        new_sources_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
433
 
 
434
 
        for (nodep = root->children; nodep != NULL; nodep = nodep->next) {
435
 
                ESource *existing_source;
436
 
                gchar *uid;
437
 
 
438
 
                if (!nodep->name)
439
 
                        continue;
440
 
 
441
 
                if (!strcmp (GC nodep->name, "properties")) {
442
 
                        GHashTable *temp = group->priv->properties;
443
 
                        group->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
444
 
                                                                          g_free, g_free);
445
 
                        import_properties (group, nodep);
446
 
                        if (!compare_str_hashes (temp, group->priv->properties))
447
 
                                changed = TRUE;
448
 
                        g_hash_table_destroy (temp);
449
 
                        continue;
450
 
                }
451
 
 
452
 
                uid = e_source_uid_from_xml_node (nodep);
453
 
                if (uid == NULL)
454
 
                        continue;
455
 
 
456
 
                existing_source = e_source_group_peek_source_by_uid (group, uid);
457
 
                if (g_hash_table_lookup (new_sources_hash, existing_source) != NULL) {
458
 
                        g_free (uid);
459
 
                        continue;
460
 
                }
461
 
 
462
 
                if (existing_source == NULL) {
463
 
                        ESource *new_source = e_source_new_from_xml_node (nodep);
464
 
 
465
 
                        if (new_source != NULL) {
466
 
                                e_source_set_group (new_source, group);
467
 
                                g_signal_connect (new_source, "changed", G_CALLBACK (source_changed_callback), group);
468
 
                                new_sources_list = g_slist_prepend (new_sources_list, new_source);
469
 
 
470
 
                                g_hash_table_insert (new_sources_hash, new_source, new_source);
471
 
 
472
 
                                g_signal_emit (group, signals[SOURCE_ADDED], 0, new_source);
473
 
                                changed = TRUE;
474
 
                        }
475
 
                } else {
476
 
                        gboolean source_changed;
477
 
 
478
 
                        group->priv->ignore_source_changed++;
479
 
 
480
 
                        if (e_source_update_from_xml_node (existing_source, nodep, &source_changed)) {
481
 
                                new_sources_list = g_slist_prepend (new_sources_list, existing_source);
482
 
                                g_object_ref (existing_source);
483
 
                                g_hash_table_insert (new_sources_hash, existing_source, existing_source);
484
 
 
485
 
                                if (source_changed)
486
 
                                        changed = TRUE;
487
 
                        }
488
 
 
489
 
                        group->priv->ignore_source_changed--;
490
 
                }
491
 
 
492
 
                g_free (uid);
493
 
        }
494
 
 
495
 
        new_sources_list = g_slist_reverse (new_sources_list);
496
 
 
497
 
        /* Emit "group_removed" and disconnect the "changed" signal for all the
498
 
           groups that we haven't found in the new list.  */
499
 
        q = new_sources_list;
500
 
        for (p = group->priv->sources; p != NULL; p = p->next) {
501
 
                ESource *source = E_SOURCE (p->data);
502
 
 
503
 
                if (g_hash_table_lookup (new_sources_hash, source) == NULL) {
504
 
                        changed = TRUE;
505
 
 
506
 
                        g_signal_emit (group, signals[SOURCE_REMOVED], 0, source);
507
 
                        g_signal_handlers_disconnect_by_func (source, source_changed_callback, group);
508
 
                }
509
 
 
510
 
                if (!changed && q != NULL) {
511
 
                        if (q->data != p->data)
512
 
                                changed = TRUE;
513
 
                        q = q->next;
514
 
                }
515
 
        }
516
 
 
517
 
        g_hash_table_destroy (new_sources_hash);
518
 
 
519
 
        /* Replace the original group list with the new one.  */
520
 
        g_slist_foreach (group->priv->sources, (GFunc) g_object_unref, NULL);
521
 
        g_slist_free (group->priv->sources);
522
 
 
523
 
        group->priv->sources = new_sources_list;
524
 
 
525
 
        /* FIXME if the order changes, the function doesn't notice.  */
526
 
 
527
 
        if (changed) {
528
 
                g_signal_emit (group, signals[CHANGED], 0);
529
 
                *changed_return = TRUE;
530
 
        }
531
 
 
532
 
        return TRUE;            /* Success. */
533
 
}
534
 
 
535
 
gchar *
536
 
e_source_group_uid_from_xmldoc (xmlDocPtr doc)
537
 
{
538
 
        xmlNodePtr root = doc->children;
539
 
        xmlChar *name;
540
 
        gchar *retval;
541
 
 
542
 
        if (root && root->name) {
543
 
                if (strcmp (GC root->name, "group") != 0)
544
 
                        return NULL;
545
 
        }
546
 
        else
547
 
                return NULL;
548
 
 
549
 
        name = xmlGetProp (root, XC "uid");
550
 
        if (name == NULL)
551
 
                return NULL;
552
 
 
553
 
        retval = g_strdup (GC name);
554
 
        xmlFree (name);
555
 
        return retval;
556
 
}
557
 
 
558
 
void
559
 
e_source_group_set_name (ESourceGroup *group,
560
 
                         const gchar *name)
561
 
{
562
 
        g_return_if_fail (E_IS_SOURCE_GROUP (group));
563
 
        g_return_if_fail (name != NULL);
564
 
 
565
 
        if (group->priv->readonly)
566
 
                return;
567
 
 
568
 
        if (group->priv->name != NULL &&
569
 
            strcmp (group->priv->name, name) == 0)
570
 
                return;
571
 
 
572
 
        g_free (group->priv->name);
573
 
        group->priv->name = g_strdup (name);
574
 
 
575
 
        g_signal_emit (group, signals[CHANGED], 0);
576
 
}
577
 
 
578
 
void e_source_group_set_base_uri (ESourceGroup *group,
579
 
                                  const gchar *base_uri)
580
 
{
581
 
        g_return_if_fail (E_IS_SOURCE_GROUP (group));
582
 
        g_return_if_fail (base_uri != NULL);
583
 
 
584
 
        if (group->priv->readonly)
585
 
                return;
586
 
 
587
 
        if (group->priv->base_uri == base_uri)
588
 
                return;
589
 
 
590
 
        g_free (group->priv->base_uri);
591
 
        group->priv->base_uri = g_strdup (base_uri);
592
 
 
593
 
        g_signal_emit (group, signals[CHANGED], 0);
594
 
}
595
 
 
596
 
void e_source_group_set_readonly (ESourceGroup *group,
597
 
                                  gboolean      readonly)
598
 
{
599
 
        GSList *i;
600
 
 
601
 
        g_return_if_fail (E_IS_SOURCE_GROUP (group));
602
 
 
603
 
        if (group->priv->readonly)
604
 
                return;
605
 
 
606
 
        if (group->priv->readonly == readonly)
607
 
                return;
608
 
 
609
 
        group->priv->readonly = readonly;
610
 
        for (i = group->priv->sources; i != NULL; i = i->next)
611
 
                e_source_set_readonly (E_SOURCE (i->data), readonly);
612
 
 
613
 
        g_signal_emit (group, signals[CHANGED], 0);
614
 
}
615
 
 
616
 
const gchar *
617
 
e_source_group_peek_uid (ESourceGroup *group)
618
 
{
619
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (group), NULL);
620
 
 
621
 
        return group->priv->uid;
622
 
}
623
 
 
624
 
const gchar *
625
 
e_source_group_peek_name (ESourceGroup *group)
626
 
{
627
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (group), NULL);
628
 
 
629
 
        return group->priv->name;
630
 
}
631
 
 
632
 
const gchar *
633
 
e_source_group_peek_base_uri (ESourceGroup *group)
634
 
{
635
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (group), NULL);
636
 
 
637
 
        return group->priv->base_uri;
638
 
}
639
 
 
640
 
gboolean
641
 
e_source_group_get_readonly (ESourceGroup *group)
642
 
{
643
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
644
 
 
645
 
        return group->priv->readonly;
646
 
}
647
 
 
648
 
GSList *
649
 
e_source_group_peek_sources (ESourceGroup *group)
650
 
{
651
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (group), NULL);
652
 
 
653
 
        return group->priv->sources;
654
 
}
655
 
 
656
 
ESource *
657
 
e_source_group_peek_source_by_uid (ESourceGroup *group,
658
 
                                   const gchar *uid)
659
 
{
660
 
        GSList *p;
661
 
 
662
 
        for (p = group->priv->sources; p != NULL; p = p->next) {
663
 
                if (strcmp (e_source_peek_uid (E_SOURCE (p->data)), uid) == 0)
664
 
                        return E_SOURCE (p->data);
665
 
        }
666
 
 
667
 
        return NULL;
668
 
}
669
 
 
670
 
ESource *
671
 
e_source_group_peek_source_by_name (ESourceGroup *group,
672
 
                                    const gchar *name)
673
 
{
674
 
        GSList *p;
675
 
 
676
 
        for (p = group->priv->sources; p != NULL; p = p->next) {
677
 
                if (strcmp (e_source_peek_name (E_SOURCE (p->data)), name) == 0)
678
 
                        return E_SOURCE (p->data);
679
 
        }
680
 
 
681
 
        return NULL;
682
 
}
683
 
 
684
 
gboolean
685
 
e_source_group_add_source (ESourceGroup *group,
686
 
                           ESource *source,
687
 
                           gint position)
688
 
{
689
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
690
 
 
691
 
        if (group->priv->readonly)
692
 
                return FALSE;
693
 
 
694
 
        if (e_source_group_peek_source_by_uid (group, e_source_peek_uid (source)) != NULL)
695
 
                return FALSE;
696
 
 
697
 
        e_source_set_group (source, group);
698
 
        e_source_set_readonly (source, group->priv->readonly);
699
 
        g_object_ref (source);
700
 
 
701
 
        g_signal_connect (source, "changed", G_CALLBACK (source_changed_callback), group);
702
 
 
703
 
        group->priv->sources = g_slist_insert (group->priv->sources, source, position);
704
 
        g_signal_emit (group, signals[SOURCE_ADDED], 0, source);
705
 
        g_signal_emit (group, signals[CHANGED], 0);
706
 
 
707
 
        return TRUE;
708
 
}
709
 
 
710
 
gboolean
711
 
e_source_group_remove_source (ESourceGroup *group,
712
 
                              ESource *source)
713
 
{
714
 
        GSList *p;
715
 
 
716
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
717
 
        g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
718
 
 
719
 
        if (group->priv->readonly)
720
 
                return FALSE;
721
 
 
722
 
        for (p = group->priv->sources; p != NULL; p = p->next) {
723
 
                if (E_SOURCE (p->data) == source) {
724
 
                        group->priv->sources = g_slist_remove_link (group->priv->sources, p);
725
 
                        g_signal_handlers_disconnect_by_func (source,
726
 
                                                              G_CALLBACK (source_changed_callback),
727
 
                                                              group);
728
 
                        g_signal_emit (group, signals[SOURCE_REMOVED], 0, source);
729
 
                        g_signal_emit (group, signals[CHANGED], 0);
730
 
                        g_object_unref (source);
731
 
                        return TRUE;
732
 
                }
733
 
        }
734
 
 
735
 
        return FALSE;
736
 
}
737
 
 
738
 
gboolean
739
 
e_source_group_remove_source_by_uid (ESourceGroup *group,
740
 
                                     const gchar *uid)
741
 
{
742
 
        GSList *p;
743
 
 
744
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
745
 
        g_return_val_if_fail (uid != NULL, FALSE);
746
 
 
747
 
        if (group->priv->readonly)
748
 
                return FALSE;
749
 
 
750
 
        for (p = group->priv->sources; p != NULL; p = p->next) {
751
 
                ESource *source = E_SOURCE (p->data);
752
 
 
753
 
                if (strcmp (e_source_peek_uid (source), uid) == 0) {
754
 
                        group->priv->sources = g_slist_remove_link (group->priv->sources, p);
755
 
                        g_signal_handlers_disconnect_by_func (source,
756
 
                                                              G_CALLBACK (source_changed_callback),
757
 
                                                              group);
758
 
                        g_signal_emit (group, signals[SOURCE_REMOVED], 0, source);
759
 
                        g_signal_emit (group, signals[CHANGED], 0);
760
 
                        g_object_unref (source);
761
 
                        return TRUE;
762
 
                }
763
 
        }
764
 
 
765
 
        return FALSE;
766
 
}
767
 
 
768
 
gchar *
769
 
e_source_group_to_xml (ESourceGroup *group)
770
 
{
771
 
        xmlDocPtr doc;
772
 
        xmlNodePtr root;
773
 
        xmlChar *xml_buffer;
774
 
        gchar *returned_buffer;
775
 
        gint xml_buffer_size;
776
 
        GSList *p;
777
 
 
778
 
        doc = xmlNewDoc (XC "1.0");
779
 
 
780
 
        root = xmlNewDocNode (doc, NULL, XC "group", NULL);
781
 
        xmlSetProp (root, XC "uid", XC e_source_group_peek_uid (group));
782
 
        xmlSetProp (root, XC "name", XC e_source_group_peek_name (group));
783
 
        xmlSetProp (root, XC "base_uri", XC e_source_group_peek_base_uri (group));
784
 
        xmlSetProp (root, XC "readonly", XC (group->priv->readonly ? "yes" : "no"));
785
 
 
786
 
        if (g_hash_table_size (group->priv->properties) != 0) {
787
 
                xmlNodePtr properties_node;
788
 
 
789
 
                properties_node = xmlNewChild (root, NULL, XC "properties", NULL);
790
 
                g_hash_table_foreach (group->priv->properties, (GHFunc) property_dump_cb, properties_node);
791
 
        }
792
 
 
793
 
        xmlDocSetRootElement (doc, root);
794
 
 
795
 
        for (p = group->priv->sources; p != NULL; p = p->next)
796
 
                e_source_dump_to_xml_node (E_SOURCE (p->data), root);
797
 
 
798
 
        xmlDocDumpMemory (doc, &xml_buffer, &xml_buffer_size);
799
 
        xmlFreeDoc (doc);
800
 
 
801
 
        returned_buffer = g_malloc (xml_buffer_size + 1);
802
 
        memcpy (returned_buffer, xml_buffer, xml_buffer_size);
803
 
        returned_buffer[xml_buffer_size] = '\0';
804
 
        xmlFree (xml_buffer);
805
 
 
806
 
        return returned_buffer;
807
 
}
808
 
 
809
 
static gint
810
 
find_esource_from_uid (gconstpointer a, gconstpointer b)
811
 
{
812
 
        return g_ascii_strcasecmp (e_source_peek_uid ((ESource *)(a)), (gchar *)(b));
813
 
}
814
 
 
815
 
static gboolean
816
 
compare_source_lists (GSList *a, GSList *b)
817
 
{
818
 
        gboolean retval = TRUE;
819
 
        GSList *l;
820
 
 
821
 
        if (g_slist_length(a) != g_slist_length(b))
822
 
                return FALSE;
823
 
 
824
 
        for (l = a; l != NULL && retval; l = l->next) {
825
 
                GSList *elem = g_slist_find_custom (b, e_source_peek_uid ((ESource *)(l->data)), (GCompareFunc) find_esource_from_uid);
826
 
 
827
 
                if (!elem || !e_source_equal ((ESource *)(l->data), (ESource *)(elem->data)))
828
 
                        retval = FALSE;
829
 
        }
830
 
 
831
 
        return retval;
832
 
}
833
 
 
834
 
/**
835
 
 * e_source_group_equal:
836
 
 * @a: An ESourceGroup
837
 
 * @b: Another ESourceGroup
838
 
 *
839
 
 * Compares if @a is equivalent to @b.
840
 
 *
841
 
 * Returns: %TRUE if @a is equivalent to @b,
842
 
 * %FALSE otherwise.
843
 
 *
844
 
 * Since: 2.24
845
 
 **/
846
 
gboolean
847
 
e_source_group_equal (ESourceGroup *a, ESourceGroup *b)
848
 
{
849
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (a), FALSE);
850
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (b), FALSE);
851
 
 
852
 
        /* Compare group stuff */
853
 
        if (a->priv->uid
854
 
         && b->priv->uid
855
 
         && g_ascii_strcasecmp (a->priv->uid, b->priv->uid))
856
 
                return FALSE;
857
 
 
858
 
        if (a->priv->name
859
 
         && b->priv->name
860
 
         && g_ascii_strcasecmp (a->priv->name, b->priv->name))
861
 
                return FALSE;
862
 
 
863
 
        if (a->priv->base_uri
864
 
         && b->priv->base_uri
865
 
         && g_ascii_strcasecmp (a->priv->base_uri, b->priv->base_uri))
866
 
                return FALSE;
867
 
 
868
 
        if (a->priv->readonly != b->priv->readonly)
869
 
                return FALSE;
870
 
 
871
 
        if (!compare_str_hashes (a->priv->properties, b->priv->properties))
872
 
                return FALSE;
873
 
 
874
 
        /* Compare ESources in the groups */
875
 
        if (!compare_source_lists (a->priv->sources, b->priv->sources))
876
 
                return FALSE;
877
 
 
878
 
        return TRUE;
879
 
}
880
 
 
881
 
/**
882
 
 * e_source_group_xmlstr_equal:
883
 
 * @a: XML representation of an ESourceGroup
884
 
 * @b: XML representation of another ESourceGroup
885
 
 *
886
 
 * Compares if @a is equivalent to @b.
887
 
 *
888
 
 * Returns: %TRUE if @a is equivalent to @b,
889
 
 * %FALSE otherwise.
890
 
 *
891
 
 * Since: 2.24
892
 
 **/
893
 
gboolean
894
 
e_source_group_xmlstr_equal (const gchar *a, const gchar *b)
895
 
{
896
 
        ESourceGroup *grpa, *grpb;
897
 
        gboolean retval;
898
 
 
899
 
        grpa = e_source_group_new_from_xml (a);
900
 
        grpb = e_source_group_new_from_xml (b);
901
 
 
902
 
        retval = e_source_group_equal (grpa, grpb);
903
 
 
904
 
        g_object_unref (grpa);
905
 
        g_object_unref (grpb);
906
 
 
907
 
        return retval;
908
 
}
909
 
 
910
 
/**
911
 
 * e_source_group_get_property:
912
 
 *
913
 
 * Since: 1.12
914
 
 **/
915
 
gchar *
916
 
e_source_group_get_property (ESourceGroup *source_group,
917
 
                             const gchar *property)
918
 
{
919
 
        ESourceGroupPrivate *priv;
920
 
 
921
 
        g_return_val_if_fail (E_IS_SOURCE_GROUP (source_group), NULL);
922
 
        priv = source_group->priv;
923
 
 
924
 
        return g_strdup (g_hash_table_lookup (priv->properties, property));
925
 
}
926
 
 
927
 
/**
928
 
 * e_source_group_set_property:
929
 
 *
930
 
 * Since: 1.12
931
 
 **/
932
 
void
933
 
e_source_group_set_property (ESourceGroup *source_group,
934
 
                             const gchar *property,
935
 
                             const gchar *value)
936
 
{
937
 
        ESourceGroupPrivate *priv;
938
 
 
939
 
        g_return_if_fail (E_IS_SOURCE_GROUP (source_group));
940
 
        priv = source_group->priv;
941
 
 
942
 
        if (value)
943
 
                g_hash_table_replace (priv->properties, g_strdup (property), g_strdup (value));
944
 
        else
945
 
                g_hash_table_remove (priv->properties, property);
946
 
 
947
 
        g_signal_emit (source_group, signals[CHANGED], 0);
948
 
}
949
 
 
950
 
/**
951
 
 * e_source_group_foreach_property:
952
 
 *
953
 
 * Since: 1.12
954
 
 **/
955
 
void
956
 
e_source_group_foreach_property (ESourceGroup *source_group, GHFunc func, gpointer data)
957
 
{
958
 
        ESourceGroupPrivate *priv;
959
 
 
960
 
        g_return_if_fail (E_IS_SOURCE_GROUP (source_group));
961
 
        priv = source_group->priv;
962
 
 
963
 
        g_hash_table_foreach (priv->properties, func, data);
964
 
}
965
 
 
966
 
#undef XC
967
 
#undef GC