1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4 * Copyright (C) 2003 Ximian, Inc.
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.
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.
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.
20 * Author: Ettore Perazzoli <ettore@ximian.com>
28 #include "e-source-list.h"
30 struct _ESourceListPrivate {
31 GConfClient *gconf_client;
38 gboolean ignore_group_changed;
51 static unsigned int signals[LAST_SIGNAL] = { 0 };
54 /* Forward declarations. */
56 static gboolean sync_idle_callback (ESourceList *list);
57 static void group_changed_callback (ESourceGroup *group,
59 static void conf_changed_callback (GConfClient *client,
60 unsigned int connection_id,
65 /* Utility functions. */
68 load_from_gconf (ESourceList *list)
70 GSList *conf_list, *p, *q;
71 GSList *new_groups_list;
72 GHashTable *new_groups_hash;
73 gboolean changed = FALSE;
76 conf_list = gconf_client_get_list (list->priv->gconf_client,
77 list->priv->gconf_path,
78 GCONF_VALUE_STRING, NULL);
80 new_groups_list = NULL;
81 new_groups_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
83 for (p = conf_list, pos = 0; p != NULL; p = p->next, pos++) {
84 const xmlChar *xml = p->data;
87 ESourceGroup *existing_group;
89 xmldoc = xmlParseDoc (xml);
93 group_uid = e_source_group_uid_from_xmldoc (xmldoc);
94 if (group_uid == NULL) {
99 existing_group = e_source_list_peek_group_by_uid (list, group_uid);
100 if (g_hash_table_lookup (new_groups_hash, existing_group) != NULL) {
106 if (existing_group == NULL) {
107 ESourceGroup *new_group = e_source_group_new_from_xmldoc (xmldoc);
109 if (new_group != NULL) {
110 g_signal_connect (new_group, "changed", G_CALLBACK (group_changed_callback), list);
111 new_groups_list = g_slist_prepend (new_groups_list, new_group);
113 g_hash_table_insert (new_groups_hash, new_group, new_group);
114 g_signal_emit (list, signals[GROUP_ADDED], 0, new_group);
118 gboolean group_changed;
120 list->priv->ignore_group_changed ++;
122 if (e_source_group_update_from_xmldoc (existing_group, xmldoc, &group_changed)) {
123 new_groups_list = g_slist_prepend (new_groups_list, existing_group);
124 g_object_ref (existing_group);
125 g_hash_table_insert (new_groups_hash, existing_group, existing_group);
131 list->priv->ignore_group_changed --;
138 new_groups_list = g_slist_reverse (new_groups_list);
140 g_slist_foreach (conf_list, (GFunc) g_free, NULL);
141 g_slist_free (conf_list);
143 /* Emit "group_removed" and disconnect the "changed" signal for all the
144 groups that we haven't found in the new list. Also, check if the
145 order has changed. */
147 for (p = list->priv->groups; p != NULL; p = p->next) {
148 ESourceGroup *group = E_SOURCE_GROUP (p->data);
150 if (g_hash_table_lookup (new_groups_hash, group) == NULL) {
152 g_signal_emit (list, signals[GROUP_REMOVED], 0, group);
153 g_signal_handlers_disconnect_by_func (group, group_changed_callback, list);
156 if (! changed && q != NULL) {
157 if (q->data != p->data)
163 g_hash_table_destroy (new_groups_hash);
165 /* Replace the original group list with the new one. */
167 g_slist_foreach (list->priv->groups, (GFunc) g_object_unref, NULL);
168 g_slist_free (list->priv->groups);
170 list->priv->groups = new_groups_list;
172 /* FIXME if the order changes, the function doesn't notice. */
175 g_signal_emit (list, signals[CHANGED], 0);
179 remove_group (ESourceList *list,
182 list->priv->groups = g_slist_remove (list->priv->groups, group);
184 g_signal_emit (list, signals[GROUP_REMOVED], 0, group);
185 g_object_unref (group);
187 g_signal_emit (list, signals[CHANGED], 0);
194 sync_idle_callback (ESourceList *list)
196 GError *error = NULL;
198 if (! e_source_list_sync (list, &error)) {
199 g_warning ("Cannot update \"%s\": %s", list->priv->gconf_path, error->message);
200 g_error_free (error);
203 list->priv->sync_idle_id= 0;
209 group_changed_callback (ESourceGroup *group,
212 if (! list->priv->ignore_group_changed)
213 g_signal_emit (list, signals[CHANGED], 0);
215 if (list->priv->sync_idle_id == 0)
216 list->priv->sync_idle_id = g_idle_add ((GSourceFunc) sync_idle_callback, list);
220 conf_changed_callback (GConfClient *client,
221 unsigned int connection_id,
225 load_from_gconf (list);
229 /* GObject methods. */
231 G_DEFINE_TYPE (ESourceList, e_source_list, G_TYPE_OBJECT);
234 impl_dispose (GObject *object)
236 ESourceListPrivate *priv = E_SOURCE_LIST (object)->priv;
238 if (priv->sync_idle_id != 0) {
239 GError *error = NULL;
241 g_source_remove (priv->sync_idle_id);
242 priv->sync_idle_id = 0;
244 if (! e_source_list_sync (E_SOURCE_LIST (object), &error))
245 g_warning ("Could not update \"%s\": %s",
246 priv->gconf_path, error->message);
249 if (priv->groups != NULL) {
252 for (p = priv->groups; p != NULL; p = p->next)
253 g_object_unref (p->data);
255 g_slist_free (priv->groups);
259 if (priv->gconf_client != NULL) {
260 if (priv->gconf_notify_id != 0) {
261 gconf_client_notify_remove (priv->gconf_client,
262 priv->gconf_notify_id);
263 priv->gconf_notify_id = 0;
266 g_object_unref (priv->gconf_client);
267 priv->gconf_client = NULL;
269 g_assert_not_reached ();
272 (* G_OBJECT_CLASS (e_source_list_parent_class)->dispose) (object);
276 impl_finalize (GObject *object)
278 ESourceListPrivate *priv = E_SOURCE_LIST (object)->priv;
280 if (priv->gconf_notify_id != 0) {
281 gconf_client_notify_remove (priv->gconf_client,
282 priv->gconf_notify_id);
283 priv->gconf_notify_id = 0;
286 g_free (priv->gconf_path);
289 (* G_OBJECT_CLASS (e_source_list_parent_class)->finalize) (object);
293 /* Initialization. */
296 e_source_list_class_init (ESourceListClass *class)
298 GObjectClass *object_class = G_OBJECT_CLASS (class);
300 object_class->dispose = impl_dispose;
301 object_class->finalize = impl_finalize;
304 g_signal_new ("changed",
305 G_OBJECT_CLASS_TYPE (object_class),
307 G_STRUCT_OFFSET (ESourceListClass, changed),
309 g_cclosure_marshal_VOID__VOID,
312 signals[GROUP_REMOVED] =
313 g_signal_new ("group_removed",
314 G_OBJECT_CLASS_TYPE (object_class),
316 G_STRUCT_OFFSET (ESourceListClass, group_removed),
318 g_cclosure_marshal_VOID__OBJECT,
322 signals[GROUP_ADDED] =
323 g_signal_new ("group_added",
324 G_OBJECT_CLASS_TYPE (object_class),
326 G_STRUCT_OFFSET (ESourceListClass, group_added),
328 g_cclosure_marshal_VOID__OBJECT,
334 e_source_list_init (ESourceList *source_list)
336 ESourceListPrivate *priv;
338 priv = g_new0 (ESourceListPrivate, 1);
340 source_list->priv = priv;
343 /* returns the type */
345 get_source_list_type (void)
347 static GType type = 0;
350 if (!(type = g_type_from_name ("ESourceList")))
351 type = e_source_list_get_type ();
357 /* Public methods. */
360 e_source_list_new (void)
362 ESourceList *list = g_object_new (get_source_list_type (), NULL);
368 e_source_list_new_for_gconf (GConfClient *client,
373 g_return_val_if_fail (GCONF_IS_CLIENT (client), NULL);
374 g_return_val_if_fail (path != NULL, NULL);
376 list = g_object_new (get_source_list_type (), NULL);
378 list->priv->gconf_path = g_strdup (path);
379 list->priv->gconf_client = client;
380 g_object_ref (client);
382 gconf_client_add_dir (client, path, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
384 list->priv->gconf_notify_id
385 = gconf_client_notify_add (client, path,
386 (GConfClientNotifyFunc) conf_changed_callback, list,
388 load_from_gconf (list);
394 e_source_list_new_for_gconf_default (const char *path)
398 g_return_val_if_fail (path != NULL, NULL);
400 list = g_object_new (get_source_list_type (), NULL);
402 list->priv->gconf_path = g_strdup (path);
403 list->priv->gconf_client = gconf_client_get_default ();
405 gconf_client_add_dir (list->priv->gconf_client, path, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
407 list->priv->gconf_notify_id
408 = gconf_client_notify_add (list->priv->gconf_client, path,
409 (GConfClientNotifyFunc) conf_changed_callback, list,
411 load_from_gconf (list);
417 e_source_list_peek_groups (ESourceList *list)
419 g_return_val_if_fail (E_IS_SOURCE_LIST (list), NULL);
421 return list->priv->groups;
425 e_source_list_peek_group_by_uid (ESourceList *list,
430 g_return_val_if_fail (E_IS_SOURCE_LIST (list), NULL);
431 g_return_val_if_fail (uid != NULL, NULL);
433 for (p = list->priv->groups; p != NULL; p = p->next) {
434 ESourceGroup *group = E_SOURCE_GROUP (p->data);
436 if (strcmp (e_source_group_peek_uid (group), uid) == 0)
444 e_source_list_peek_group_by_name (ESourceList *list,
449 g_return_val_if_fail (E_IS_SOURCE_LIST (list), NULL);
450 g_return_val_if_fail (name != NULL, NULL);
452 for (p = list->priv->groups; p != NULL; p = p->next) {
453 ESourceGroup *group = E_SOURCE_GROUP (p->data);
455 if (strcmp (e_source_group_peek_name (group), name) == 0)
463 e_source_list_peek_source_by_uid (ESourceList *list,
468 g_return_val_if_fail (E_IS_SOURCE_LIST (list), NULL);
469 g_return_val_if_fail (uid != NULL, NULL);
471 for (p = list->priv->groups; p != NULL; p = p->next) {
472 ESourceGroup *group = E_SOURCE_GROUP (p->data);
475 source = e_source_group_peek_source_by_uid (group, uid);
484 e_source_list_peek_source_any (ESourceList *list)
488 g_return_val_if_fail (E_IS_SOURCE_LIST (list), NULL);
490 for (p = list->priv->groups; p != NULL; p = p->next) {
491 ESourceGroup *group = E_SOURCE_GROUP (p->data);
494 sources = e_source_group_peek_sources (group);
495 if (sources && sources->data)
496 return E_SOURCE (sources->data);
503 e_source_list_add_group (ESourceList *list,
507 g_return_val_if_fail (E_IS_SOURCE_LIST (list), FALSE);
508 g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
510 if (e_source_list_peek_group_by_uid (list, e_source_group_peek_uid (group)) != NULL)
513 list->priv->groups = g_slist_insert (list->priv->groups, group, position);
514 g_object_ref (group);
516 g_signal_connect (group, "changed", G_CALLBACK (group_changed_callback), list);
518 g_signal_emit (list, signals[GROUP_ADDED], 0, group);
519 g_signal_emit (list, signals[CHANGED], 0);
525 e_source_list_remove_group (ESourceList *list,
528 g_return_val_if_fail (E_IS_SOURCE_LIST (list), FALSE);
529 g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
531 if (e_source_list_peek_group_by_uid (list, e_source_group_peek_uid (group)) == NULL)
534 remove_group (list, group);
539 e_source_list_remove_group_by_uid (ESourceList *list,
544 g_return_val_if_fail (E_IS_SOURCE_LIST (list), FALSE);
545 g_return_val_if_fail (uid != NULL, FALSE);
547 group = e_source_list_peek_group_by_uid (list, uid);
551 remove_group (list, group);
556 e_source_list_remove_source_by_uid (ESourceList *list,
561 g_return_val_if_fail (E_IS_SOURCE_LIST (list), FALSE);
562 g_return_val_if_fail (uid != NULL, FALSE);
564 for (p = list->priv->groups; p != NULL; p = p->next) {
565 ESourceGroup *group = E_SOURCE_GROUP (p->data);
568 source = e_source_group_peek_source_by_uid (group, uid);
570 return e_source_group_remove_source_by_uid (group, uid);
578 e_source_list_sync (ESourceList *list,
585 g_return_val_if_fail (E_IS_SOURCE_LIST (list), FALSE);
588 for (p = list->priv->groups; p != NULL; p = p->next)
589 conf_list = g_slist_prepend (conf_list, e_source_group_to_xml (E_SOURCE_GROUP (p->data)));
590 conf_list = g_slist_reverse (conf_list);
592 if (!e_source_list_is_gconf_updated (list))
593 retval = gconf_client_set_list (list->priv->gconf_client,
594 list->priv->gconf_path,
601 g_slist_foreach (conf_list, (GFunc) g_free, NULL);
602 g_slist_free (conf_list);
608 e_source_list_is_gconf_updated (ESourceList *list)
610 char *source_group_xml = NULL;
611 char *gconf_xml = NULL;
612 char *group_uid = NULL;
613 GSList *conf_list = NULL, *temp = NULL, *p = NULL;
615 ESourceGroup *group = NULL;
616 GSList *groups = NULL;
617 gboolean conf_to_list = TRUE, list_to_conf = TRUE;
619 g_return_val_if_fail (list != NULL, FALSE);
621 conf_list = gconf_client_get_list (list->priv->gconf_client,
622 list->priv->gconf_path,
623 GCONF_VALUE_STRING, NULL);
625 /* From conf to list */
627 for (temp = conf_list; temp != NULL; temp = temp->next) {
628 gconf_xml = (char *)temp->data;
629 xmldoc = xmlParseDoc ((const xmlChar *)gconf_xml);
631 group_uid = e_source_group_uid_from_xmldoc (xmldoc);
632 group = e_source_list_peek_group_by_uid (list, group_uid);
637 source_group_xml = e_source_group_to_xml (group);
638 if (!strcmp (gconf_xml, source_group_xml)) {
639 g_free (source_group_xml);
643 conf_to_list = FALSE;
644 g_free (source_group_xml);
648 conf_to_list = FALSE;
654 /* If there is mismatch, free the conf_list and return FALSE */
656 for (p = conf_list; p != NULL ; p = p->next) {
657 gconf_xml = (char *) p->data;
660 g_slist_free (conf_list);
664 groups = e_source_list_peek_groups (list);
666 /* From list to conf */
668 for (p = groups ; p != NULL; p = p->next) {
669 group = E_SOURCE_GROUP (p->data);
670 source_group_xml = e_source_group_to_xml (group);
672 for (temp = conf_list; temp != NULL; temp = temp->next) {
673 gconf_xml = (char *)temp->data;
674 if (strcmp (gconf_xml, source_group_xml))
679 g_free (source_group_xml);
682 list_to_conf = FALSE;
688 for (p = conf_list; p != NULL ; p = p->next) {
689 gconf_xml = (char *) p->data;
692 g_slist_free (conf_list);
694 /* If there is mismatch return FALSE */