[evolution] Add e_load_cal_source_async().



commit 7c1ec3723ad7367449c93e8559c27158b24a51dd
Author: Matthew Barnes <mbarnes redhat com>
Date:   Fri Aug 20 13:25:21 2010 -0400

    Add e_load_cal_source_async().
    
    Similar to e_load_book_source_async() in libedataserverui (and may wind
    up there eventually).  This replaces e_auth_new_cal_from_source().
    
      void   e_load_cal_source_async  (ESource *source,
                                       ECalSourceType source_type,
                                       icaltimezone *default_zone,
                                       GtkWindow *parent,
                                       GCancellable *cancellable,
                                       GAsyncReadyCallback callback,
                                       gpointer user_data);
    
      ECal * e_load_cal_source_finish (ESource *source,
                                       GAsyncResult *result,
                                       GError **error);

 calendar/common/authentication.c           |  273 +++++++++++++++++++++++++++-
 calendar/common/authentication.h           |   18 ++-
 modules/addressbook/e-book-shell-backend.c |    7 +-
 modules/calendar/e-cal-shell-backend.c     |  130 +++++++------
 modules/calendar/e-cal-shell-sidebar.c     |  126 +++++++------
 modules/calendar/e-memo-shell-backend.c    |  104 ++++++-----
 modules/calendar/e-memo-shell-sidebar.c    |  126 +++++++------
 modules/calendar/e-task-shell-backend.c    |  101 ++++++-----
 modules/calendar/e-task-shell-sidebar.c    |  126 +++++++------
 9 files changed, 668 insertions(+), 343 deletions(-)
---
diff --git a/calendar/common/authentication.c b/calendar/common/authentication.c
index 675754c..b36e10a 100644
--- a/calendar/common/authentication.c
+++ b/calendar/common/authentication.c
@@ -32,8 +32,6 @@
 #include "authentication.h"
 #include <libedataserver/e-url.h>
 
-static GHashTable *source_lists_hash = NULL;
-
 static gchar *
 auth_func_cb (ECal *ecal,
               const gchar *prompt,
@@ -124,3 +122,274 @@ e_auth_new_cal_from_source (ESource *source, ECalSourceType type)
 
 	return cal;
 }
+
+typedef struct {
+	ECal *cal;
+	GtkWindow *parent;
+	GCancellable *cancellable;
+	ECalSourceType source_type;
+	icaltimezone *default_zone;
+
+	/* Authentication Details */
+	gchar *auth_uri;
+	gchar *auth_component;
+} LoadContext;
+
+static void
+load_cal_source_context_free (LoadContext *context)
+{
+	if (context->cal != NULL)
+		g_object_unref (context->cal);
+
+	if (context->parent != NULL)
+		g_object_unref (context->parent);
+
+	if (context->cancellable != NULL)
+		g_object_unref (context->cancellable);
+
+	g_free (context->auth_uri);
+	g_free (context->auth_component);
+
+	g_slice_free (LoadContext, context);
+}
+
+static void
+load_cal_source_get_auth_details (ESource *source,
+                                  LoadContext *context)
+{
+	const gchar *property;
+
+	/* ECal figures out most of the details before invoking the
+	 * authentication callback, but we still need a component name
+	 * for e_passwords_ask_password(). */
+
+	/* auth_component */
+
+	property = e_source_get_property (source, "auth-domain");
+
+	if (property == NULL)
+		property = "Calendar";
+
+	context->auth_component = g_strdup (property);
+}
+
+static gchar *
+load_cal_source_authenticate (ECal *cal,
+                              const gchar *prompt,
+                              const gchar *uri,
+                              LoadContext *context)
+{
+	const gchar *title;
+	gboolean remember;  /* not used */
+	gchar *password;
+
+	/* Remember the URI so we don't have to reconstruct it if
+	 * authentication fails and we have to forget the password. */
+	g_free (context->auth_uri);
+	context->auth_uri = g_strdup (uri);
+
+	/* XXX Dialog windows should not have titles. */
+	title = "";
+
+	password = e_passwords_get_password (context->auth_component, uri);
+
+	if (password == NULL)
+		password = e_passwords_ask_password (
+			title, context->auth_component, uri,
+			prompt, E_PASSWORDS_REMEMBER_FOREVER |
+			E_PASSWORDS_SECRET | E_PASSWORDS_ONLINE,
+			&remember, context->parent);
+
+	return password;
+}
+
+static void
+load_cal_source_thread (GSimpleAsyncResult *simple,
+                        ESource *source,
+                        GCancellable *cancellable)
+{
+	ECal *cal;
+	LoadContext *context;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	/* XXX This doesn't take a GError, it just dumps
+	 *     error messages to the terminal... so broken. */
+	cal = e_cal_new (source, context->source_type);
+	g_return_if_fail (cal != NULL);
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_object_unref (cal);
+		g_error_free (error);
+		return;
+	}
+
+	if (!e_cal_set_default_timezone (cal, context->default_zone, &error)) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_object_unref (cal);
+		g_error_free (error);
+		return;
+	}
+
+	/* XXX Why doesn't EBook have something like this? */
+	e_cal_set_auth_func (
+		cal, (ECalAuthFunc)
+		load_cal_source_authenticate, context);
+
+try_again:
+	if (!e_cal_open (cal, FALSE, &error))
+		goto fail;
+
+	/* We should be authenticated now if we're going to be,
+	 * so remove the authentication callback so it doesn't
+	 * get triggered after we free the load context. */
+	e_cal_set_auth_func (cal, NULL, NULL);
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_object_unref (cal);
+		g_error_free (error);
+		return;
+	}
+
+	context->cal = cal;
+
+	return;
+
+fail:
+	g_return_if_fail (error != NULL);
+
+	/* If authentication failed, forget the password and reprompt. */
+	if (g_error_matches (
+		error, E_CALENDAR_ERROR,
+		E_CALENDAR_STATUS_AUTHENTICATION_FAILED)) {
+		e_passwords_forget_password (
+			context->auth_component, context->auth_uri);
+		g_clear_error (&error);
+		goto try_again;
+
+	/* XXX Might this cause a busy loop? */
+	} else if (g_error_matches (
+		error, E_CALENDAR_ERROR, E_CALENDAR_STATUS_BUSY)) {
+		g_clear_error (&error);
+		goto try_again;
+
+	} else {
+		g_simple_async_result_set_from_error (simple, error);
+		g_object_unref (cal);
+		g_error_free (error);
+	}
+}
+
+/**
+ * e_load_cal_source_async:
+ * @source: an #ESource
+ * @source_type: the type of #ECal to load
+ * @default_zone: default time zone, or %NULL to use UTC
+ * @parent: parent window for password dialogs, or %NULL
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to @callback
+ *
+ * Creates a new #ECal specified by @source and opens it, prompting the
+ * user for authentication if necessary.
+ *
+ * When the operation is finished, @callback will be called.  You can
+ * then call e_load_cal_source_finish() to obtain the resulting #ECal.
+ **/
+void
+e_load_cal_source_async (ESource *source,
+                         ECalSourceType source_type,
+                         icaltimezone *default_zone,
+                         GtkWindow *parent,
+                         GCancellable *cancellable,
+                         GAsyncReadyCallback callback,
+                         gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	LoadContext *context;
+
+	g_return_if_fail (E_IS_SOURCE (source));
+
+	/* Source must have a group so we can obtain its URI. */
+	g_return_if_fail (e_source_peek_group (source) != NULL);
+
+	if (parent != NULL) {
+		g_return_if_fail (GTK_IS_WINDOW (parent));
+		g_object_ref (parent);
+	}
+
+	if (cancellable != NULL) {
+		g_return_if_fail (G_IS_CANCELLABLE (cancellable));
+		g_object_ref (cancellable);
+	}
+
+	if (default_zone == NULL)
+		default_zone = icaltimezone_get_utc_timezone ();
+
+	context = g_slice_new0 (LoadContext);
+	context->parent = parent;
+	context->cancellable = cancellable;
+	context->source_type = source_type;
+	context->default_zone = default_zone;
+
+	/* Extract authentication details from the ESource before
+	 * spawning the thread, since ESource is not thread-safe. */
+	load_cal_source_get_auth_details (source, context);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (source), callback,
+		user_data, e_load_cal_source_async);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify)
+		load_cal_source_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc) load_cal_source_thread,
+		G_PRIORITY_DEFAULT, context->cancellable);
+
+	g_object_unref (simple);
+}
+
+/**
+ * e_load_cal_source_finish:
+ * @source: an #ESource
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes an asynchronous #ECal open operation started with
+ * e_load_cal_source_async().  If an error occurred, or the user
+ * declined to authenticate, the function will return %NULL and
+ * set @error.
+ *
+ * Returns: a ready-to-use #ECal, or %NULL on error
+ **/
+ECal *
+e_load_cal_source_finish (ESource *source,
+                          GAsyncResult *result,
+                          GError **error)
+{
+	GSimpleAsyncResult *simple;
+	LoadContext *context;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+			result, G_OBJECT (source),
+			e_load_cal_source_async), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+	g_return_val_if_fail (context != NULL, NULL);
+
+	return g_object_ref (context->cal);
+}
diff --git a/calendar/common/authentication.h b/calendar/common/authentication.h
index d48ca3e..11b2dc1 100644
--- a/calendar/common/authentication.h
+++ b/calendar/common/authentication.h
@@ -21,9 +21,10 @@
  *
  */
 
-#ifndef _AUTHENTICATION_H_
-#define _AUTHENTICATION_H_
+#ifndef AUTHENTICATION_H
+#define AUTHENTICATION_H
 
+#include <gtk/gtk.h>
 #include <libedataserver/e-source.h>
 #include <libecal/e-cal.h>
 
@@ -31,4 +32,15 @@ ECal *e_auth_new_cal_from_default (ECalSourceType type);
 ECal *e_auth_new_cal_from_source (ESource *source, ECalSourceType type);
 void e_auth_cal_forget_password (ECal *ecal);
 
-#endif
+void		e_load_cal_source_async		(ESource *source,
+						 ECalSourceType source_type,
+						 icaltimezone *default_zone,
+						 GtkWindow *parent,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+ECal *		e_load_cal_source_finish	(ESource *source,
+						 GAsyncResult *result,
+						 GError **error);
+
+#endif /* AUTHENTICATION_H */
diff --git a/modules/addressbook/e-book-shell-backend.c b/modules/addressbook/e-book-shell-backend.c
index 5036796..aecc93e 100644
--- a/modules/addressbook/e-book-shell-backend.c
+++ b/modules/addressbook/e-book-shell-backend.c
@@ -228,15 +228,13 @@ action_contact_new_cb (GtkAction *action,
 
 	/* This callback is used for both contacts and contact lists. */
 
-	/* Dig out the EBookShellBackend's source list. */
 	shell = e_shell_window_get_shell (shell_window);
 	shell_backend = e_shell_get_backend_by_name (shell, "addressbook");
+
 	g_object_get (shell_backend, "source-list", &source_list, NULL);
 	g_return_if_fail (E_IS_SOURCE_LIST (source_list));
 
 	client = e_shell_get_gconf_client (shell);
-	action_name = gtk_action_get_name (action);
-
 	key = "/apps/evolution/addressbook/display/primary_addressbook";
 	uid = gconf_client_get_string (client, key, NULL);
 
@@ -250,13 +248,14 @@ action_contact_new_cb (GtkAction *action,
 
 	g_return_if_fail (E_IS_SOURCE (source));
 
+	/* Use a callback function appropriate for the action. */
+	action_name = gtk_action_get_name (action);
 	if (strcmp (action_name, "contact-new") == 0)
 		e_load_book_source_async (
 			source, GTK_WINDOW (shell_window),
 			NULL, (GAsyncReadyCallback)
 			book_shell_backend_new_contact_cb,
 			g_object_ref (shell));
-
 	if (strcmp (action_name, "contact-new-list") == 0)
 		e_load_book_source_async (
 			source, GTK_WINDOW (shell_window),
diff --git a/modules/calendar/e-cal-shell-backend.c b/modules/calendar/e-cal-shell-backend.c
index 8861a82..e726263 100644
--- a/modules/calendar/e-cal-shell-backend.c
+++ b/modules/calendar/e-cal-shell-backend.c
@@ -221,20 +221,19 @@ cal_shell_backend_ensure_sources (EShellBackend *shell_backend)
 }
 
 static void
-cal_shell_backend_new_event (ECal *cal,
-                             const GError *error,
+cal_shell_backend_new_event (ESource *source,
+                             GAsyncResult *result,
                              EShell *shell,
                              CompEditorFlags flags,
                              gboolean all_day)
 {
+	ECal *cal;
 	ECalComponent *comp;
 	CompEditor *editor;
 
 	/* XXX Handle errors better. */
-	if (error)
-		return;
-
-	flags |= COMP_EDITOR_NEW_ITEM;
+	cal = e_load_cal_source_finish (source, result, NULL);
+	g_return_if_fail (E_IS_CAL (cal));
 
 	editor = event_editor_new (cal, shell, flags);
 	comp = cal_comp_event_new_with_current_time (cal, all_day);
@@ -244,68 +243,77 @@ cal_shell_backend_new_event (ECal *cal,
 	gtk_window_present (GTK_WINDOW (editor));
 
 	g_object_unref (comp);
+	g_object_unref (cal);
 }
 
 static void
-cal_shell_backend_event_new_cb (ECal *cal,
-                                const GError *error,
+cal_shell_backend_event_new_cb (ESource *source,
+                                GAsyncResult *result,
                                 EShell *shell)
 {
-	CompEditorFlags flags;
+	CompEditorFlags flags = 0;
+	gboolean all_day = FALSE;
 
-	flags = COMP_EDITOR_USER_ORG;
-	cal_shell_backend_new_event (cal, error, shell, flags, FALSE);
+	flags |= COMP_EDITOR_NEW_ITEM;
+	flags |= COMP_EDITOR_USER_ORG;
 
-	g_object_unref (cal);
+	cal_shell_backend_new_event (source, result, shell, flags, all_day);
+
+	g_object_unref (shell);
 }
 
 static void
-cal_shell_backend_event_all_day_new_cb (ECal *cal,
-                                        const GError *error,
+cal_shell_backend_event_all_day_new_cb (ESource *source,
+                                        GAsyncResult *result,
                                         EShell *shell)
 {
-	CompEditorFlags flags;
+	CompEditorFlags flags = 0;
+	gboolean all_day = TRUE;
 
-	flags = COMP_EDITOR_USER_ORG;
-	cal_shell_backend_new_event (cal, error, shell, flags, TRUE);
+	flags |= COMP_EDITOR_NEW_ITEM;
+	flags |= COMP_EDITOR_USER_ORG;
 
-	g_object_unref (cal);
+	cal_shell_backend_new_event (source, result, shell, flags, all_day);
+
+	g_object_unref (shell);
 }
 
 static void
-cal_shell_backend_event_meeting_new_cb (ECal *cal,
-                                        const GError *error,
+cal_shell_backend_event_meeting_new_cb (ESource *source,
+                                        GAsyncResult *result,
                                         EShell *shell)
 {
-	CompEditorFlags flags;
+	CompEditorFlags flags = 0;
+	gboolean all_day = FALSE;
 
-	flags = COMP_EDITOR_USER_ORG | COMP_EDITOR_MEETING;
-	cal_shell_backend_new_event (cal, error, shell, flags, FALSE);
+	flags |= COMP_EDITOR_NEW_ITEM;
+	flags |= COMP_EDITOR_USER_ORG;
+	flags |= COMP_EDITOR_MEETING;
 
-	g_object_unref (cal);
+	cal_shell_backend_new_event (source, result, shell, flags, all_day);
+
+	g_object_unref (shell);
 }
 
 static void
 action_event_new_cb (GtkAction *action,
                      EShellWindow *shell_window)
 {
-	ECal *cal = NULL;
-	ECalSourceType source_type;
-	ESourceList *source_list;
-	EShellSettings *shell_settings;
-	EShellView *shell_view;
 	EShell *shell;
-	const gchar *view_name;
+	EShellView *shell_view;
+	EShellBackend *shell_backend;
+	EShellSettings *shell_settings;
+	ESource *source = NULL;
+	ESourceList *source_list;
+	ECalSourceType source_type;
 	const gchar *action_name;
 	gchar *uid;
 
 	/* With a 'calendar' active shell view pass the new appointment
 	 * request to it, thus the event will inherit selected time from
 	 * the view. */
-	view_name = e_shell_window_get_active_view (shell_window);
-	shell_view = e_shell_window_get_shell_view (shell_window, view_name);
-
-	if (shell_view && g_ascii_strcasecmp (view_name, "calendar") == 0) {
+	shell_view = e_shell_window_peek_shell_view (shell_window, "calendar");
+	if (shell_view != NULL) {
 		EShellContent *shell_content;
 		GnomeCalendar *gcal;
 		GnomeCalendarViewType view_type;
@@ -338,48 +346,50 @@ action_event_new_cb (GtkAction *action,
 
 	shell = e_shell_window_get_shell (shell_window);
 	shell_settings = e_shell_get_shell_settings (shell);
+	shell_backend = e_shell_get_backend_by_name (shell, "calendar");
 
-	if (!e_cal_get_sources (&source_list, source_type, NULL)) {
-		g_warning ("Could not get calendar sources from GConf!");
-		return;
-	}
+	g_object_get (shell_backend, "source-list", &source_list, NULL);
+	g_return_if_fail (E_IS_SOURCE_LIST (source_list));
 
 	uid = e_shell_settings_get_string (
 		shell_settings, "cal-primary-calendar");
 
 	if (uid != NULL) {
-		ESource *source;
-
 		source = e_source_list_peek_source_by_uid (source_list, uid);
-		if (source != NULL)
-			cal = e_auth_new_cal_from_source (source, source_type);
 		g_free (uid);
 	}
 
-	if (cal == NULL)
-		cal = e_auth_new_cal_from_default (source_type);
+	if (source == NULL)
+		source = e_source_list_peek_default_source (source_list);
 
-	g_return_if_fail (cal != NULL);
+	g_return_if_fail (E_IS_SOURCE (source));
 
-	/* Connect the appropriate signal handler. */
+	/* Use a callback function appropriate for the action.
+	 * FIXME Need to obtain a better default time zone. */
 	action_name = gtk_action_get_name (action);
 	if (strcmp (action_name, "event-all-day-new") == 0)
-		g_signal_connect (
-			cal, "cal-opened-ex",
-			G_CALLBACK (cal_shell_backend_event_all_day_new_cb),
-			shell);
+		e_load_cal_source_async (
+			source, source_type, NULL,
+			GTK_WINDOW (shell_window),
+			NULL, (GAsyncReadyCallback)
+			cal_shell_backend_event_all_day_new_cb,
+			g_object_ref (shell));
 	else if (strcmp (action_name, "event-meeting-new") == 0)
-		g_signal_connect (
-			cal, "cal-opened-ex",
-			G_CALLBACK (cal_shell_backend_event_meeting_new_cb),
-			shell);
+		e_load_cal_source_async (
+			source, source_type, NULL,
+			GTK_WINDOW (shell_window),
+			NULL, (GAsyncReadyCallback)
+			cal_shell_backend_event_meeting_new_cb,
+			g_object_ref (shell));
 	else
-		g_signal_connect (
-			cal, "cal-opened-ex",
-			G_CALLBACK (cal_shell_backend_event_new_cb),
-			shell);
+		e_load_cal_source_async (
+			source, source_type, NULL,
+			GTK_WINDOW (shell_window),
+			NULL, (GAsyncReadyCallback)
+			cal_shell_backend_event_new_cb,
+			g_object_ref (shell));
 
-	e_cal_open_async (cal, FALSE);
+	g_object_unref (source_list);
 }
 
 static void
diff --git a/modules/calendar/e-cal-shell-sidebar.c b/modules/calendar/e-cal-shell-sidebar.c
index 1cd4ba9..a819d88 100644
--- a/modules/calendar/e-cal-shell-sidebar.c
+++ b/modules/calendar/e-cal-shell-sidebar.c
@@ -59,6 +59,8 @@ struct _ECalShellSidebarPrivate {
 	 * sometime later we update our default-client property
 	 * which is bound by an EBinding to ECalModel. */
 	ECal *default_client;
+
+	GCancellable *loading_default_client;
 };
 
 enum {
@@ -241,104 +243,106 @@ cal_shell_sidebar_client_opened_cb (ECalShellSidebar *cal_shell_sidebar,
 }
 
 static void
-cal_shell_sidebar_default_opened_cb (ECalShellSidebar *cal_shell_sidebar,
-                                     const GError *error,
-                                     ECal *client)
+cal_shell_sidebar_default_loaded_cb (ESource *source,
+                                     GAsyncResult *result,
+                                     EShellSidebar *shell_sidebar)
 {
+	ECalShellSidebarPrivate *priv;
+	EShellWindow *shell_window;
 	EShellView *shell_view;
-	EShellSidebar *shell_sidebar;
+	ECal *client;
+	GError *error = NULL;
+
+	priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (shell_sidebar);
 
-	shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+	shell_window = e_shell_view_get_shell_window (shell_view);
 
-	if (g_error_matches (error, E_CALENDAR_ERROR,
-		E_CALENDAR_STATUS_AUTHENTICATION_FAILED) ||
-	    g_error_matches (error, E_CALENDAR_ERROR,
-		E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED))
-		e_auth_cal_forget_password (client);
+	client = e_load_cal_source_finish (source, result, &error);
 
-	/* Handle errors. */
-	switch (error ? error->code : E_CALENDAR_STATUS_OK) {
-		case E_CALENDAR_STATUS_OK:
-			break;
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		g_error_free (error);
+		goto exit;
 
-		case E_CALENDAR_STATUS_AUTHENTICATION_FAILED:
-			e_cal_open_async (client, FALSE);
-			return;
+	} else if (error != NULL) {
+		e_alert_run_dialog_for_args (
+			GTK_WINDOW (shell_window),
+			"calendar:failed-open-calendar",
+			error->message, NULL);
+		g_error_free (error);
+		goto exit;
+	}
 
-		case E_CALENDAR_STATUS_BUSY:
-			return;
+	g_return_if_fail (E_IS_CAL (client));
 
-		default:
-			e_alert_run_dialog_for_args (
-				GTK_WINDOW (e_shell_view_get_shell_window (shell_view)),
-				"calendar:failed-open-calendar",
-				error->message, NULL);
+	if (priv->default_client != NULL)
+		g_object_unref (priv->default_client);
 
-			e_cal_shell_sidebar_remove_source (
-				cal_shell_sidebar,
-				e_cal_get_source (client));
-			return;
-	}
+	priv->default_client = client;
 
-	g_assert (error == NULL);
+	g_object_notify (G_OBJECT (shell_sidebar), "default-client");
 
-	g_signal_handlers_disconnect_matched (
-		client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
-		cal_shell_sidebar_default_opened_cb, NULL);
-
-	g_object_notify (G_OBJECT (cal_shell_sidebar), "default-client");
+exit:
+	g_object_unref (shell_sidebar);
 }
 
 static void
 cal_shell_sidebar_set_default (ECalShellSidebar *cal_shell_sidebar,
                                ESource *source)
 {
+	ECalShellSidebarPrivate *priv;
 	EShellView *shell_view;
+	EShellWindow *shell_window;
 	EShellContent *shell_content;
 	EShellSidebar *shell_sidebar;
 	ECalShellContent *cal_shell_content;
 	ECalSourceType source_type;
-	GHashTable *client_table;
 	ECalModel *model;
 	ECal *client;
 	icaltimezone *timezone;
 	const gchar *uid;
 
+	priv = cal_shell_sidebar->priv;
 	source_type = E_CAL_SOURCE_TYPE_EVENT;
-	client_table = cal_shell_sidebar->priv->client_table;
-
-	uid = e_source_peek_uid (source);
-	client = g_hash_table_lookup (client_table, uid);
-
-	if (cal_shell_sidebar->priv->default_client != NULL)
-		g_object_unref (cal_shell_sidebar->priv->default_client);
-
-	if (client != NULL)
-		g_object_ref (client);
-	else
-		client = e_auth_new_cal_from_source (source, source_type);
-
-	cal_shell_sidebar->priv->default_client = client;
-	g_return_if_fail (client != NULL);
-
-	g_signal_connect_swapped (
-		client, "cal-opened-ex",
-		G_CALLBACK (cal_shell_sidebar_default_opened_cb),
-		cal_shell_sidebar);
 
 	/* FIXME Sidebar should not be accessing the EShellContent.
 	 *       This probably needs to be moved to ECalShellView. */
 	shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar);
 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 	shell_content = e_shell_view_get_shell_content (shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
 
 	cal_shell_content = E_CAL_SHELL_CONTENT (shell_content);
 	model = e_cal_shell_content_get_model (cal_shell_content);
 	timezone = e_cal_model_get_timezone (model);
 
-	e_cal_set_default_timezone (client, timezone, NULL);
-	e_cal_open_async (client, FALSE);
+	/* Cancel any unfinished previous request. */
+	if (priv->loading_default_client != NULL) {
+		g_cancellable_cancel (priv->loading_default_client);
+		g_object_unref (priv->loading_default_client);
+		priv->loading_default_client = NULL;
+	}
+
+	uid = e_source_peek_uid (source);
+	client = g_hash_table_lookup (priv->client_table, uid);
+
+	/* If we already have an open connection for
+	 * this UID, we can finish immediately. */
+	if (client != NULL) {
+		if (priv->default_client != NULL)
+			g_object_unref (priv->default_client);
+		priv->default_client = g_object_ref (client);
+		g_object_notify (G_OBJECT (shell_sidebar), "default-client");
+		return;
+	}
+
+	priv->loading_default_client = g_cancellable_new ();
+
+	e_load_cal_source_async (
+		source, source_type, timezone,
+		GTK_WINDOW (shell_window), priv->loading_default_client,
+		(GAsyncReadyCallback) cal_shell_sidebar_default_loaded_cb,
+		g_object_ref (shell_sidebar));
 }
 
 static void
@@ -572,6 +576,12 @@ cal_shell_sidebar_dispose (GObject *object)
 		priv->default_client = NULL;
 	}
 
+	if (priv->loading_default_client != NULL) {
+		g_cancellable_cancel (priv->loading_default_client);
+		g_object_unref (priv->loading_default_client);
+		priv->loading_default_client = NULL;
+	}
+
 	g_hash_table_remove_all (priv->client_table);
 
 	/* Chain up to parent's dispose() method. */
diff --git a/modules/calendar/e-memo-shell-backend.c b/modules/calendar/e-memo-shell-backend.c
index 8f5bf40..d54c32f 100644
--- a/modules/calendar/e-memo-shell-backend.c
+++ b/modules/calendar/e-memo-shell-backend.c
@@ -153,19 +153,18 @@ memo_shell_backend_ensure_sources (EShellBackend *shell_backend)
 }
 
 static void
-memo_shell_backend_memo_new_cb (ECal *cal,
-                                const GError *error,
-                                EShell *shell)
+memo_shell_backend_new_memo (ESource *source,
+                             GAsyncResult *result,
+                             EShell *shell,
+                             CompEditorFlags flags)
 {
+	ECal *cal;
 	ECalComponent *comp;
 	CompEditor *editor;
-	CompEditorFlags flags = 0;
 
 	/* XXX Handle errors better. */
-	if (error)
-		return;
-
-	flags |= COMP_EDITOR_NEW_ITEM;
+	cal = e_load_cal_source_finish (source, result, NULL);
+	g_return_if_fail (E_IS_CAL (cal));
 
 	comp = cal_comp_memo_new_with_defaults (cal);
 	cal_comp_update_time_by_active_window (comp, shell);
@@ -179,42 +178,45 @@ memo_shell_backend_memo_new_cb (ECal *cal,
 }
 
 static void
-memo_shell_backend_memo_shared_new_cb (ECal *cal,
-                                       const GError *error,
-                                       EShell *shell)
+memo_shell_backend_memo_new_cb (ESource *source,
+                                GAsyncResult *result,
+                                EShell *shell)
 {
-	ECalComponent *comp;
-	CompEditor *editor;
 	CompEditorFlags flags = 0;
 
-	/* XXX Handle errors better. */
-	if (error)
-		return;
+	flags |= COMP_EDITOR_NEW_ITEM;
+
+	memo_shell_backend_new_memo (source, result, shell, flags);
+
+	g_object_unref (shell);
+}
+
+static void
+memo_shell_backend_memo_shared_new_cb (ESource *source,
+                                       GAsyncResult *result,
+                                       EShell *shell)
+{
+	CompEditorFlags flags = 0;
 
 	flags |= COMP_EDITOR_NEW_ITEM;
 	flags |= COMP_EDITOR_IS_SHARED;
 	flags |= COMP_EDITOR_USER_ORG;
 
-	comp = cal_comp_memo_new_with_defaults (cal);
-	cal_comp_update_time_by_active_window (comp, shell);
-	editor = memo_editor_new (cal, shell, flags);
-	comp_editor_edit_comp (editor, comp);
-
-	gtk_window_present (GTK_WINDOW (editor));
+	memo_shell_backend_new_memo (source, result, shell, flags);
 
-	g_object_unref (comp);
-	g_object_unref (cal);
+	g_object_unref (shell);
 }
 
 static void
 action_memo_new_cb (GtkAction *action,
                     EShellWindow *shell_window)
 {
-	ECal *cal = NULL;
-	ECalSourceType source_type;
-	ESourceList *source_list;
-	EShellSettings *shell_settings;
 	EShell *shell;
+	EShellBackend *shell_backend;
+	EShellSettings *shell_settings;
+	ESource *source = NULL;
+	ESourceList *source_list;
+	ECalSourceType source_type;
 	const gchar *action_name;
 	gchar *uid;
 
@@ -224,43 +226,43 @@ action_memo_new_cb (GtkAction *action,
 
 	shell = e_shell_window_get_shell (shell_window);
 	shell_settings = e_shell_get_shell_settings (shell);
+	shell_backend = e_shell_get_backend_by_name (shell, "memos");
 
-	if (!e_cal_get_sources (&source_list, source_type, NULL)) {
-		g_warning ("Could not get memo sources from GConf!");
-		return;
-	}
+	g_object_get (shell_backend, "source-list", &source_list, NULL);
+	g_return_if_fail (E_IS_SOURCE_LIST (source_list));
 
 	uid = e_shell_settings_get_string (
 		shell_settings, "cal-primary-memo-list");
 
 	if (uid != NULL) {
-		ESource *source;
-
 		source = e_source_list_peek_source_by_uid (source_list, uid);
-		if (source != NULL)
-			cal = e_auth_new_cal_from_source (source, source_type);
 		g_free (uid);
 	}
 
-	if (cal == NULL)
-		cal = e_auth_new_cal_from_default (source_type);
+	if (source == NULL)
+		source = e_source_list_peek_default_source (source_list);
 
-	g_return_if_fail (cal != NULL);
+	g_return_if_fail (E_IS_SOURCE (source));
 
-	/* Connect the appropriate signal handler. */
+	/* Use a callback function appropriate for the action.
+	 * FIXME Need to obtain a better default time zone. */
 	action_name = gtk_action_get_name (action);
-	if (strcmp (action_name, "memo-shared-new") == 0)
-		g_signal_connect (
-			cal, "cal-opened-ex",
-			G_CALLBACK (memo_shell_backend_memo_shared_new_cb),
-			shell);
+	if (g_strcmp0 (action_name, "memo-shared-new") == 0)
+		e_load_cal_source_async (
+			source, source_type, NULL,
+			GTK_WINDOW (shell_window),
+			NULL, (GAsyncReadyCallback)
+			memo_shell_backend_memo_shared_new_cb,
+			g_object_ref (shell));
 	else
-		g_signal_connect (
-			cal, "cal-opened-ex",
-			G_CALLBACK (memo_shell_backend_memo_new_cb),
-			shell);
+		e_load_cal_source_async (
+			source, source_type, NULL,
+			GTK_WINDOW (shell_window),
+			NULL, (GAsyncReadyCallback)
+			memo_shell_backend_memo_new_cb,
+			g_object_ref (shell));
 
-	e_cal_open_async (cal, FALSE);
+	g_object_unref (source_list);
 }
 
 static void
diff --git a/modules/calendar/e-memo-shell-sidebar.c b/modules/calendar/e-memo-shell-sidebar.c
index 7b4b8f7..f463394 100644
--- a/modules/calendar/e-memo-shell-sidebar.c
+++ b/modules/calendar/e-memo-shell-sidebar.c
@@ -54,6 +54,8 @@ struct _EMemoShellSidebarPrivate {
 	 * sometime later we update our default-client property
 	 * which is bound by an EBinding to ECalModel. */
 	ECal *default_client;
+
+	GCancellable *loading_default_client;
 };
 
 enum {
@@ -235,104 +237,106 @@ memo_shell_sidebar_client_opened_cb (EMemoShellSidebar *memo_shell_sidebar,
 }
 
 static void
-memo_shell_sidebar_default_opened_cb (EMemoShellSidebar *memo_shell_sidebar,
-                                      const GError *error,
-                                      ECal *client)
+memo_shell_sidebar_default_loaded_cb (ESource *source,
+                                      GAsyncResult *result,
+                                      EShellSidebar *shell_sidebar)
 {
+	EMemoShellSidebarPrivate *priv;
+	EShellWindow *shell_window;
 	EShellView *shell_view;
-	EShellSidebar *shell_sidebar;
+	ECal *client;
+	GError *error = NULL;
+
+	priv = E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (shell_sidebar);
 
-	shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar);
 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+	shell_window = e_shell_view_get_shell_window (shell_view);
 
-	if (g_error_matches (error, E_CALENDAR_ERROR,
-		E_CALENDAR_STATUS_AUTHENTICATION_FAILED) ||
-	    g_error_matches (error, E_CALENDAR_ERROR,
-		E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED))
-		e_auth_cal_forget_password (client);
+	client = e_load_cal_source_finish (source, result, &error);
 
-	/* Handle errors. */
-	switch (error ? error->code : E_CALENDAR_STATUS_OK) {
-		case E_CALENDAR_STATUS_OK:
-			break;
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		g_error_free (error);
+		goto exit;
 
-		case E_CALENDAR_STATUS_AUTHENTICATION_FAILED:
-			e_cal_open_async (client, FALSE);
-			return;
+	} else if (error != NULL) {
+		e_alert_run_dialog_for_args (
+			GTK_WINDOW (shell_window),
+			"calendar:failed-open-memos",
+			error->message, NULL);
+		g_error_free (error);
+		goto exit;
+	}
 
-		case E_CALENDAR_STATUS_BUSY:
-			return;
+	g_return_if_fail (E_IS_CAL (client));
 
-		default:
-			e_alert_run_dialog_for_args (
-				GTK_WINDOW (e_shell_view_get_shell_window (shell_view)),
-				"calendar:failed-open-memos",
-				error->message, NULL);
+	if (priv->default_client != NULL)
+		g_object_unref (priv->default_client);
 
-			e_memo_shell_sidebar_remove_source (
-				memo_shell_sidebar,
-				e_cal_get_source (client));
-			return;
-	}
+	priv->default_client = client;
 
-	g_assert (error == NULL);
+	g_object_notify (G_OBJECT (shell_sidebar), "default-client");
 
-	g_signal_handlers_disconnect_matched (
-		client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
-		memo_shell_sidebar_default_opened_cb, NULL);
-
-	g_object_notify (G_OBJECT (memo_shell_sidebar), "default-client");
+exit:
+	g_object_unref (shell_sidebar);
 }
 
 static void
 memo_shell_sidebar_set_default (EMemoShellSidebar *memo_shell_sidebar,
                                 ESource *source)
 {
+	EMemoShellSidebarPrivate *priv;
 	EShellView *shell_view;
+	EShellWindow *shell_window;
 	EShellContent *shell_content;
 	EShellSidebar *shell_sidebar;
 	EMemoShellContent *memo_shell_content;
 	ECalSourceType source_type;
-	GHashTable *client_table;
 	ECalModel *model;
 	ECal *client;
 	icaltimezone *timezone;
 	const gchar *uid;
 
+	priv = memo_shell_sidebar->priv;
 	source_type = E_CAL_SOURCE_TYPE_JOURNAL;
-	client_table = memo_shell_sidebar->priv->client_table;
-
-	uid = e_source_peek_uid (source);
-	client = g_hash_table_lookup (client_table, uid);
-
-	if (memo_shell_sidebar->priv->default_client != NULL)
-		g_object_unref (memo_shell_sidebar->priv->default_client);
-
-	if (client != NULL)
-		g_object_ref (client);
-	else
-		client = e_auth_new_cal_from_source (source, source_type);
-
-	memo_shell_sidebar->priv->default_client = client;
-	g_return_if_fail (client != NULL);
-
-	g_signal_connect_swapped (
-		client, "cal-opened-ex",
-		G_CALLBACK (memo_shell_sidebar_default_opened_cb),
-		memo_shell_sidebar);
 
 	/* FIXME Sidebar should not be accessing the EShellContent.
 	 *       This probably needs to be moved to EMemoShellView. */
 	shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar);
 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 	shell_content = e_shell_view_get_shell_content (shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
 
 	memo_shell_content = E_MEMO_SHELL_CONTENT (shell_content);
 	model = e_memo_shell_content_get_memo_model (memo_shell_content);
 	timezone = e_cal_model_get_timezone (model);
 
-	e_cal_set_default_timezone (client, timezone, NULL);
-	e_cal_open_async (client, FALSE);
+	/* Cancel any unfinished previous request. */
+	if (priv->loading_default_client != NULL) {
+		g_cancellable_cancel (priv->loading_default_client);
+		g_object_unref (priv->loading_default_client);
+		priv->loading_default_client = NULL;
+	}
+
+	uid = e_source_peek_uid (source);
+	client = g_hash_table_lookup (priv->client_table, uid);
+
+	/* If we already have an open connection for
+	 * this UID, we can finish immediately. */
+	if (client != NULL) {
+		if (priv->default_client != NULL)
+			g_object_unref (priv->default_client);
+		priv->default_client = g_object_ref (client);
+		g_object_notify (G_OBJECT (shell_sidebar), "default-client");
+		return;
+	}
+
+	priv->loading_default_client = g_cancellable_new ();
+
+	e_load_cal_source_async (
+		source, source_type, timezone,
+		GTK_WINDOW (shell_window), priv->loading_default_client,
+		(GAsyncReadyCallback) memo_shell_sidebar_default_loaded_cb,
+		g_object_ref (shell_sidebar));
 }
 
 static void
@@ -538,6 +542,12 @@ memo_shell_sidebar_dispose (GObject *object)
 		priv->default_client = NULL;
 	}
 
+	if (priv->loading_default_client != NULL) {
+		g_cancellable_cancel (priv->loading_default_client);
+		g_object_unref (priv->loading_default_client);
+		priv->loading_default_client = NULL;
+	}
+
 	g_hash_table_remove_all (priv->client_table);
 
 	/* Chain up to parent's dispose() method. */
diff --git a/modules/calendar/e-task-shell-backend.c b/modules/calendar/e-task-shell-backend.c
index cdd4dcc..be085af 100644
--- a/modules/calendar/e-task-shell-backend.c
+++ b/modules/calendar/e-task-shell-backend.c
@@ -153,19 +153,18 @@ task_shell_backend_ensure_sources (EShellBackend *shell_backend)
 }
 
 static void
-task_shell_backend_task_new_cb (ECal *cal,
-                                const GError *error,
-                                EShell *shell)
+task_shell_backend_new_task (ESource *source,
+                             GAsyncResult *result,
+                             EShell *shell,
+                             CompEditorFlags flags)
 {
+	ECal *cal;
 	ECalComponent *comp;
 	CompEditor *editor;
-	CompEditorFlags flags = 0;
 
 	/* XXX Handle errors better. */
-	if (error)
-		return;
-
-	flags |= COMP_EDITOR_NEW_ITEM;
+	cal = e_load_cal_source_finish (source, result, NULL);
+	g_return_if_fail (E_IS_CAL (cal));
 
 	editor = task_editor_new (cal, shell, flags);
 	comp = cal_comp_task_new_with_defaults (cal);
@@ -178,41 +177,45 @@ task_shell_backend_task_new_cb (ECal *cal,
 }
 
 static void
-task_shell_backend_task_assigned_new_cb (ECal *cal,
-                                         const GError *error,
-                                         EShell *shell)
+task_shell_backend_task_new_cb (ESource *source,
+                                GAsyncResult *result,
+                                EShell *shell)
 {
-	ECalComponent *comp;
-	CompEditor *editor;
 	CompEditorFlags flags = 0;
 
-	/* XXX Handle errors better. */
-	if (error)
-		return;
+	flags |= COMP_EDITOR_NEW_ITEM;
+
+	task_shell_backend_new_task (source, result, shell, flags);
+
+	g_object_unref (shell);
+}
+
+static void
+task_shell_backend_task_assigned_new_cb (ESource *source,
+                                         GAsyncResult *result,
+                                         EShell *shell)
+{
+	CompEditorFlags flags = 0;
 
 	flags |= COMP_EDITOR_NEW_ITEM;
 	flags |= COMP_EDITOR_IS_ASSIGNED;
 	flags |= COMP_EDITOR_USER_ORG;
 
-	editor = task_editor_new (cal, shell, flags);
-	comp = cal_comp_task_new_with_defaults (cal);
-	comp_editor_edit_comp (editor, comp);
+	task_shell_backend_new_task (source, result, shell, flags);
 
-	gtk_window_present (GTK_WINDOW (editor));
-
-	g_object_unref (comp);
-	g_object_unref (cal);
+	g_object_unref (shell);
 }
 
 static void
 action_task_new_cb (GtkAction *action,
                     EShellWindow *shell_window)
 {
-	ECal *cal = NULL;
-	ECalSourceType source_type;
-	ESourceList *source_list;
-	EShellSettings *shell_settings;
 	EShell *shell;
+	EShellBackend *shell_backend;
+	EShellSettings *shell_settings;
+	ESource *source = NULL;
+	ESourceList *source_list;
+	ECalSourceType source_type;
 	const gchar *action_name;
 	gchar *uid;
 
@@ -222,43 +225,43 @@ action_task_new_cb (GtkAction *action,
 
 	shell = e_shell_window_get_shell (shell_window);
 	shell_settings = e_shell_get_shell_settings (shell);
+	shell_backend = e_shell_get_backend_by_name (shell, "tasks");
 
-	if (!e_cal_get_sources (&source_list, source_type, NULL)) {
-		g_warning ("Could not get task sources from GConf!");
-		return;
-	}
+	g_object_get (shell_backend, "source-list", &source_list, NULL);
+	g_return_if_fail (E_IS_SOURCE_LIST (source_list));
 
 	uid = e_shell_settings_get_string (
 		shell_settings, "cal-primary-task-list");
 
 	if (uid != NULL) {
-		ESource *source;
-
 		source = e_source_list_peek_source_by_uid (source_list, uid);
-		if (source != NULL)
-			cal = e_auth_new_cal_from_source (source, source_type);
 		g_free (uid);
 	}
 
-	if (cal == NULL)
-		cal = e_auth_new_cal_from_default (source_type);
+	if (source == NULL)
+		source = e_source_list_peek_default_source (source_list);
 
-	g_return_if_fail (cal != NULL);
+	g_return_if_fail (E_IS_SOURCE (source));
 
-	/* Connect the appropriate signal handler. */
+	/* Use a callback function appropriate for the action.
+	 * FIXME Need to obtain a better default time zone. */
 	action_name = gtk_action_get_name (action);
 	if (strcmp (action_name, "task-assigned-new") == 0)
-		g_signal_connect (
-			cal, "cal-opened-ex",
-			G_CALLBACK (task_shell_backend_task_assigned_new_cb),
-			shell);
+		e_load_cal_source_async (
+			source, source_type, NULL,
+			GTK_WINDOW (shell_window),
+			NULL, (GAsyncReadyCallback)
+			task_shell_backend_task_assigned_new_cb,
+			g_object_ref (shell));
 	else
-		g_signal_connect (
-			cal, "cal-opened-ex",
-			G_CALLBACK (task_shell_backend_task_new_cb),
-			shell);
+		e_load_cal_source_async (
+			source, source_type, NULL,
+			GTK_WINDOW (shell_window),
+			NULL, (GAsyncReadyCallback)
+			task_shell_backend_task_new_cb,
+			g_object_ref (shell));
 
-	e_cal_open_async (cal, FALSE);
+	g_object_unref (source_list);
 }
 
 static void
diff --git a/modules/calendar/e-task-shell-sidebar.c b/modules/calendar/e-task-shell-sidebar.c
index 8613b4e..b50e463 100644
--- a/modules/calendar/e-task-shell-sidebar.c
+++ b/modules/calendar/e-task-shell-sidebar.c
@@ -54,6 +54,8 @@ struct _ETaskShellSidebarPrivate {
 	 * sometime later we update our default-client property
 	 * which is bound by an EBinding to ECalModel. */
 	ECal *default_client;
+
+	GCancellable *loading_default_client;
 };
 
 enum {
@@ -235,104 +237,106 @@ task_shell_sidebar_client_opened_cb (ETaskShellSidebar *task_shell_sidebar,
 }
 
 static void
-task_shell_sidebar_default_opened_cb (ETaskShellSidebar *task_shell_sidebar,
-                                      const GError *error,
-                                      ECal *client)
+task_shell_sidebar_default_loaded_cb (ESource *source,
+                                      GAsyncResult *result,
+                                      EShellSidebar *shell_sidebar)
 {
+	ETaskShellSidebarPrivate *priv;
+	EShellWindow *shell_window;
 	EShellView *shell_view;
-	EShellSidebar *shell_sidebar;
+	ECal *client;
+	GError *error = NULL;
+
+	priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (shell_sidebar);
 
-	shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar);
 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
+	shell_window = e_shell_view_get_shell_window (shell_view);
 
-	if (g_error_matches (error, E_CALENDAR_ERROR,
-		E_CALENDAR_STATUS_AUTHENTICATION_FAILED) ||
-	    g_error_matches (error, E_CALENDAR_ERROR,
-		E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED))
-		e_auth_cal_forget_password (client);
+	client = e_load_cal_source_finish (source, result, &error);
 
-	/* Handle errors. */
-	switch (error ? error->code : E_CALENDAR_STATUS_OK) {
-		case E_CALENDAR_STATUS_OK:
-			break;
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		g_error_free (error);
+		goto exit;
 
-		case E_CALENDAR_STATUS_AUTHENTICATION_FAILED:
-			e_cal_open_async (client, FALSE);
-			return;
+	} else if (error != NULL) {
+		e_alert_run_dialog_for_args (
+			GTK_WINDOW (shell_window),
+			"calendar:failed-open-tasks",
+			error->message, NULL);
+		g_error_free (error);
+		goto exit;
+	}
 
-		case E_CALENDAR_STATUS_BUSY:
-			return;
+	g_return_if_fail (E_IS_CAL (client));
 
-		default:
-			e_alert_run_dialog_for_args (
-				GTK_WINDOW (e_shell_view_get_shell_window (shell_view)),
-				"calendar:failed-open-tasks",
-				error->message, NULL);
+	if (priv->default_client != NULL)
+		g_object_unref (priv->default_client);
 
-			e_task_shell_sidebar_remove_source (
-				task_shell_sidebar,
-				e_cal_get_source (client));
-			return;
-	}
+	priv->default_client = client;
 
-	g_assert (error == NULL);
+	g_object_notify (G_OBJECT (shell_sidebar), "default-client");
 
-	g_signal_handlers_disconnect_matched (
-		client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
-		task_shell_sidebar_default_opened_cb, NULL);
-
-	g_object_notify (G_OBJECT (task_shell_sidebar), "default-client");
+exit:
+	g_object_unref (shell_sidebar);
 }
 
 static void
 task_shell_sidebar_set_default (ETaskShellSidebar *task_shell_sidebar,
                                 ESource *source)
 {
+	ETaskShellSidebarPrivate *priv;
 	EShellView *shell_view;
+	EShellWindow *shell_window;
 	EShellContent *shell_content;
 	EShellSidebar *shell_sidebar;
 	ETaskShellContent *task_shell_content;
 	ECalSourceType source_type;
-	GHashTable *client_table;
 	ECalModel *model;
 	ECal *client;
 	icaltimezone *timezone;
 	const gchar *uid;
 
+	priv = task_shell_sidebar->priv;
 	source_type = E_CAL_SOURCE_TYPE_TODO;
-	client_table = task_shell_sidebar->priv->client_table;
-
-	uid = e_source_peek_uid (source);
-	client = g_hash_table_lookup (client_table, uid);
-
-	if (task_shell_sidebar->priv->default_client != NULL)
-		g_object_unref (task_shell_sidebar->priv->default_client);
-
-	if (client != NULL)
-		g_object_ref (client);
-	else
-		client = e_auth_new_cal_from_source (source, source_type);
-
-	task_shell_sidebar->priv->default_client = client;
-	g_return_if_fail (client != NULL);
-
-	g_signal_connect_swapped (
-		client, "cal-opened-ex",
-		G_CALLBACK (task_shell_sidebar_default_opened_cb),
-		task_shell_sidebar);
 
 	/* FIXME Sidebar should not be accessing the EShellContent.
 	 *       This probably needs to be moved to ETaskShellView. */
 	shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar);
 	shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
 	shell_content = e_shell_view_get_shell_content (shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
 
 	task_shell_content = E_TASK_SHELL_CONTENT (shell_content);
 	model = e_task_shell_content_get_task_model (task_shell_content);
 	timezone = e_cal_model_get_timezone (model);
 
-	e_cal_set_default_timezone (client, timezone, NULL);
-	e_cal_open_async (client, FALSE);
+	/* Cancel any unfinished previous request. */
+	if (priv->loading_default_client != NULL) {
+		g_cancellable_cancel (priv->loading_default_client);
+		g_object_unref (priv->loading_default_client);
+		priv->loading_default_client = NULL;
+	}
+
+	uid = e_source_peek_uid (source);
+	client = g_hash_table_lookup (priv->client_table, uid);
+
+	/* If we already have an open connection for
+	 * this UID, we can finish immediately. */
+	if (client != NULL) {
+		if (priv->default_client != NULL)
+			g_object_unref (priv->default_client);
+		priv->default_client = g_object_ref (client);
+		g_object_notify (G_OBJECT (shell_sidebar), "default-client");
+		return;
+	}
+
+	priv->loading_default_client = g_cancellable_new ();
+
+	e_load_cal_source_async (
+		source, source_type, timezone,
+		GTK_WINDOW (shell_window), priv->loading_default_client,
+		(GAsyncReadyCallback) task_shell_sidebar_default_loaded_cb,
+		g_object_ref (shell_sidebar));
 }
 
 static void
@@ -538,6 +542,12 @@ task_shell_sidebar_dispose (GObject *object)
 		priv->default_client = NULL;
 	}
 
+	if (priv->loading_default_client != NULL) {
+		g_cancellable_cancel (priv->loading_default_client);
+		g_object_unref (priv->loading_default_client);
+		priv->loading_default_client = NULL;
+	}
+
 	g_hash_table_remove_all (priv->client_table);
 
 	/* Chain up to parent's dispose() method. */



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