Index: ChangeLog
===================================================================
--- ChangeLog	(revision 9793)
+++ ChangeLog	(working copy)
@@ -1,3 +1,20 @@
+2008-12-04  Milan Crha  <mcrha@redhat.com>
+
+	** Part of fix for bug #442398
+
+	* libedataserver/e-source-group.h:
+	(e_source_group_set_hidden), (e_source_group_get_hidden):
+	* libedataserver/e-source-group.c: (struct _ESourceGroupPrivate),
+	(e_source_group_new_from_xmldoc), (e_source_group_update_from_xmldoc),
+	(e_source_group_set_hidden), (e_source_group_get_hidden),
+	(e_source_group_add_source), (e_source_group_to_xml),
+	(e_source_group_equal): Be able to set 'hidden' flag on the group.
+
+	* libedataserver/e-source-list.h: (e_source_list_force_save):
+	* libedataserver/e-source-list.c: (internal_sync),
+	(e_source_list_sync), (e_source_list_force_save):
+	Function to force save of the ESourceList object.
+
 2008-12-03  Milan Crha  <mcrha@redhat.com>
 
 	** Part of fix for bug #352287
Index: libedataserverui/e-source-selector.c
===================================================================
--- libedataserverui/e-source-selector.c	(revision 9793)
+++ libedataserverui/e-source-selector.c	(working copy)
@@ -26,6 +26,7 @@
 #endif
 
 #include <string.h>
+#include <glib/gi18n.h>
 
 #include "e-data-server-ui-marshal.h"
 #include "e-source-selector.h"
@@ -196,7 +197,10 @@ rebuild_existing_cb (GtkTreeModel *model
 	if (E_IS_SOURCE_GROUP (node)) {
 		uid = e_source_group_peek_uid (E_SOURCE_GROUP (node));
 
-		if (e_source_list_peek_group_by_uid (rebuild_data->selector->priv->list, uid)) {
+		/* show a group if known, not hidden and has at least one source */
+		if (e_source_list_peek_group_by_uid (rebuild_data->selector->priv->list, uid) &&
+		   !e_source_group_get_hidden (E_SOURCE_GROUP (node)) &&
+		   e_source_group_peek_sources (E_SOURCE_GROUP (node))) {
 			g_hash_table_insert (rebuild_data->remaining_uids, g_strdup (uid),
 					     gtk_tree_row_reference_new (model, path));
 		} else {
@@ -285,6 +289,76 @@ get_sorted_sources (GSList *sources)
 	return res;
 }
 
+static gint
+compare_group_order (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+	GSList *order = user_data;
+	gint aidx, bidx;
+
+	aidx = g_slist_index (order, a);
+	bidx = g_slist_index (order, b);
+
+	if (aidx < bidx)
+		return -1;
+	if (aidx > bidx)
+		return 1;
+	return 0;
+}
+
+/* sorts visible groups in same order as they are in the groups list */
+static void
+sort_groups (GtkTreeModel *model, GSList *groups)
+{
+	int i, cnt;
+	GtkTreeIter iter;
+	GSList *shown = NULL, *l;
+
+	if (!model || !groups)
+		return;
+
+	cnt = gtk_tree_model_iter_n_children (model, NULL);
+	for (i = 0; i < cnt; i++) {
+		gpointer group = NULL;
+
+		if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, i))
+			break;
+
+		gtk_tree_model_get (model, &iter, 0, &group, -1);
+
+		shown = g_slist_prepend (shown, group);
+	}
+
+	shown = g_slist_sort_with_data (shown, compare_group_order, groups);
+
+	for (i = 0, l = shown; i < cnt && l; i++, l = l->next) {
+		gpointer group = NULL;
+
+		if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, i))
+			break;
+
+		gtk_tree_model_get (model, &iter, 0, &group, -1);
+
+		if (l->data != group) {
+			int j;
+			GtkTreeIter jter;
+
+			for (j = i + 1; j < cnt; j++) {
+				if (gtk_tree_model_iter_nth_child (model, &jter, NULL, j)) {
+					gtk_tree_model_get (model, &jter, 0, &group, -1);
+
+					if (l->data == group) {
+						/* found the right group later in the list, swap them now */
+						gtk_tree_store_swap (GTK_TREE_STORE (model), &iter, &jter);
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	g_slist_free (shown);
+}
+
 static void
 rebuild_model (ESourceSelector *selector)
 {
@@ -321,7 +395,7 @@ rebuild_model (ESourceSelector *selector
 		gint position;
 
 		sources = get_sorted_sources (e_source_group_peek_sources (group));
-		if (sources == NULL)
+		if (sources == NULL || e_source_group_get_hidden (group))
 			continue;
 
 		row_ref = g_hash_table_lookup (rebuild_data->remaining_uids, e_source_group_peek_uid (group));
@@ -367,6 +441,8 @@ rebuild_model (ESourceSelector *selector
 		g_slist_free (sources);
 	}
 
+	sort_groups (GTK_TREE_MODEL (tree_store), groups);
+
 	if (rebuild_data->selection_changed)
 		g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
 
@@ -1572,3 +1648,348 @@ e_source_selector_set_primary_selection 
 	}
 }
 
+static void
+tree_show_toggled (GtkCellRendererToggle *renderer, char *arg1, gpointer user_data)
+{
+	GtkWidget *table = user_data;
+	GtkTreeModel *model;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+
+	path = gtk_tree_path_new_from_string (arg1);
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (table));
+
+	if (gtk_tree_model_get_iter (model, &iter, path)) {
+		gboolean shown = TRUE;
+
+		gtk_tree_model_get (model, &iter, 2, &shown, -1);
+		shown = !shown;
+		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, shown, -1);
+
+		/* to have buttons synced with the change */
+		g_signal_emit_by_name (table, "cursor-changed");
+	}
+
+	gtk_tree_path_free (path);
+}
+
+static GtkWidget *
+create_tree (ESourceSelector *selector, GtkWidget **tree)
+{
+	GtkWidget *table, *scrolled;
+	GtkTreeSelection *selection;
+	GtkCellRenderer *renderer;
+	GtkListStore *model;
+	GSList *g;
+
+	scrolled = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+	model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
+	table = gtk_tree_view_new_with_model ((GtkTreeModel *) model);
+	gtk_tree_view_set_headers_visible ((GtkTreeView *) table, FALSE);
+
+	renderer = gtk_cell_renderer_toggle_new ();
+	g_object_set (G_OBJECT (renderer), "activatable", TRUE, NULL);
+	gtk_tree_view_insert_column_with_attributes ((GtkTreeView *) table, -1,
+						     _("Show"), renderer,
+						     "active", 2, NULL);
+	g_signal_connect (renderer, "toggled", G_CALLBACK (tree_show_toggled), table);
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_insert_column_with_attributes ((GtkTreeView *) table, -1,
+						     _("Group name"), renderer,
+						     "text", 0, NULL);
+
+	selection = gtk_tree_view_get_selection ((GtkTreeView *) table);
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+	gtk_container_add (GTK_CONTAINER (scrolled), table);
+
+	g_object_set_data ((GObject *) scrolled, "table", table);
+	g_object_set_data ((GObject *) scrolled, "model", model);
+
+	*tree = table;
+
+	g_object_unref (model);
+
+	for (g = e_source_list_peek_groups (selector->priv->list); g; g = g->next) {
+		GtkTreeIter iter;
+		ESourceGroup *group = E_SOURCE_GROUP (g->data);
+
+		if (group) {
+			gtk_list_store_append (model, &iter);
+			gtk_list_store_set (model, &iter,
+					0, e_source_group_peek_name (group),
+					1, group,
+					2, !e_source_group_get_hidden (group),
+					-1);
+		}
+	}
+
+	return scrolled;
+}
+
+static void
+process_move_button (GtkButton *button, GtkTreeView *tree, gboolean is_up, gboolean do_move)
+{
+	GtkTreeModel *model;
+	GtkTreeSelection *selection;
+	GtkTreeIter iter;
+	gboolean enable = FALSE;
+
+	g_return_if_fail (button != NULL);
+	g_return_if_fail (tree != NULL);
+
+	selection = gtk_tree_view_get_selection (tree);
+
+	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		gpointer ptr = NULL, ptr2;
+		GtkTreeIter iter2;
+		int i, cnt = gtk_tree_model_iter_n_children (model, NULL);
+		gboolean can_move = FALSE;
+
+		gtk_tree_model_get (model, &iter, 1, &ptr, -1);
+
+		for (i = 0; i < cnt; i++) {
+			if (!gtk_tree_model_iter_nth_child (model, &iter2, NULL, i))
+				break;
+
+			ptr2 = NULL;
+			gtk_tree_model_get (model, &iter2, 1, &ptr2, -1);
+
+			if (ptr == ptr2 || (is_up && !do_move && i > 0)) {
+				can_move = TRUE;
+				break;
+			}
+		}
+
+		if (can_move)
+			can_move = ((is_up && i > 0) || (!is_up && i + 1 < cnt)) && i < cnt;
+
+		if (can_move && do_move) {
+			i = i + (is_up ? -1 : 1);
+			if (gtk_tree_model_iter_nth_child (model, &iter2, NULL, i)) {
+				GtkTreePath *path;
+
+				gtk_list_store_swap (GTK_LIST_STORE (model), &iter, &iter2);
+				gtk_tree_selection_select_iter (selection, &iter);
+
+				/* scroll to the selected cell */
+				path = gtk_tree_model_get_path (model, &iter);
+				gtk_tree_view_scroll_to_cell (tree, path, NULL, FALSE, 0.0, 0.0);
+				gtk_tree_path_free (path);
+
+				/* cursor has been moved to the other row */
+				can_move = (is_up && i > 0) || (!is_up && i + 1 < cnt);
+
+				g_signal_emit_by_name (tree, "cursor-changed");
+			}
+		}
+
+		enable = can_move;
+	}
+
+	if (!do_move)
+		gtk_widget_set_sensitive (GTK_WIDGET (button), enable);
+}
+
+static void
+up_clicked (GtkButton *button, GtkTreeView *tree)
+{
+	process_move_button (button, tree, TRUE, TRUE);
+}
+
+static void
+up_cursor_changed (GtkTreeView *tree, GtkButton *button)
+{
+	process_move_button (button, tree, TRUE, FALSE);
+}
+
+static void
+down_clicked (GtkButton *button, GtkTreeView *tree)
+{
+	process_move_button (button, tree, FALSE, TRUE);
+}
+
+static void
+down_cursor_changed (GtkTreeView *tree, GtkButton *button)
+{
+	process_move_button (button, tree, FALSE, FALSE);
+}
+
+static void
+show_hide_cursor_changed (GtkTreeView *tree, GtkButton *button)
+{
+	GtkTreeModel *model;
+	GtkTreeSelection *selection;
+	GtkTreeIter iter;
+
+	g_return_if_fail (button != NULL);
+	g_return_if_fail (tree != NULL);
+
+	selection = gtk_tree_view_get_selection (tree);
+
+	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		gboolean shown = FALSE;
+
+		gtk_tree_model_get (model, &iter, 2, &shown, -1);
+
+		gtk_button_set_label (button, shown ? _("_Hide") : _("_Show"));
+	}
+}
+
+static void
+show_hide_clicked (GtkButton *button, GtkTreeView *tree)
+{
+	GtkTreeModel *model;
+	GtkTreeSelection *selection;
+	GtkTreeIter iter;
+
+	g_return_if_fail (button != NULL);
+	g_return_if_fail (tree != NULL);
+
+	selection = gtk_tree_view_get_selection (tree);
+
+	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		gboolean shown = TRUE;
+
+		gtk_tree_model_get (model, &iter, 2, &shown, -1);
+		shown = !shown;
+		gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, shown, -1);
+
+		show_hide_cursor_changed (tree, button);
+	}
+}
+
+static gboolean
+any_source_selected (ESourceSelector *selector, ESourceGroup *group)
+{
+	GSList  *sources;
+
+	g_return_val_if_fail (selector != NULL, FALSE);
+	g_return_val_if_fail (group != NULL, FALSE);
+	g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
+
+	for (sources = e_source_group_peek_sources (group); sources; sources = sources->next) {
+		if (source_is_selected (selector, sources->data))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+/**
+ * e_source_selector_manage_groups:
+ * @selector: an #ESourceSelector widget
+ *
+ * Manages list of groups, like their order in the source list, and hidden property of the group.
+ **/
+void
+e_source_selector_manage_groups (ESourceSelector *selector)
+{
+	GtkWidget *dlg, *box, *pbox, *tree, *w, *w2;
+	char *txt;
+
+	g_return_if_fail (selector != NULL);
+	g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
+
+	w = gtk_widget_get_toplevel (GTK_WIDGET (selector));
+	if (!w || !GTK_WIDGET_TOPLEVEL (w))
+		w = NULL;
+
+	dlg = gtk_dialog_new_with_buttons (_("Manage Groups"),
+			w ? GTK_WINDOW (w) : NULL,
+			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+			GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+			GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+			NULL);
+
+	gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE);
+
+	w = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
+	box = gtk_vbox_new (FALSE, 2);
+	gtk_container_set_border_width (GTK_CONTAINER (box), 12);
+	gtk_box_pack_start (GTK_BOX (w), box, FALSE, FALSE, 0);
+
+	txt = g_strconcat ("<b>", _("Available Groups:"), "</b>", NULL);
+	w = gtk_label_new ("");
+	gtk_label_set_markup (GTK_LABEL (w), txt);
+	g_free (txt);
+	gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
+	gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 2);
+
+	pbox = gtk_hbox_new (FALSE, 2);
+	gtk_box_pack_start (GTK_BOX (box), pbox, FALSE, FALSE, 2);
+
+	/* space on the left */
+	w = gtk_label_new ("");
+	gtk_box_pack_start (GTK_BOX (pbox), w, FALSE, FALSE, 6);
+
+	w = create_tree (selector, &tree);
+	gtk_widget_set_size_request (w, 200, 240);
+	gtk_box_pack_start (GTK_BOX (pbox), w, TRUE, TRUE, 2);
+
+	/* box of buttons */
+	w2 = gtk_vbutton_box_new ();
+	gtk_button_box_set_layout (GTK_BUTTON_BOX (w2), GTK_BUTTONBOX_START);
+	gtk_box_pack_start (GTK_BOX (pbox), w2, FALSE, FALSE, 2);
+
+	#define add_button(_x,_cb,_cb2) \
+		w = gtk_button_new_from_stock (_x); \
+		gtk_box_pack_start (GTK_BOX (w2), w, FALSE, FALSE, 2); \
+		g_signal_connect (w, "clicked", (GCallback)_cb, tree); \
+		g_signal_connect (tree, "cursor-changed", (GCallback)_cb2, w);
+
+	add_button (GTK_STOCK_GO_UP, up_clicked, up_cursor_changed);
+	add_button (GTK_STOCK_GO_DOWN, down_clicked, down_cursor_changed);
+	add_button (NULL, show_hide_clicked, show_hide_cursor_changed);
+
+	gtk_button_set_use_underline (GTK_BUTTON (w), TRUE);
+
+	#undef add_button
+
+	gtk_widget_show_all (box);
+
+	if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_ACCEPT) {
+		GSList *groups, *want = NULL;
+		GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+		GtkTreeIter iter;
+		int i, cnt = gtk_tree_model_iter_n_children (model, NULL);
+
+		for (i = 0; i < cnt; i++) {
+			gpointer group = NULL;
+			gboolean shown = TRUE;
+
+			if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, i))
+				break;
+
+			gtk_tree_model_get (model, &iter, 1, &group, 2, &shown, -1);
+			/* can hide only groups with all sources disabled */
+			e_source_group_set_hidden (E_SOURCE_GROUP (group), !shown && !any_source_selected (selector, group));
+
+			want = g_slist_append (want, group);
+		}
+
+		groups = e_source_list_peek_groups (selector->priv->list);
+		if (g_slist_length (want) == g_slist_length (groups)) {
+			GSList *s, *d;
+
+			for (s = want, d = groups; s && d; s = s->next, d = d->next) {
+				d->data = s->data;
+			}
+
+			/* finally save it */
+			e_source_list_force_save (selector->priv->list);
+
+			rebuild_model (selector);
+
+			/* to let other know about the change of the list */
+			g_signal_emit_by_name (selector->priv->list, "changed");
+		}
+	}
+
+	gtk_widget_destroy (dlg);
+}
Index: libedataserverui/e-source-selector.h
===================================================================
--- libedataserverui/e-source-selector.h	(revision 9793)
+++ libedataserverui/e-source-selector.h	(working copy)
@@ -95,6 +95,8 @@ void     e_source_selector_set_primary_s
 
 ESourceGroup *e_source_selector_get_primary_source_group (ESourceSelector *selector);
 
+void     e_source_selector_manage_groups (ESourceSelector *selector);
+
 G_END_DECLS
 
 #endif /* _E_SOURCE_SELECTOR_H_ */
Index: libedataserverui/ChangeLog
===================================================================
--- libedataserverui/ChangeLog	(revision 9793)
+++ libedataserverui/ChangeLog	(working copy)
@@ -1,3 +1,16 @@
+2008-12-04  Milan Crha  <mcrha@redhat.com>
+
+	** Fix for bug #442398
+
+	* e-source-selector.h: (e_source_selector_manage_groups):
+	* e-source-selector.c: (rebuild_existing_cb), (compare_group_order),
+	(sort_groups), (rebuild_model), (tree_show_toggled), (create_tree),
+	(process_move_button), (up_clicked), (up_cursor_changed),
+	(down_clicked), (down_cursor_changed), (show_hide_cursor_changed),
+	(show_hide_clicked), (any_source_selected),
+	(e_source_selector_manage_groups):
+	Manage group's visibility and order in the selector.
+
 2008-12-03  Milan Crha  <mcrha@redhat.com>
 
 	** Fix for bug #357948
Index: libedataserver/e-source-group.c
===================================================================
--- libedataserver/e-source-group.c	(revision 9793)
+++ libedataserver/e-source-group.c	(working copy)
@@ -42,6 +42,7 @@ struct _ESourceGroupPrivate {
 
 	gboolean ignore_source_changed;
 	gboolean readonly;
+	gboolean hidden;
 
 	GHashTable *properties;
 };
@@ -274,6 +275,7 @@ e_source_group_new_from_xmldoc (xmlDocPt
 	xmlChar *name;
 	xmlChar *base_uri;
 	xmlChar *readonly_str;
+	xmlChar *hidden_str;
 	ESourceGroup *new = NULL;
 
 	g_return_val_if_fail (doc != NULL, NULL);
@@ -286,6 +288,7 @@ e_source_group_new_from_xmldoc (xmlDocPt
 	name = xmlGetProp (root, XC "name");
 	base_uri = xmlGetProp (root, XC "base_uri");
 	readonly_str = xmlGetProp (root, XC "readonly");
+	hidden_str = xmlGetProp (root, XC "hidden");
 
 	if (uid == NULL || name == NULL || base_uri == NULL)
 		goto done;
@@ -320,6 +323,7 @@ e_source_group_new_from_xmldoc (xmlDocPt
 	}
 
 	e_source_group_set_readonly (new, readonly_str && !strcmp (GC readonly_str, "yes"));
+	e_source_group_set_hidden (new, hidden_str && g_str_equal (GC hidden_str, "yes"));
 
  done:
 	if (uid != NULL)
@@ -331,6 +335,8 @@ e_source_group_new_from_xmldoc (xmlDocPt
 		xmlFree (base_uri);
 	if (readonly_str != NULL)
 		xmlFree (readonly_str);
+	if (hidden_str != NULL)
+		xmlFree (hidden_str);
 	return new;
 }
 
@@ -363,8 +369,8 @@ e_source_group_update_from_xmldoc (ESour
 	GHashTable *new_sources_hash;
 	GSList *new_sources_list = NULL;
 	xmlNodePtr root, nodep;
-	xmlChar *name, *base_uri, *readonly_str;
-	gboolean readonly;
+	xmlChar *name, *base_uri, *readonly_str, *hidden_str;
+	gboolean readonly, hidden;
 	gboolean changed = FALSE;
 	GSList *p, *q;
 
@@ -409,6 +415,14 @@ e_source_group_update_from_xmldoc (ESour
 	}
 	xmlFree (readonly_str);
 
+	hidden_str = xmlGetProp (root, XC "hidden");
+	hidden = hidden_str && g_str_equal (GC hidden_str, "yes");
+	if (hidden != group->priv->hidden) {
+		group->priv->hidden = hidden;
+		changed = TRUE;
+	}
+	xmlFree (hidden_str);
+
 	if (g_hash_table_size (group->priv->properties) && !root->children) {
 		g_hash_table_destroy (group->priv->properties);
 		group->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
@@ -600,6 +614,20 @@ void e_source_group_set_readonly (ESourc
 	g_signal_emit (group, signals[CHANGED], 0);
 }
 
+/* it is automatically reset to FALSE when adding to this group new source */
+void
+e_source_group_set_hidden (ESourceGroup *group, gboolean hidden)
+{
+	g_return_if_fail (E_IS_SOURCE_GROUP (group));
+
+	if (group->priv->hidden == hidden)
+		return;
+
+	group->priv->hidden = hidden;
+
+	g_signal_emit (group, signals[CHANGED], 0);
+}
+
 const char *
 e_source_group_peek_uid (ESourceGroup *group)
 {
@@ -632,6 +660,14 @@ e_source_group_get_readonly (ESourceGrou
 	return group->priv->readonly;
 }
 
+gboolean
+e_source_group_get_hidden (ESourceGroup *group)
+{
+	g_return_val_if_fail (E_IS_SOURCE_GROUP (group), FALSE);
+
+	return group->priv->hidden;
+}
+
 GSList *
 e_source_group_peek_sources (ESourceGroup *group)
 {
@@ -681,6 +717,9 @@ e_source_group_add_source (ESourceGroup 
 	if (e_source_group_peek_source_by_uid (group, e_source_peek_uid (source)) != NULL)
 		return FALSE;
 
+	/* always show this group after added new source */
+	group->priv->hidden = FALSE;
+
 	e_source_set_group (source, group);
 	e_source_set_readonly (source, group->priv->readonly);
 	g_object_ref (source);
@@ -770,6 +809,7 @@ e_source_group_to_xml (ESourceGroup *gro
 	xmlSetProp (root, XC "name", XC e_source_group_peek_name (group));
 	xmlSetProp (root, XC "base_uri", XC e_source_group_peek_base_uri (group));
 	xmlSetProp (root, XC "readonly", XC (group->priv->readonly ? "yes" : "no"));
+	xmlSetProp (root, XC "hidden", XC (group->priv->hidden ? "yes" : "no"));
 
 	if (g_hash_table_size (group->priv->properties) != 0) {
 		xmlNodePtr properties_node;
@@ -854,6 +894,9 @@ e_source_group_equal (ESourceGroup *a, E
 	if (a->priv->readonly != b->priv->readonly)
 		return FALSE; 
 
+	if (a->priv->hidden != b->priv->hidden)
+		return FALSE;
+
 	if (!compare_str_hashes (a->priv->properties, b->priv->properties))
 		return FALSE; 
 
Index: libedataserver/e-source-group.h
===================================================================
--- libedataserver/e-source-group.h	(revision 9793)
+++ libedataserver/e-source-group.h	(working copy)
@@ -83,10 +83,14 @@ void  e_source_group_set_base_uri  (ESou
 void e_source_group_set_readonly (ESourceGroup *group,
 				  gboolean      readonly);
 
+void e_source_group_set_hidden   (ESourceGroup *group,
+				  gboolean      hidden);
+
 const char *e_source_group_peek_uid       (ESourceGroup *group);
 const char *e_source_group_peek_name      (ESourceGroup *group);
 const char *e_source_group_peek_base_uri  (ESourceGroup *group);
 gboolean    e_source_group_get_readonly   (ESourceGroup *group);
+gboolean    e_source_group_get_hidden     (ESourceGroup *group);
 
 GSList  *e_source_group_peek_sources        (ESourceGroup *group);
 ESource *e_source_group_peek_source_by_uid  (ESourceGroup *group,
Index: libedataserver/e-source-list.c
===================================================================
--- libedataserver/e-source-list.c	(revision 9793)
+++ libedataserver/e-source-list.c	(working copy)
@@ -574,9 +574,8 @@ e_source_list_remove_source_by_uid (ESou
 }
 
 
-gboolean
-e_source_list_sync (ESourceList *list,
-		    GError **error)
+static gboolean
+internal_sync (ESourceList *list, GError **error, gboolean force_save)
 {
 	GSList *conf_list;
 	GSList *p;
@@ -589,7 +588,7 @@ e_source_list_sync (ESourceList *list,
 		conf_list = g_slist_prepend (conf_list, e_source_group_to_xml (E_SOURCE_GROUP (p->data)));
 	conf_list = g_slist_reverse (conf_list);
 
-	if (!e_source_list_is_gconf_updated (list))
+	if (force_save || !e_source_list_is_gconf_updated (list))
 		retval = gconf_client_set_list (list->priv->gconf_client,
 						list->priv->gconf_path,
 						GCONF_VALUE_STRING,
@@ -605,6 +604,19 @@ e_source_list_sync (ESourceList *list,
 }
 
 gboolean
+e_source_list_sync (ESourceList *list,
+		    GError **error)
+{
+	return internal_sync (list, error, FALSE);
+}
+
+void
+e_source_list_force_save (ESourceList  *list)
+{
+	internal_sync (list, NULL, TRUE);
+}
+
+gboolean
 e_source_list_is_gconf_updated (ESourceList *list)
 {
 	char *source_group_xml = NULL;
Index: libedataserver/e-source-list.h
===================================================================
--- libedataserver/e-source-list.h	(revision 9793)
+++ libedataserver/e-source-list.h	(working copy)
@@ -87,6 +87,8 @@ gboolean  e_source_list_remove_source_by
 gboolean  e_source_list_sync  (ESourceList  *list,
 			       GError      **error);
 
+void      e_source_list_force_save (ESourceList  *list);
+
 gboolean e_source_list_is_gconf_updated (ESourceList *list);
 
 G_END_DECLS
