commit 4c45a2c07dfb9f6f0fca3ed8d4e372603ed3105f
Author: Tristan Van Berkom <tristan.van.berkom@gmail.com>
Date:   Sat Jul 23 18:05:32 2011 -0400

    Allow asynchronous storing of data in ECal.
    
    Patch adds the functions:
      o e_cal_create_object_async()
      o e_cal_modify_object_async()
      o e_cal_remove_object_async()
      o e_cal_remove_object_with_mod_async()
    
    And updates the test cases to test the _async() variants:
      calendar/tests/ecal/test-ecal-create-object.c
      calendar/tests/ecal/test-ecal-modify-object.c
      calendar/tests/ecal/test-ecal-remove-object.c
    
    Addresses bug: https://bugzilla.gnome.org/show_bug.cgi?id=652174

diff --git a/calendar/libecal/e-cal.c b/calendar/libecal/e-cal.c
index fcdc9f2..fc10525 100644
--- a/calendar/libecal/e-cal.c
+++ b/calendar/libecal/e-cal.c
@@ -122,6 +122,12 @@ struct _ECalPrivate {
 	GStaticRecMutex cache_lock;
 };
 
+typedef struct {
+	ECal      *cal;
+	gpointer   callback;
+	gpointer   user_data;
+} AsyncData;
+
 
 
 /* Signal IDs */
@@ -3629,6 +3635,73 @@ e_cal_create_object (ECal *ecal, icalcomponent *icalcomp, gchar **uid, GError **
 	}
 }
 
+static void
+create_object_reply (GObject *gdbus_cal, GAsyncResult *res, gpointer user_data)
+{
+	GError              *error    = NULL;
+	AsyncData           *data     = user_data;
+	ECalIdAsyncCallback  callback = data->callback;
+	gchar               *id       = NULL;
+
+	if (!e_gdbus_cal_call_create_object_finish (E_GDBUS_CAL (gdbus_cal), &id, res, &error))
+		g_warning ("Error finishing createContact: %s", error->message);
+
+	unwrap_gerror (&error);
+
+	if (callback)
+		callback (data->cal, error, id, data->user_data);
+
+	if (error)
+		g_error_free (error);
+
+	g_free (id);
+	g_object_unref (data->cal);
+	g_slice_free (AsyncData, data);
+}
+
+/**
+ * e_cal_create_object_async:
+ * @ecal: A calendar client.
+ * @icalcomp: The component to create.
+ * @callback: A #ECalIdAsyncCallback which will be called when the operation is complete, or %NULL.
+ * @user_data: The pointer to be given to @callback
+ *
+ * Requests the calendar backend to create the object specified by the @icalcomp
+ * argument. Some backends would assign a specific UID to the newly created object,
+ * in those cases that UID would be returned as the id parameter of @callback when
+ * the operation is done.
+ *
+ * Returns: %TRUE if the async message was sent, %FALSE if some preconditions were not satisfied.
+ */
+gboolean
+e_cal_create_object_async (ECal *ecal, 
+			   icalcomponent *icalcomp, 
+			   ECalIdAsyncCallback callback,
+			   gpointer user_data)
+{
+	ECalPrivate *priv;
+	gchar       *obj;
+	AsyncData   *data;
+
+	g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
+	g_return_val_if_fail (icalcomp != NULL, FALSE);
+	g_return_val_if_fail (icalcomponent_is_valid (icalcomp), FALSE);
+	priv = ecal->priv;
+	g_return_val_if_fail (priv->gdbus_cal, FALSE);
+
+	data            = g_slice_new0 (AsyncData);
+	data->cal       = g_object_ref (ecal);
+	data->callback  = callback;
+	data->user_data = user_data;
+
+	obj = icalcomponent_as_ical_string_r (icalcomp);
+	e_gdbus_cal_call_create_object (priv->gdbus_cal, obj, NULL, create_object_reply, data);
+	g_free (obj);
+
+	return TRUE;
+}
+
+
 /**
  * e_cal_modify_object:
  * @ecal: A calendar client.
@@ -3681,6 +3754,84 @@ e_cal_modify_object (ECal *ecal, icalcomponent *icalcomp, CalObjModType mod, GEr
 	E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
 }
 
+static void
+modify_object_reply (GObject *gdbus_cal, GAsyncResult *res, gpointer user_data)
+{
+	GError              *error    = NULL;
+	AsyncData           *data     = user_data;
+	ECalAsyncCallback    callback = data->callback;
+
+	if (!e_gdbus_cal_call_modify_object_finish (E_GDBUS_CAL (gdbus_cal), res, &error))
+		g_warning ("Error finishing modifyContact: %s", error->message);
+
+	unwrap_gerror (&error);
+
+	if (callback)
+		callback (data->cal, error, data->user_data);
+
+	if (error)
+		g_error_free (error);
+
+	g_object_unref (data->cal);
+	g_slice_free (AsyncData, data);
+}
+
+/**
+ * e_cal_modify_object_async:
+ * @ecal: A calendar client.
+ * @icalcomp: Component to modify.
+ * @mod: Type of modification.
+ * @callback: A #ECalAsyncCallback which will be called when the operation is complete, or %NULL.
+ * @user_data: The pointer to be given to @callback
+ *
+ * Requests the calendar backend to modify an existing object. If the object
+ * does not exist on the calendar, an error will be returned in the async callback.
+ *
+ * For recurrent appointments, the @mod argument specifies what to modify,
+ * if all instances (CALOBJ_MOD_ALL), a single instance (CALOBJ_MOD_THIS),
+ * or a specific set of instances (CALOBJ_MOD_THISNADPRIOR and
+ * CALOBJ_MOD_THISANDFUTURE).
+ *
+ * Returns: %TRUE if the async message was sent, %FALSE if some preconditions were not satisfied.
+ */
+gboolean
+e_cal_modify_object_async (ECal             *ecal, 
+			   icalcomponent    *icalcomp, 
+			   CalObjModType     mod, 
+			   ECalAsyncCallback callback, 
+			   gpointer          user_data)
+{
+	ECalPrivate *priv;
+	gchar       *obj;
+	AsyncData   *data;
+
+	g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
+	g_return_val_if_fail (icalcomp, FALSE);
+	g_return_val_if_fail (icalcomponent_is_valid (icalcomp), FALSE);
+	switch (mod) {
+	case CALOBJ_MOD_THIS:
+	case CALOBJ_MOD_THISANDPRIOR:
+	case CALOBJ_MOD_THISANDFUTURE:
+	case CALOBJ_MOD_ALL:
+		break;
+	default:
+		g_return_val_if_fail ("valid CalObjModType" && FALSE, FALSE);
+	}
+	priv = ecal->priv;
+	g_return_val_if_fail (priv->gdbus_cal, FALSE);
+
+	data            = g_slice_new0 (AsyncData);
+	data->cal       = g_object_ref (ecal);
+	data->callback  = callback;
+	data->user_data = user_data;
+
+	obj = icalcomponent_as_ical_string_r (icalcomp);
+	e_gdbus_cal_call_modify_object (priv->gdbus_cal, obj, mod, NULL, modify_object_reply, data);
+	g_free (obj);
+
+	return TRUE;
+}
+
 /**
  * e_cal_remove_object_with_mod:
  * @ecal: A calendar client.
@@ -3758,6 +3909,73 @@ e_cal_remove_object_with_mod (ECal *ecal, const gchar *uid,
 	E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
 }
 
+static void
+remove_object_reply (GObject *gdbus_cal, GAsyncResult *res, gpointer user_data)
+{
+	GError              *error    = NULL;
+	AsyncData           *data     = user_data;
+	ECalAsyncCallback    callback = data->callback;
+
+	if (!e_gdbus_cal_call_remove_object_finish (E_GDBUS_CAL (gdbus_cal), res, &error))
+		g_warning ("Error finishing removeContact: %s", error->message);
+
+	unwrap_gerror (&error);
+
+	if (callback)
+		callback (data->cal, error, data->user_data);
+
+	if (error)
+		g_error_free (error);
+
+	g_object_unref (data->cal);
+	g_slice_free (AsyncData, data);
+}
+
+/**
+ * e_cal_remove_object_with_mod_async:
+ * @ecal: A calendar client.
+ * @uid: UID of the object to remove.
+ * @rid: Recurrence ID of the specific recurrence to remove.
+ * @mod: Type of removal.
+ * @callback: A #ECalAsyncCallback which will be called when the operation is complete, or %NULL.
+ * @user_data: The pointer to be given to @callback
+ *
+ * An asynchronous method for removing objects, see e_cal_remove_object_with_mod() for full details.
+ *
+ * Returns: %TRUE if the async message was sent, %FALSE if some preconditions were not satisfied.
+ */
+gboolean 
+e_cal_remove_object_with_mod_async (ECal *ecal, const gchar *uid, const gchar *rid, CalObjModType mod, ECalAsyncCallback callback, gpointer user_data)
+{
+	ECalPrivate *priv;
+	AsyncData   *data;
+
+	g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
+	g_return_val_if_fail (uid, FALSE);
+	switch (mod) {
+	case CALOBJ_MOD_THIS:
+	case CALOBJ_MOD_THISANDPRIOR:
+	case CALOBJ_MOD_THISANDFUTURE:
+	case CALOBJ_MOD_ONLY_THIS:
+	case CALOBJ_MOD_ALL:
+		break;
+	default:
+		g_return_val_if_fail ("valid CalObjModType" && FALSE, FALSE);
+	}
+	priv = ecal->priv;
+	g_return_val_if_fail (priv->gdbus_cal, FALSE);
+
+	data            = g_slice_new0 (AsyncData);
+	data->cal       = g_object_ref (ecal);
+	data->callback  = callback;
+	data->user_data = user_data;
+
+	e_gdbus_cal_call_remove_object (priv->gdbus_cal, uid, rid ? rid : "", mod, NULL,
+					remove_object_reply, data);
+
+	return TRUE;
+}
+
 /**
  * e_cal_remove_object:
  * @ecal:  A calendar client.
@@ -3780,6 +3998,23 @@ e_cal_remove_object (ECal *ecal, const gchar *uid, GError **error)
 }
 
 /**
+ * e_cal_remove_object_async:
+ * @ecal: A calendar client.
+ * @uid: UID of the object to remove.
+ * @callback: A #ECalAsyncCallback which will be called when the operation is complete, or %NULL.
+ * @user_data: The pointer to be given to @callback
+ *
+ * An asynchronous method for removing objects, see e_cal_remove_object() for the synchronous version.
+ *
+ * Returns: %TRUE if the async message was sent, %FALSE if some preconditions were not satisfied.
+ */
+gboolean 
+e_cal_remove_object_async (ECal *ecal, const gchar *uid, ECalAsyncCallback callback, gpointer user_data)
+{
+	return e_cal_remove_object_with_mod_async (ecal, uid, NULL, CALOBJ_MOD_THIS, callback, user_data);
+}
+
+/**
  * e_cal_receive_objects:
  * @ecal:  A calendar client.
  * @icalcomp: An icalcomponent.
diff --git a/calendar/libecal/e-cal.h b/calendar/libecal/e-cal.h
index ead68c6..c7bd500 100644
--- a/calendar/libecal/e-cal.h
+++ b/calendar/libecal/e-cal.h
@@ -47,6 +47,20 @@ typedef struct _ECal ECal;
 typedef struct _ECalClass ECalClass;
 typedef struct _ECalPrivate ECalPrivate;
 
+
+/**
+ * ECalAsyncCallback:
+ *
+ **/
+typedef void (*ECalAsyncCallback) (ECal *cal, const GError *error, gpointer user_data);
+
+/**
+ * ECalIdAsyncCallback:
+ *
+ **/
+typedef void (*ECalIdAsyncCallback) (ECal *cal, const GError *error, const gchar *id, gpointer user_data);
+
+
 typedef enum {
 	E_CAL_SOURCE_TYPE_EVENT,
 	E_CAL_SOURCE_TYPE_TODO,
@@ -176,9 +190,16 @@ gboolean e_cal_get_alarms_for_object (ECal *ecal, const ECalComponentId *id,
 				      ECalComponentAlarms **alarms);
 
 gboolean e_cal_create_object (ECal *ecal, icalcomponent *icalcomp, gchar **uid, GError **error);
+gboolean e_cal_create_object_async (ECal *ecal, icalcomponent *icalcomp, ECalIdAsyncCallback callback, gpointer user_data);
+
 gboolean e_cal_modify_object (ECal *ecal, icalcomponent *icalcomp, CalObjModType mod, GError **error);
+gboolean e_cal_modify_object_async (ECal *ecal, icalcomponent *icalcomp, CalObjModType mod, ECalAsyncCallback callback, gpointer user_data);
+
 gboolean e_cal_remove_object (ECal *ecal, const gchar *uid, GError **error);
+gboolean e_cal_remove_object_async (ECal *ecal, const gchar *uid, ECalAsyncCallback callback, gpointer user_data);
+
 gboolean e_cal_remove_object_with_mod (ECal *ecal, const gchar *uid, const gchar *rid, CalObjModType mod, GError **error);
+gboolean e_cal_remove_object_with_mod_async (ECal *ecal, const gchar *uid, const gchar *rid, CalObjModType mod, ECalAsyncCallback callback, gpointer user_data);
 
 gboolean e_cal_discard_alarm (ECal *ecal, ECalComponent *comp, const gchar *auid, GError **error);
 
diff --git a/calendar/tests/ecal/test-ecal-create-object.c b/calendar/tests/ecal/test-ecal-create-object.c
index e9f2301..c0074c1 100644
--- a/calendar/tests/ecal/test-ecal-create-object.c
+++ b/calendar/tests/ecal/test-ecal-create-object.c
@@ -6,31 +6,76 @@
 
 #include "ecal-test-utils.h"
 
+
+icalcomponent *component = NULL;
+
+
+static void
+object_created_cb (ECal *cal, const GError *error, const gchar *id, gpointer user_data)
+{
+	GMainLoop *loop = (GMainLoop *)user_data;
+	icalcomponent *component_final;
+
+	if (error)
+		g_error ("Error removing object: %s", error->message);
+
+	g_assert (id != NULL);
+
+	/* We only get the uid after the backend adds the component, set the uid
+	 * here before comparing the newly returned 'component_final' */
+	icalcomponent_set_uid (component, id);
+
+	component_final = ecal_test_utils_cal_get_object (cal, id);
+	ecal_test_utils_cal_assert_objects_equal_shallow (component, component_final);
+
+	test_print ("e_cal_create_object_async() created id '%s'\n", id);
+
+	icalcomponent_free (component_final);
+
+	g_main_loop_quit (loop);
+}
+
 gint
 main (gint argc, gchar **argv)
 {
 	ECal *cal;
 	gchar *uri = NULL;
-	icalcomponent *component;
-	icalcomponent *component_final;
 	gchar *uid;
+	icalcomponent *component_final;
+	GMainLoop *loop;
 
 	g_type_init ();
 
 	cal = ecal_test_utils_cal_new_temp (&uri, E_CAL_SOURCE_TYPE_EVENT);
 	ecal_test_utils_cal_open (cal, FALSE);
 
+	/* Sync Version */
 	component = icalcomponent_new (ICAL_VEVENT_COMPONENT);
 	uid = ecal_test_utils_cal_create_object (cal, component);
 
 	component_final = ecal_test_utils_cal_get_object (cal, uid);
 	ecal_test_utils_cal_assert_objects_equal_shallow (component, component_final);
 
-	ecal_test_utils_cal_remove (cal);
-
 	g_free (uid);
 	icalcomponent_free (component);
 	icalcomponent_free (component_final);
+	uid = NULL;
+
+	/* Async Version */
+	loop = g_main_loop_new (NULL, FALSE);
+
+	component = icalcomponent_new (ICAL_VEVENT_COMPONENT);
+
+	e_cal_create_object_async (cal, component, 
+				   object_created_cb,
+				   loop);
+
+	g_main_loop_run (loop);
+
+	icalcomponent_free (component);
+
+	ecal_test_utils_cal_remove (cal);
+	g_main_loop_unref (loop);
 
 	return 0;
 }
diff --git a/calendar/tests/ecal/test-ecal-modify-object.c b/calendar/tests/ecal/test-ecal-modify-object.c
index b1c9933..4351485 100644
--- a/calendar/tests/ecal/test-ecal-modify-object.c
+++ b/calendar/tests/ecal/test-ecal-modify-object.c
@@ -14,56 +14,125 @@
 #define FINAL_BEGIN_TIME       "20091221T090000Z"
 #define FINAL_BEGIN_TIMEZONE   "UTC"
 
+
+typedef struct {
+	GMainLoop     *loop;
+	ECal          *cal;
+	ECalComponent *e_component; 
+	const gchar   *uid;
+} AsyncData;
+
+static void
+verify (ECal *cal, ECalComponent *e_component, const gchar *uid)
+{
+	icalcomponent *component;
+	icalcomponent *component_final;
+	ECalComponent *e_component_final;
+
+        component = e_cal_component_get_icalcomponent (e_component);
+
+        component_final = ecal_test_utils_cal_get_object (cal, uid);
+        e_component_final = e_cal_component_new ();
+        ecal_test_utils_cal_component_set_icalcomponent (e_component_final,
+							 component_final);
+
+        ecal_test_utils_cal_assert_e_cal_components_equal (e_component,
+							   e_component_final);
+
+	g_object_unref (e_component_final);
+}
+
+static void 
+object_modified_cb (ECal *cal, const GError *error, gpointer user_data)
+{
+	AsyncData *data =  (AsyncData *)user_data;
+
+	if (error)
+		g_error ("Error modifying object: %s", error->message);
+
+	/* verify */
+	verify (data->cal, data->e_component, data->uid);
+
+	test_print ("e_cal_modify_object_async() passed for uid '%s'\n", data->uid);
+
+	g_main_loop_quit (data->loop);
+}
+
 gint
 main (gint argc, gchar **argv)
 {
 	ECal *cal;
 	gchar *uri = NULL;
 	ECalComponent *e_component;
-	ECalComponent *e_component_final;
 	icalcomponent *component;
 	icalcomponent *component_final;
 	struct icaltimetype icaltime;
 	gchar *uid;
+	GMainLoop *loop;
+	AsyncData data = { 0, };
 
 	g_type_init ();
 
 	cal = ecal_test_utils_cal_new_temp (&uri, E_CAL_SOURCE_TYPE_EVENT);
 	ecal_test_utils_cal_open (cal, FALSE);
 
+	/* 
+	 * Sync Version
+	 */
 	ecal_test_utils_create_component (cal, INITIAL_BEGIN_TIME,
 			INITIAL_BEGIN_TIMEZONE, INITIAL_END_TIME,
-			INITIAL_END_TIMEZONE, EVENT_SUMMARY, &e_component,
-			&uid);
+			INITIAL_END_TIMEZONE, EVENT_SUMMARY, &e_component, &uid);
         component = e_cal_component_get_icalcomponent (e_component);
 
 	component_final = ecal_test_utils_cal_get_object (cal, uid);
-	ecal_test_utils_cal_assert_objects_equal_shallow (component,
-			component_final);
+	ecal_test_utils_cal_assert_objects_equal_shallow (component, component_final);
 	icalcomponent_free (component_final);
 
 	/* make and commit changes */
 	icaltime = icaltime_from_string (FINAL_BEGIN_TIME);
 	icalcomponent_set_dtstart (component, icaltime);
-	ecal_test_utils_cal_component_set_icalcomponent (e_component,
-			component);
+	ecal_test_utils_cal_component_set_icalcomponent (e_component, component);
 	ecal_test_utils_cal_modify_object (cal, component, CALOBJ_MOD_ALL);
 
 	/* verify */
-        component_final = ecal_test_utils_cal_get_object (cal, uid);
-        e_component_final = e_cal_component_new ();
-        ecal_test_utils_cal_component_set_icalcomponent (e_component_final,
-                                component_final);
+	verify (cal, e_component, uid);
 
-        ecal_test_utils_cal_assert_e_cal_components_equal (e_component,
-                        e_component_final);
+	g_free (uid);
+	icalcomponent_free (component);
 
-	/* Clean-up */
-	ecal_test_utils_cal_remove (cal);
+	/* 
+	 * Async Version
+	 */
+	ecal_test_utils_create_component (cal, INITIAL_BEGIN_TIME,
+			INITIAL_BEGIN_TIMEZONE, INITIAL_END_TIME,
+			INITIAL_END_TIMEZONE, EVENT_SUMMARY, &e_component, &uid);
+        component = e_cal_component_get_icalcomponent (e_component);
+
+	component_final = ecal_test_utils_cal_get_object (cal, uid);
+	ecal_test_utils_cal_assert_objects_equal_shallow (component, component_final);
+	icalcomponent_free (component_final);
+
+	/* make and commit changes */
+	icaltime = icaltime_from_string (FINAL_BEGIN_TIME);
+	icalcomponent_set_dtstart (component, icaltime);
+	ecal_test_utils_cal_component_set_icalcomponent (e_component, component);
+
+	loop = g_main_loop_new (NULL, FALSE);
+	data.loop = loop;
+	data.cal  = cal;
+	data.e_component = e_component;
+	data.uid  = uid;
+
+	e_cal_modify_object_async (cal, component, CALOBJ_MOD_ALL, object_modified_cb, &data);
+
+	g_main_loop_run (loop);
+	g_main_loop_unref (loop);
 
-	g_object_unref (e_component_final);
 	g_free (uid);
 	icalcomponent_free (component);
 
+	/* Clean-up */
+	ecal_test_utils_cal_remove (cal);
+
 	return 0;
 }
diff --git a/calendar/tests/ecal/test-ecal-remove-object.c b/calendar/tests/ecal/test-ecal-remove-object.c
index 1bc9e1c..75ded85 100644
--- a/calendar/tests/ecal/test-ecal-remove-object.c
+++ b/calendar/tests/ecal/test-ecal-remove-object.c
@@ -6,6 +6,19 @@
 
 #include "ecal-test-utils.h"
 
+static void
+object_removed_cb (ECal *cal, const GError *error, gpointer user_data)
+{
+	GMainLoop *loop = (GMainLoop *)user_data;
+
+	if (error)
+		g_error ("Error removing object: %s", error->message);
+
+	test_print ("e_cal_remove_object_async() passed successfully\n");
+
+	g_main_loop_quit (loop);
+}
+
 gint
 main (gint argc, gchar **argv)
 {
@@ -14,23 +27,49 @@ main (gint argc, gchar **argv)
 	icalcomponent *component;
 	icalcomponent *component_final;
 	gchar *uid;
+	GMainLoop *loop;
 
 	g_type_init ();
 
 	cal = ecal_test_utils_cal_new_temp (&uri, E_CAL_SOURCE_TYPE_EVENT);
 	ecal_test_utils_cal_open (cal, FALSE);
 
+	/*
+	 * Sync Version
+	 */
 	component = icalcomponent_new (ICAL_VEVENT_COMPONENT);
 	uid = ecal_test_utils_cal_create_object (cal, component);
 
 	component_final = ecal_test_utils_cal_get_object (cal, uid);
 	ecal_test_utils_cal_assert_objects_equal_shallow (component, component_final);
 	ecal_test_utils_cal_remove_object (cal, uid);
-	ecal_test_utils_cal_remove (cal);
 
 	g_free (uid);
 	icalcomponent_free (component);
 	icalcomponent_free (component_final);
 
+	/*
+	 * Async Version
+	 */
+	component = icalcomponent_new (ICAL_VEVENT_COMPONENT);
+	uid = ecal_test_utils_cal_create_object (cal, component);
+
+	component_final = ecal_test_utils_cal_get_object (cal, uid);
+	ecal_test_utils_cal_assert_objects_equal_shallow (component, component_final);
+
+	loop = g_main_loop_new (NULL, FALSE);
+	e_cal_remove_object_async (cal, uid, object_removed_cb, loop);
+	
+	g_main_loop_run (loop);
+
+	g_free (uid);
+	icalcomponent_free (component);
+	icalcomponent_free (component_final);
+
+	/* Cleanup */
+	ecal_test_utils_cal_remove (cal);
+
+	g_main_loop_unref (loop);
+
 	return 0;
 }
