[evolution-data-server/account-mgmt: 11/42] Add built-in ESource key files.



commit 5af47ba32dbf1c12757b74c732929d9a3c3d91d2
Author: Matthew Barnes <mbarnes redhat com>
Date:   Sat Nov 27 10:25:32 2010 -0500

    Add built-in ESource key files.

 configure.ac                                       |    1 +
 data/Makefile.am                                   |    2 +
 data/sources/Makefile.am                           |   35 +++
 data/sources/birthdays.in                          |   11 +
 data/sources/caldav-stub.in                        |    5 +
 data/sources/contacts-stub.in                      |    5 +
 data/sources/google-stub.in                        |    5 +
 data/sources/ldap-stub.in                          |    5 +
 data/sources/local-stub.in                         |    7 +
 data/sources/local.in                              |    9 +
 data/sources/sendmail.in                           |    8 +
 data/sources/system-address-book.in                |    9 +
 data/sources/system-calendar.in                    |   11 +
 data/sources/system-memo-list.in                   |   11 +
 data/sources/system-task-list.in                   |   11 +
 data/sources/vfolder.in                            |    9 +
 data/sources/weather-stub.in                       |    5 +
 data/sources/webcal-stub.in                        |    5 +
 data/sources/webdav-stub.in                        |    5 +
 .../libedataserver/libedataserver-sections.txt     |    3 +
 libedataserver/e-source-camel.c                    |   69 ++++-
 libedataserver/e-source-registry.c                 |  316 ++++++++++++++++----
 libedataserver/e-source-registry.h                 |   15 +
 23 files changed, 505 insertions(+), 57 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index ceac76a..b2c12b4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1632,6 +1632,7 @@ camel/tests/misc/Makefile
 camel/tests/smime/Makefile
 camel/camel.pc
 data/Makefile
+data/sources/Makefile
 libebackend/Makefile
 libebackend/libebackend.pc
 libedataserver/Makefile
diff --git a/data/Makefile.am b/data/Makefile.am
index b416bfc..dcd8536 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,3 +1,5 @@
+SUBDIRS = sources
+
 gsettings_SCHEMAS = \
 	org.gnome.Evolution.DefaultSources.gschema.xml
 
diff --git a/data/sources/Makefile.am b/data/sources/Makefile.am
new file mode 100644
index 0000000..8393636
--- /dev/null
+++ b/data/sources/Makefile.am
@@ -0,0 +1,35 @@
+NULL =
+
+# These are non-removable and non-writable.
+ro_sources_in_files = \
+	caldav-stub.in \
+	contacts-stub.in \
+	google-stub.in \
+	ldap-stub.in \
+	local-stub.in \
+	weather-stub.in \
+	webcal-stub.in \
+	webdav-stub.in \
+	$(NULL)
+ro_sources_DATA = $(ro_sources_in_files:.in=)
+
+# These are non-removable, but can be changed
+# and written to the user's sources directory.
+rw_sources_in_files = \
+	birthdays.in \
+	local.in \
+	sendmail.in \
+	system-address-book.in \
+	system-calendar.in \
+	system-memo-list.in \
+	system-task-list.in \
+	vfolder.in \
+	$(NULL)
+rw_sources_DATA = $(rw_sources_in_files:.in=)
+
+%: %.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po)
+	LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@
+
+CLEANFILES = $(ro_sources_DATA) $(rw_sources_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/data/sources/birthdays.in b/data/sources/birthdays.in
new file mode 100644
index 0000000..7f90f94
--- /dev/null
+++ b/data/sources/birthdays.in
@@ -0,0 +1,11 @@
+
+[Data Source]
+_DisplayName=Birthdays & Anniversaries
+Enabled=true
+Parent=contacts-stub
+
+[Calendar]
+BackendName=contacts
+Color=#fed4d3
+Enabled=true
+Writable=false
diff --git a/data/sources/caldav-stub.in b/data/sources/caldav-stub.in
new file mode 100644
index 0000000..90081a3
--- /dev/null
+++ b/data/sources/caldav-stub.in
@@ -0,0 +1,5 @@
+
+[Data Source]
+_DisplayName=CalDAV
+Enabled=true
+Parent=
diff --git a/data/sources/contacts-stub.in b/data/sources/contacts-stub.in
new file mode 100644
index 0000000..f2bcb97
--- /dev/null
+++ b/data/sources/contacts-stub.in
@@ -0,0 +1,5 @@
+
+[Data Source]
+_DisplayName=Contacts
+Enabled=true
+Parent=
diff --git a/data/sources/google-stub.in b/data/sources/google-stub.in
new file mode 100644
index 0000000..93de6f1
--- /dev/null
+++ b/data/sources/google-stub.in
@@ -0,0 +1,5 @@
+
+[Data Source]
+_DisplayName=Google
+Enabled=true
+Parent=
diff --git a/data/sources/ldap-stub.in b/data/sources/ldap-stub.in
new file mode 100644
index 0000000..9c5e324
--- /dev/null
+++ b/data/sources/ldap-stub.in
@@ -0,0 +1,5 @@
+
+[Data Source]
+_DisplayName=On LDAP Servers
+Enabled=true
+Parent=
diff --git a/data/sources/local-stub.in b/data/sources/local-stub.in
new file mode 100644
index 0000000..bad02f0
--- /dev/null
+++ b/data/sources/local-stub.in
@@ -0,0 +1,7 @@
+# An unfortunate name collision with the "On This Computer"
+# mail store forced the introduction of this "stub" suffix.
+
+[Data Source]
+_DisplayName=On This Computer
+Enabled=true
+Parent=
diff --git a/data/sources/local.in b/data/sources/local.in
new file mode 100644
index 0000000..1c38384
--- /dev/null
+++ b/data/sources/local.in
@@ -0,0 +1,9 @@
+# Special built-in mail store.
+
+[Data Source]
+_DisplayName=On This Computer
+Enabled=true
+Parent=
+
+[Mail Account]
+BackendName=maildir
diff --git a/data/sources/sendmail.in b/data/sources/sendmail.in
new file mode 100644
index 0000000..ce825cb
--- /dev/null
+++ b/data/sources/sendmail.in
@@ -0,0 +1,8 @@
+
+[Data Source]
+_DisplayName=Sendmail
+Enabled=true
+Parent=
+
+[Mail Transport]
+BackendName=sendmail
diff --git a/data/sources/system-address-book.in b/data/sources/system-address-book.in
new file mode 100644
index 0000000..f1cea75
--- /dev/null
+++ b/data/sources/system-address-book.in
@@ -0,0 +1,9 @@
+
+[Data Source]
+_DisplayName=Personal
+Enabled=true
+Parent=local-stub
+
+[Address Book]
+BackendName=local
+Color=#becedd
diff --git a/data/sources/system-calendar.in b/data/sources/system-calendar.in
new file mode 100644
index 0000000..ffbe161
--- /dev/null
+++ b/data/sources/system-calendar.in
@@ -0,0 +1,11 @@
+
+[Data Source]
+_DisplayName=Personal
+Enabled=true
+Parent=local-stub
+
+[Calendar]
+BackendName=local
+Color=#becedd
+Selected=true
+
diff --git a/data/sources/system-memo-list.in b/data/sources/system-memo-list.in
new file mode 100644
index 0000000..f53f0f0
--- /dev/null
+++ b/data/sources/system-memo-list.in
@@ -0,0 +1,11 @@
+
+[Data Source]
+_DisplayName=Personal
+Enabled=true
+Parent=local-stub
+
+[Memo List]
+BackendName=local
+Color=#becedd
+Selected=true
+
diff --git a/data/sources/system-task-list.in b/data/sources/system-task-list.in
new file mode 100644
index 0000000..22f40bc
--- /dev/null
+++ b/data/sources/system-task-list.in
@@ -0,0 +1,11 @@
+
+[Data Source]
+_DisplayName=Personal
+Enabled=true
+Parent=local-stub
+
+[Task List]
+BackendName=local
+Color=#becedd
+Selected=true
+
diff --git a/data/sources/vfolder.in b/data/sources/vfolder.in
new file mode 100644
index 0000000..98a9df4
--- /dev/null
+++ b/data/sources/vfolder.in
@@ -0,0 +1,9 @@
+# Special built-in mail store.
+
+[Data Source]
+_DisplayName=Search Folders
+Enabled=true
+Parent=
+
+[Mail Account]
+BackendName=vfolder
diff --git a/data/sources/weather-stub.in b/data/sources/weather-stub.in
new file mode 100644
index 0000000..e1a549d
--- /dev/null
+++ b/data/sources/weather-stub.in
@@ -0,0 +1,5 @@
+
+[Data Source]
+_DisplayName=Weather
+Enabled=true
+Parent=
diff --git a/data/sources/webcal-stub.in b/data/sources/webcal-stub.in
new file mode 100644
index 0000000..178cec1
--- /dev/null
+++ b/data/sources/webcal-stub.in
@@ -0,0 +1,5 @@
+
+[Data Source]
+_DisplayName=On The Web
+Enabled=true
+Parent=
diff --git a/data/sources/webdav-stub.in b/data/sources/webdav-stub.in
new file mode 100644
index 0000000..78ec0c4
--- /dev/null
+++ b/data/sources/webdav-stub.in
@@ -0,0 +1,5 @@
+
+[Data Source]
+_DisplayName=WebDAV
+Enabled=true
+Parent=
diff --git a/docs/reference/libedataserver/libedataserver-sections.txt b/docs/reference/libedataserver/libedataserver-sections.txt
index 912d20c..0731e36 100644
--- a/docs/reference/libedataserver/libedataserver-sections.txt
+++ b/docs/reference/libedataserver/libedataserver-sections.txt
@@ -805,6 +805,9 @@ e_source_registry_commit_source_finish
 e_source_registry_create_source_sync
 e_source_registry_create_source
 e_source_registry_create_source_finish
+e_source_registry_create_multiple_sources_sync
+e_source_registry_create_multiple_sources
+e_source_registry_create_multiple_sources_finish
 e_source_registry_ref_source
 e_source_registry_list_sources
 e_source_registry_find_extension
diff --git a/libedataserver/e-source-camel.c b/libedataserver/e-source-camel.c
index 0ab9f5f..ba8f65a 100644
--- a/libedataserver/e-source-camel.c
+++ b/libedataserver/e-source-camel.c
@@ -122,6 +122,71 @@ G_DEFINE_ABSTRACT_TYPE (
 	e_source_camel,
 	E_TYPE_SOURCE_EXTENSION)
 
+/* XXX A function like this belongs in GObject.  I may yet propose it,
+ *     GParamSpecClass still has some reserved slots.  This fiddles with
+ *     GParamSpec fields that are supposed to be private to GObject, but
+ *     I have no other choice.
+ *
+ * XXX Historical note, originally I tried (ab)using override properties
+ *     in ESourceCamel, which redirected to the equivalent CamelSettings
+ *     property.  Seemed to work at first, and I was proud of my clever
+ *     hack, but it turns out g_object_class_list_properties() excludes
+ *     override properties.  So the ESourceCamel properties were being
+ *     skipped in source_load_from_key_file() (e-source.c). */
+static GParamSpec *
+param_spec_clone (GParamSpec *pspec)
+{
+	GParamSpec *clone;
+	GTypeQuery query;
+
+	/* Query the instance size. */
+	g_type_query (G_PARAM_SPEC_TYPE (pspec), &query);
+
+	/* Start with a memcpy()'d buffer. */
+	clone = g_slice_alloc0 (query.instance_size);
+	memcpy (clone, pspec, query.instance_size);
+
+	/* This sort of mimics g_param_spec_init(). */
+
+#define PARAM_FLOATING_FLAG 0x2  /* from gparam.c */
+	g_datalist_set_flags (&clone->qdata, PARAM_FLOATING_FLAG);
+	clone->ref_count = 1;
+
+	/* Clear the owner_type. */
+	clone->owner_type = G_TYPE_INVALID;
+
+	/* Clear the param_id. */
+	clone->param_id = 0;
+
+	/* This sort of mimics g_param_spec_internal(). */
+
+	/* Param name should already be canonicalized and interned. */
+
+	/* Always copy the nickname. */
+	clone->flags &= ~G_PARAM_STATIC_NICK;
+	clone->_nick = g_strdup (g_param_spec_get_nick (pspec));
+
+	/* Always copy the blurb. */
+	clone->flags &= ~G_PARAM_STATIC_BLURB;
+	clone->_blurb = g_strdup (g_param_spec_get_blurb (pspec));
+
+	/* Handle special cases. */
+
+	if (G_IS_PARAM_SPEC_STRING (clone)) {
+		GParamSpecString *clone_s;
+
+		clone_s = (GParamSpecString *) clone;
+		clone_s->default_value = g_strdup (clone_s->default_value);
+	}
+
+	/* Some types we don't handle but shouldn't need to. */
+	g_warn_if_fail (!G_IS_PARAM_SPEC_VALUE_ARRAY (clone));
+	g_warn_if_fail (!G_IS_PARAM_SPEC_OVERRIDE (clone));
+	g_warn_if_fail (!G_IS_PARAM_SPEC_VARIANT (clone));
+
+	return clone;
+}
+
 static gint
 subclass_get_binding_index (GParamSpec *settings_property)
 {
@@ -248,15 +313,13 @@ source_camel_register_subtype (GType service_type,
 
 	for (ii = 0; ii < n_properties; ii++) {
 		GParamSpec *pspec;
-		const gchar *name;
 
 		/* Some properties in CamelSettings may be covered
 		 * by other ESourceExtensions.  Skip them here. */
 		if (subclass_get_binding_index (properties[ii]) >= 0)
 			continue;
 
-		name = properties[ii]->name;
-		pspec = g_param_spec_override (name, properties[ii]);
+		pspec = param_spec_clone (properties[ii]);
 		pspec->flags |= E_SOURCE_PARAM_SETTING;
 
 		/* Clear the G_PARAM_CONSTRUCT flag.  We apply default
diff --git a/libedataserver/e-source-registry.c b/libedataserver/e-source-registry.c
index de19db3..c22c538 100644
--- a/libedataserver/e-source-registry.c
+++ b/libedataserver/e-source-registry.c
@@ -112,6 +112,7 @@ struct _ESourceRegistryPrivate {
 };
 
 struct _AsyncContext {
+	GList *input_sources;
 	ESource *input_source;
 	ESource *output_source;
 	ESourceAuthenticator *auth;
@@ -134,8 +135,8 @@ struct _CreateContext {
 	ESourceRegistry *registry;
 	GMainContext *main_context;
 	GMainLoop *main_loop;
-	gchar *object_path;
-	ESource *source;
+	ESource *input_source;
+	ESource *output_source;
 };
 
 struct _SourceClosure {
@@ -190,6 +191,10 @@ G_DEFINE_TYPE_WITH_CODE (
 static void
 async_context_free (AsyncContext *async_context)
 {
+	g_list_free_full (
+		async_context->input_sources,
+		(GDestroyNotify) g_object_unref);
+
 	if (async_context->input_source != NULL)
 		g_object_unref (async_context->input_source);
 
@@ -234,24 +239,21 @@ create_context_new (ESourceRegistry *registry)
 	create_context->main_loop = g_main_loop_new (
 		create_context->main_context, FALSE);
 
-	g_main_context_push_thread_default (create_context->main_context);
-
 	return create_context;
 }
 
 static void
 create_context_free (CreateContext *create_context)
 {
-	g_main_context_pop_thread_default (create_context->main_context);
-
 	g_object_unref (create_context->registry);
 	g_main_context_unref (create_context->main_context);
 	g_main_loop_unref (create_context->main_loop);
 
-	if (create_context->source != NULL)
-		g_object_unref (create_context->source);
+	if (create_context->input_source != NULL)
+		g_object_unref (create_context->input_source);
 
-	g_free (create_context->object_path);
+	if (create_context->output_source != NULL)
+		g_object_unref (create_context->output_source);
 
 	g_slice_free (CreateContext, create_context);
 }
@@ -2117,18 +2119,11 @@ source_registry_create_idle_cb (gpointer user_data)
 {
 	CreateContext *create_context = user_data;
 
-	/* We should know what object path we're looking for by now. */
-	g_return_val_if_fail (create_context->object_path != NULL, FALSE);
+	/* If we got here it means we found a matching ESource. */
+	g_return_val_if_fail (create_context->output_source != NULL, FALSE);
 
-	/* Find the corresponding ESource in the object path table.
-	 * Note that the lookup returns a new ESource reference. */
-	create_context->source =
-		source_registry_object_path_table_lookup (
-		create_context->registry, create_context->object_path);
-
-	/* If we got a hit, terminate the main loop. */
-	if (create_context->source != NULL)
-		g_main_loop_quit (create_context->main_loop);
+	/* Just need to terminate the main loop. */
+	g_main_loop_quit (create_context->main_loop);
 
 	return FALSE;
 }
@@ -2139,19 +2134,42 @@ source_registry_create_object_added_cb (GDBusObjectManager *object_manager,
                                         GDBusObject *dbus_object,
                                         CreateContext *create_context)
 {
-	GSource *idle_source;
+	ESource *source;
+	const gchar *object_path;
 
-	/* Schedule a callback in response to an object being added.
-	 * Note that we're in the object manager thread here, so we
-	 * want to do as little as possible. */
+	/* Note that we're in the object manager thread here.
+	 * The object manager thread's "object-added" handler
+	 * should have already run.  We depend on it. */
 
-	idle_source = g_idle_source_new ();
-	g_source_set_callback (
-		idle_source,
-		source_registry_create_idle_cb,
-		create_context, (GDestroyNotify) NULL);
-	g_source_attach (idle_source, create_context->main_context);
-	g_source_unref (idle_source);
+	object_path = g_dbus_object_get_object_path (dbus_object);
+	g_return_if_fail (object_path != NULL);
+
+	/* Find the corresponding ESource in the object path table.
+	 * Note that the lookup returns a new ESource reference. */
+	source = source_registry_object_path_table_lookup (
+		create_context->registry, object_path);
+	g_return_if_fail (source != NULL);
+
+	/* Check if this is the source we want based on its UID. */
+	if (e_source_equal (source, create_context->input_source)) {
+		GSource *idle_source;
+
+		create_context->output_source = g_object_ref (source);
+
+		/* Schedule an idle source to quit the main loop.
+		 * We do it this way because the main loop may not
+		 * be running yet, but an idle source will not be
+		 * dispatched until the main loop is running. */
+		idle_source = g_idle_source_new ();
+		g_source_set_callback (
+			idle_source,
+			source_registry_create_idle_cb,
+			create_context, (GDestroyNotify) NULL);
+		g_source_attach (idle_source, create_context->main_context);
+		g_source_unref (idle_source);
+	}
+
+	g_object_unref (source);
 }
 
 /**
@@ -2184,6 +2202,8 @@ e_source_registry_create_source_sync (ESourceRegistry *registry,
                                       GError **error)
 {
 	CreateContext *create_context;
+	GVariantBuilder builder;
+	GVariant *variant;
 	gulong object_added_id;
 	gchar *source_data;
 	const gchar *uid;
@@ -2203,59 +2223,71 @@ e_source_registry_create_source_sync (ESourceRegistry *registry,
 	 *
 	 *     This is most likely to occur when creating a collection of
 	 *     related sources one at a time and the caller is not careful
-	 *     to create parent sources before children.  Perhaps a method
-	 *     to create the collection in parallel would help to mitigate
-	 *     the deadlock risk?
+	 *     to create parent sources before children.
+	 *
+	 *     ADDENDUM
+	 *
+	 *     I wrote e_source_registry_create_multiple_sources_sync()
+	 *     to address this issue, and so that collections are added
+	 *     atomically on the server side (wrt the main loop) which
+	 *     is easier for collection backends to deal with.
 	 */
 
+	uid = e_source_get_uid (input_source);
+
 	create_context = create_context_new (registry);
+	create_context->input_source = g_object_ref (input_source);
+
+	g_main_context_push_thread_default (create_context->main_context);
 
 	/* Start listening for the "object-added" signal BEFORE we call
-	 * the D-Bus method, even though we don't know what object path
-	 * we're looking for yet.  The signal may be emitted before the
-	 * D-Bus method returns. */
+	 * the D-Bus method.  The signal may be emitted before the D-Bus
+	 * method returns.
+	 *
+	 * The object manager thread's "object-added" signal handler
+	 * will run first and add a new ESource instance to the object
+	 * path table.  We then grab it from the object path table and
+	 * compare it to the input source.
+	 */
 	object_added_id = g_signal_connect_data (
 		registry->priv->dbus_object_manager, "object-added",
 		G_CALLBACK (source_registry_create_object_added_cb),
 		create_context, (GClosureNotify) create_context_free, 0);
 
-	uid = e_source_get_uid (input_source);
 	source_data = e_source_to_string (input_source, NULL);
 
-	/* Setting the object path directly on the CreateContext
-	 * is thread-safe because only the idle callbacks on our
-	 * GMainContext use the object path, but we have not yet
-	 * started our main loop so any scheduled idle callbacks
-	 * are still pending. */
-	success = e_dbus_source_manager_call_create_source_sync (
+	g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+	g_variant_builder_add (&builder, "{ss}", uid, source_data);
+	variant = g_variant_builder_end (&builder);
+
+	/* This function sinks the floating GVariant reference. */
+	success = e_dbus_source_manager_call_create_sources_sync (
 		registry->priv->dbus_source_manager,
-		uid, source_data, &create_context->object_path,
-		cancellable, error);
+		variant, cancellable, error);
 
-	g_free (source_data);
+	g_variant_builder_clear (&builder);
 
-	/* Sanity check. */
-	g_warn_if_fail (
-		(success && create_context->object_path != NULL) ||
-		(!success && create_context->object_path == NULL));
+	g_free (source_data);
 
 	if (success) {
 		/* Now start the main loop.  It will terminate
 		 * when we have our newly-created ESource. */
 		g_main_loop_run (create_context->main_loop);
 
-		g_warn_if_fail (E_IS_SOURCE (create_context->source));
+		g_warn_if_fail (create_context->output_source);
 
 		/* Hand the newly-created ESource back to
 		 * the caller if the caller is interested. */
 		if (output_source != NULL) {
-			*output_source = create_context->source;
-			create_context->source = NULL;
+			*output_source = create_context->output_source;
+			create_context->output_source = NULL;
 		}
 	}
 
 	/* Clean up. */
 
+	g_main_context_pop_thread_default (create_context->main_context);
+
 	/* This will free the CreateContext signal handler data. */
 	g_signal_handler_disconnect (
 		registry->priv->dbus_object_manager, object_added_id);
@@ -2354,6 +2386,182 @@ e_source_registry_create_source_finish (ESourceRegistry *registry,
 	return TRUE;
 }
 
+/* Helper for e_source_registry_create_multiple_sources() */
+static void
+source_registry_create_multiple_sources_thread (GSimpleAsyncResult *simple,
+                                                GObject *object,
+                                                GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	e_source_registry_create_multiple_sources_sync (
+		E_SOURCE_REGISTRY (object),
+		async_context->input_sources,
+		cancellable, &error);
+
+	if (error != NULL)
+		g_simple_async_result_take_error (simple, error);
+}
+
+/**
+ * e_source_registry_create_multiple_sources_sync:
+ * @registry: an #ESourceRegistry
+ * @input_sources: a list of #ESource instances with no #GDBusObject
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Requests the D-Bus service create new key files for each #ESource in
+ * @input_sources.  Each list element must be a throw-away #ESource with
+ * no #GDBusObject.
+ *
+ * Use this function when creating a collection of related data sources
+ * so the D-Bus service can handle them all at once.
+ *
+ * If an error occurs, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_registry_create_multiple_sources_sync (ESourceRegistry *registry,
+                                                GList *input_sources,
+                                                GCancellable *cancellable,
+                                                GError **error)
+{
+	GVariantBuilder builder;
+	GVariant *variant;
+	GList *link;
+	gboolean success;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
+
+	/* Verify the list elements are all ESources. */
+	for (link = input_sources; link != NULL; link = g_list_next (link))
+		g_return_val_if_fail (E_IS_SOURCE (link->data), FALSE);
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+
+	for (link = input_sources; link != NULL; link = g_list_next (link)) {
+		ESource *source;
+		const gchar *uid;
+		gchar *source_data;
+
+		source = E_SOURCE (link->data);
+		uid = e_source_get_uid (source);
+
+		source_data = e_source_to_string (source, NULL);
+		g_variant_builder_add (&builder, "{ss}", uid, source_data);
+		g_free (source_data);
+	}
+
+	variant = g_variant_builder_end (&builder);
+
+	/* This function sinks the floating GVariant reference. */
+	success = e_dbus_source_manager_call_create_sources_sync (
+		registry->priv->dbus_source_manager,
+		variant, cancellable, error);
+
+	g_variant_builder_clear (&builder);
+
+	return success;
+}
+
+/**
+ * e_source_registry_create_multiple_sources:
+ * @registry: an #ESourceRegistry
+ * @input_sources: a list of #ESource instances with no #GDBusObject
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously requests the D-Bus service create new key files for each
+ * #ESource in @input_sources.  Each list element must be a throw-away
+ * #ESource with no #GDBusObject.
+ *
+ * Use this function when creating a collection of related data sources
+ * so the D-Bus service can handle them all at once.
+ *
+ * When the operation is finished, @callback will be called.  You can then
+ * call e_source_registry_create_multiple_sources_finish() to get the result
+ * of the operation.
+ *
+ * Since: 3.6
+ **/
+void
+e_source_registry_create_multiple_sources (ESourceRegistry *registry,
+                                           GList *input_sources,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+	GList *link;
+
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+
+	/* Verify the list elements are all ESources. */
+	for (link = input_sources; link != NULL; link = g_list_next (link))
+		g_return_if_fail (E_IS_SOURCE (link->data));
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->input_sources = g_list_copy (input_sources);
+
+	g_list_foreach (
+		async_context->input_sources,
+		(GFunc) g_object_ref, NULL);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (registry), callback, user_data,
+		e_source_registry_create_multiple_sources);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, source_registry_create_multiple_sources_thread,
+		G_PRIORITY_DEFAULT, cancellable);
+
+	g_object_unref (simple);
+}
+
+/**
+ * e_source_registry_create_multiple_sources_finish:
+ * @registry: an #ESourceRegistry
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with
+ * e_source_registry_create_multiple_sources().
+ *
+ * If an error occurred, the function will set @error and return %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.6
+ **/
+gboolean
+e_source_registry_create_multiple_sources_finish (ESourceRegistry *registry,
+                                                  GAsyncResult *result,
+                                                  GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (registry),
+		e_source_registry_create_multiple_sources), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
 /**
  * e_source_registry_ref_source:
  * @registry: an #ESourceRegistry
diff --git a/libedataserver/e-source-registry.h b/libedataserver/e-source-registry.h
index 523217a..4be5273 100644
--- a/libedataserver/e-source-registry.h
+++ b/libedataserver/e-source-registry.h
@@ -134,6 +134,21 @@ gboolean	e_source_registry_create_source_finish
 						 GAsyncResult *result,
 						 ESource **output_source,
 						 GError **error);
+gboolean	e_source_registry_create_multiple_sources_sync
+						(ESourceRegistry *registry,
+						 GList *input_sources,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_source_registry_create_multiple_sources
+						(ESourceRegistry *registry,
+						 GList *input_sources,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+gboolean	e_source_registry_create_multiple_sources_finish
+						(ESourceRegistry *registry,
+						 GAsyncResult *result,
+						 GError **error);
 ESource *	e_source_registry_ref_source	(ESourceRegistry *registry,
 						 const gchar *uid);
 GList *		e_source_registry_list_sources	(ESourceRegistry *registry,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]