[evolution-data-server] Rewrite e_load_book_source_async().



commit 7494d975b37e250f78964b11a768dfea06c6f780
Author: Matthew Barnes <mbarnes redhat com>
Date:   Wed Aug 18 07:08:06 2010 -0400

    Rewrite e_load_book_source_async().
    
    This is a late libedataserverui API break, but so be it.
    
    Rewrite e_load_book_source_async() to use a GIO-style asynchronous
    pattern, and add a corresponding e_load_book_source_finish().
    
    The API is fairly obvious:
    
       void     e_load_book_source_async   (ESource *source,
                                            GtkWindow *parent,
                                            GCancellable *cancellable,
                                            GAsyncReadyCallback callback,
                                            gpointer user_data);
    
       EBook *  e_load_book_source_finish  (ESource *source,
                                            GAsyncResult *result,
                                            GError **error);
    
    This also eliminates the thread for loading EBooks from ENameSelector.

 configure.ac                                       |    2 +-
 .../libedataserverui/libedataserverui-sections.txt |    1 +
 .../libedataserverui/tmpl/e-book-auth-util.sgml    |   14 +-
 libedataserverui/e-book-auth-util.c                |  359 ++++++++++++++++++--
 libedataserverui/e-book-auth-util.h                |   20 +-
 libedataserverui/e-name-selector-dialog.c          |   84 ++++--
 libedataserverui/e-name-selector-entry.c           |   47 +++-
 libedataserverui/e-name-selector.c                 |  179 ++++++----
 8 files changed, 575 insertions(+), 131 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 180468e..0961866 100644
--- a/configure.ac
+++ b/configure.ac
@@ -85,7 +85,7 @@ LIBEDATASERVER_CURRENT=14
 LIBEDATASERVER_REVISION=0
 LIBEDATASERVER_AGE=0
 
-LIBEDATASERVERUI_CURRENT=10
+LIBEDATASERVERUI_CURRENT=11
 LIBEDATASERVERUI_REVISION=0
 LIBEDATASERVERUI_AGE=0
 
diff --git a/docs/reference/libedataserverui/libedataserverui-sections.txt b/docs/reference/libedataserverui/libedataserverui-sections.txt
index 6e918cf..f089ce3 100644
--- a/docs/reference/libedataserverui/libedataserverui-sections.txt
+++ b/docs/reference/libedataserverui/libedataserverui-sections.txt
@@ -2,6 +2,7 @@
 <FILE>e-book-auth-util</FILE>
 e_load_book_source
 e_load_book_source_async
+e_load_book_source_finish
 </SECTION>
 
 <SECTION>
diff --git a/docs/reference/libedataserverui/tmpl/e-book-auth-util.sgml b/docs/reference/libedataserverui/tmpl/e-book-auth-util.sgml
index 5eda8ea..38dce82 100644
--- a/docs/reference/libedataserverui/tmpl/e-book-auth-util.sgml
+++ b/docs/reference/libedataserverui/tmpl/e-book-auth-util.sgml
@@ -37,8 +37,20 @@ e-book-auth-util
 </para>
 
 @source: 
- open_func: 
+ parent: 
+ cancellable: 
+ callback: 
 @user_data: 
+
+
+<!-- ##### FUNCTION e_load_book_source_finish ##### -->
+<para>
+
+</para>
+
+ source: 
+ result: 
+ error: 
 @Returns: 
 
 
diff --git a/libedataserverui/e-book-auth-util.c b/libedataserverui/e-book-auth-util.c
index db794a4..25b380b 100644
--- a/libedataserverui/e-book-auth-util.c
+++ b/libedataserverui/e-book-auth-util.c
@@ -318,41 +318,358 @@ e_load_book_source (ESource *source, EBookCallback open_func, gpointer user_data
 }
 #endif
 
+typedef struct {
+	EBook *book;
+	GtkWindow *parent;
+	GCancellable *cancellable;
+
+	gboolean anonymous_alert;
+
+	/* Authentication Details */
+	gchar *auth_uri;
+	gchar *auth_method;
+	gchar *auth_username;
+	gchar *auth_component;
+	gboolean auth_remember;
+} LoadContext;
+
+static void
+load_book_source_context_free (LoadContext *context)
+{
+	if (context->book != NULL)
+		g_object_unref (context->book);
+
+	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_method);
+	g_free (context->auth_username);
+	g_free (context->auth_component);
+
+	g_slice_free (LoadContext, context);
+}
+
+static void
+load_book_source_get_auth_details (ESource *source,
+                                   LoadContext *context)
+{
+	const gchar *property;
+	gchar *uri;
+
+	/* auth_method */
+
+	property = e_source_get_property (source, "auth");
+
+	if (property == NULL || strcmp (property, "none") == 0)
+		return;
+
+	context->auth_method = g_strdup (property);
+
+	/* auth_uri */
+
+	uri = e_source_get_uri (source);
+	context->auth_uri = remove_parameters_from_uri (uri);
+	g_free (uri);
+
+	/* auth_username */
+
+	if (g_strcmp0 (context->auth_method, "ldap/simple-binddn") == 0) {
+		property = e_source_get_property (source, "binddn");
+
+	} else if (g_strcmp0 (context->auth_method, "plain/password") == 0) {
+		property = e_source_get_property (source, "user");
+		if (property == NULL)
+			property = e_source_get_property (source, "username");
+
+	} else
+		property = e_source_get_property (source, "email_addr");
+
+	if (property == NULL)
+		property = "";
+
+	context->auth_username = g_strdup (property);
+
+	/* auth_component */
+
+	property = e_source_get_property (source, "auth-domain");
+
+	if (property == NULL)
+		property = "Addressbook";
+
+	context->auth_component = g_strdup (property);
+
+	/* auth_remember */
+
+	property = e_source_get_property (source, "remember_password");
+
+	context->auth_remember = (g_strcmp0 (property, "true") == 0);
+}
+
+static gchar *
+load_book_source_password_prompt (EBook *book,
+                                  LoadContext *context,
+                                  gboolean reprompt)
+{
+	ESource *source;
+	GString *string;
+	const gchar *title;
+	gchar *password;
+	guint32 flags;
+
+	source = e_book_get_source (book);
+	string = g_string_sized_new (256);
+
+	flags = E_PASSWORDS_REMEMBER_FOREVER |
+		E_PASSWORDS_SECRET | E_PASSWORDS_ONLINE;
+
+	if (reprompt) {
+		g_string_assign (string, _("Failed to authenticate.\n"));
+		flags |= E_PASSWORDS_REPROMPT;
+	}
+
+	g_string_append_printf (
+		string, _("Enter password for %s (user %s)"),
+		e_source_peek_name (source), context->auth_username);
+
+	/* XXX Dialog windows should not have titles. */
+	title = "";
+
+	password = e_passwords_ask_password (
+		title, context->auth_component,
+		context->auth_uri, string->str, flags,
+		&context->auth_remember, context->parent);
+
+	g_string_free (string, TRUE);
+
+	return password;
+}
+
+static void
+load_book_source_thread (GSimpleAsyncResult *simple,
+                         ESource *source,
+                         GCancellable *cancellable)
+{
+	EBook *book;
+	LoadContext *context;
+	gchar *password;
+	gboolean reprompt = FALSE;
+	GError *error = NULL;
+
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	book = e_book_new (source, &error);
+	if (book == NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+		return;
+	}
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_object_unref (book);
+		g_error_free (error);
+		return;
+	}
+
+	if (!e_book_open (book, FALSE, &error)) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_object_unref (book);
+		g_error_free (error);
+		return;
+	}
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_object_unref (book);
+		g_error_free (error);
+		return;
+	}
+
+	/* Do we need to authenticate? */
+	if (context->auth_method == NULL)
+		goto exit;
+
+	password = e_passwords_get_password (
+		context->auth_component, context->auth_uri);
+
+prompt:
+	if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_object_unref (book);
+		g_error_free (error);
+		g_free (password);
+		return;
+	}
+
+	if (password == NULL) {
+		password = load_book_source_password_prompt (
+			book, context, reprompt);
+		reprompt = TRUE;
+	}
+
+	/* If we have a password, attempt to authenticate with it. */
+	if (password != NULL) {
+		e_book_authenticate_user (
+			book, context->auth_username, password,
+			context->auth_method, &error);
+
+		g_free (password);
+		password = NULL;
+
+	/* The user did not wish to provide a password.  If the address
+	 * book can be accessed anonymously, do that but warn about it. */
+	} else if (e_book_check_static_capability (book, "anon-access")) {
+		context->anonymous_alert = TRUE;
+		goto exit;
+
+	/* Final fallback is to fail. */
+	} else {
+		g_cancellable_cancel (cancellable);
+		goto prompt;
+	}
+
+	/* If authentication failed, forget the password and reprompt. */
+	if (g_error_matches (
+		error, E_BOOK_ERROR, E_BOOK_ERROR_AUTHENTICATION_FAILED)) {
+		e_passwords_forget_password (
+			context->auth_component, context->auth_uri);
+		g_clear_error (&error);
+		goto prompt;
+
+	} else if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_object_unref (book);
+		g_error_free (error);
+		return;
+	}
+
+exit:
+	context->book = book;
+}
+
 /**
  * e_load_book_source_async:
  * @source: an #ESource
- * @open_func_ex: a function to call when the operation finishes, or %NULL
- * @user_data: data to pass to callback function
+ * @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 #EBook specified by @source, and starts a non-blocking
- * open operation on it. If the book requires authorization, presents
- * a window asking the user for such.
+ * Creates a new #EBook specified by @source and opens it, prompting the
+ * user for authentication if necessary.
  *
- * When the operation finishes, calls the callback function indicating
- * if it succeeded or not. If you don't care, you can pass %NULL for
- * @open_func_ex, and no action will be taken on completion.
+ * When the operation is finished, @callback will be called.  You can
+ * then call e_load_book_source_finish() to obtain the resulting #EBook.
  *
- * Returns: A new #EBook that is being opened.
+ * Since: 2.32
+ **/
+void
+e_load_book_source_async (ESource *source,
+                          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);
+	}
+
+	context = g_slice_new0 (LoadContext);
+	context->parent = parent;
+	context->cancellable = cancellable;
+
+	/* Extract authentication details from the ESource before
+	 * spawning the thread, since ESource is not thread-safe. */
+	load_book_source_get_auth_details (source, context);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (source), callback,
+		user_data, e_load_book_source_async);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, context, (GDestroyNotify)
+		load_book_source_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, (GSimpleAsyncThreadFunc) load_book_source_thread,
+		G_PRIORITY_DEFAULT, context->cancellable);
+
+	g_object_unref (simple);
+}
+
+/**
+ * e_load_book_source_finish:
+ * @source: an #ESource
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes an asynchronous #EBook open operation started with
+ * e_load_book_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 #EBook, or %NULL or error
  *
  * Since: 2.32
  **/
 EBook *
-e_load_book_source_async (ESource *source, EBookAsyncCallback open_func_ex, gpointer user_data)
+e_load_book_source_finish (ESource *source,
+                           GAsyncResult *result,
+                           GError **error)
 {
-	EBook          *book;
-	LoadSourceData *load_source_data = g_new0 (LoadSourceData, 1);
+	GSimpleAsyncResult *simple;
+	LoadContext *context;
 
-	load_source_data->source = g_object_ref (source);
-	load_source_data->open_func_ex = open_func_ex;
-	load_source_data->open_func_data = user_data;
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
 
-	book = e_book_new (source, NULL);
-	if (!book)
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+			result, G_OBJECT (source),
+			e_load_book_source_async), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	if (g_simple_async_result_propagate_error (simple, error))
 		return NULL;
 
-	load_source_data->book = book;
-	g_object_ref (book);
-	e_book_open_async (book, FALSE, load_source_cb, load_source_data);
+	context = g_simple_async_result_get_op_res_gpointer (simple);
+	g_return_val_if_fail (context != NULL, NULL);
 
-	return book;
+	/* Alert the user that an address book is being accessed anonymously.
+	 * FIXME Do not mention "LDAP", as this may apply to other backends. */
+	if (context->anonymous_alert) {
+		GtkWidget *dialog;
+
+		dialog = gtk_message_dialog_new (
+			context->parent, 0,
+			GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
+			_("Accessing LDAP Server anonymously"));
+		gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+	}
+
+	e_source_set_property (
+		source, "remember_password",
+		context->auth_remember ? "true" : "false");
+
+	return g_object_ref (context->book);
 }
diff --git a/libedataserverui/e-book-auth-util.h b/libedataserverui/e-book-auth-util.h
index 893991e..b7075b5 100644
--- a/libedataserverui/e-book-auth-util.h
+++ b/libedataserverui/e-book-auth-util.h
@@ -26,12 +26,26 @@
 #ifndef E_BOOK_AUTH_UTIL_H
 #define E_BOOK_AUTH_UTIL_H
 
+#include <gtk/gtk.h>
 #include <libebook/e-book.h>
 
+G_BEGIN_DECLS
+
 #ifndef E_BOOK_DISABLE_DEPRECATED
-EBook *e_load_book_source (ESource *source, EBookCallback open_func, gpointer user_data);
+EBook *		e_load_book_source		(ESource *source,
+						 EBookCallback open_func,
+						 gpointer user_data);
 #endif
 
-EBook *e_load_book_source_async (ESource *source, EBookAsyncCallback open_func, gpointer user_data);
+void		e_load_book_source_async	(ESource *source,
+						 GtkWindow *parent,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+EBook *		e_load_book_source_finish	(ESource *source,
+						 GAsyncResult *result,
+						 GError **error);
 
-#endif
+G_END_DECLS
+
+#endif /* E_BOOK_AUTH_UTIL_H */
diff --git a/libedataserverui/e-name-selector-dialog.c b/libedataserverui/e-name-selector-dialog.c
index 88fe902..c3a58d7 100644
--- a/libedataserverui/e-name-selector-dialog.c
+++ b/libedataserverui/e-name-selector-dialog.c
@@ -58,9 +58,9 @@ typedef struct {
 
 struct _ENameSelectorDialogPrivate {
 
-	EBook *pending_book;
 	ENameSelectorModel *name_selector_model;
 	GtkTreeModelSort *contact_sort;
+	GCancellable *cancellable;
 
 	GtkBuilder *gui;
 	GtkTreeView *contact_view;
@@ -460,10 +460,10 @@ remove_books (ENameSelectorDialog *name_selector_dialog)
 	g_list_free (books);
 
 	/* See if we have a book pending; stop loading it if so */
-	if (name_selector_dialog->priv->pending_book) {
-		e_book_cancel (name_selector_dialog->priv->pending_book, NULL);
-		g_object_unref (name_selector_dialog->priv->pending_book);
-		name_selector_dialog->priv->pending_book = NULL;
+	if (name_selector_dialog->priv->cancellable != NULL) {
+		g_cancellable_cancel (name_selector_dialog->priv->cancellable);
+		g_object_unref (name_selector_dialog->priv->cancellable);
+		name_selector_dialog->priv->cancellable = NULL;
 	}
 }
 
@@ -777,51 +777,83 @@ view_complete(EBookView *view, EBookViewStatus status, const gchar *error_msg, E
 }
 
 static void
-book_opened (EBook *book, const GError *error, gpointer data)
+book_loaded_cb (ESource *source,
+                GAsyncResult *result,
+                ENameSelectorDialog *name_selector_dialog)
 {
-	ENameSelectorDialog *name_selector_dialog = E_NAME_SELECTOR_DIALOG (data);
-	EContactStore       *contact_store;
-	EBookView           *view;
+	EBook *book;
+	EBookView *view;
+	EContactStore *store;
+	ENameSelectorModel *model;
+	GError *error = NULL;
 
-	if (error) {
-		gchar *msg;
+	book = e_load_book_source_finish (source, result, &error);
 
-		msg = g_strdup_printf ("Error loading addressbook, code:%d (%s)", error->code, error->message);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		g_warn_if_fail (book == NULL);
+		g_error_free (error);
+		goto exit;
+	}
 
-		gtk_label_set_text(
-			name_selector_dialog->priv->status_label,
-			msg);
+	if (error != NULL) {
+		gchar *message;
 
-		g_warning ("ENameSelectorDialog failed to open book! (%d - %s)", error->code, error->message);
+		/* FIXME This shold be translated, no? */
+		message = g_strdup_printf (
+			"Error loading address book: %s", error->message);
+		gtk_label_set_text (
+			name_selector_dialog->priv->status_label, message);
+		g_free (message);
 
-		return;
+		g_warn_if_fail (book == NULL);
+		g_error_free (error);
+		goto exit;
 	}
 
-	contact_store = e_name_selector_model_peek_contact_store (
-		name_selector_dialog->priv->name_selector_model);
-	e_contact_store_add_book (contact_store, book);
-	view = find_contact_source_by_book_return_view(contact_store, book);
-	g_signal_connect(view, "status_message", G_CALLBACK(status_message), name_selector_dialog);
-	g_signal_connect(view, "view_complete", G_CALLBACK(view_complete), name_selector_dialog);
+	model = name_selector_dialog->priv->name_selector_model;
+	store = e_name_selector_model_peek_contact_store (model);
+	e_contact_store_add_book (store, book);
+
+	view = find_contact_source_by_book_return_view (store, book);
+
+	g_signal_connect (
+		view, "status-message",
+		G_CALLBACK (status_message), name_selector_dialog);
+
+	g_signal_connect (
+		view, "view-complete",
+		G_CALLBACK (view_complete), name_selector_dialog);
 
 	g_object_unref (book);
-	name_selector_dialog->priv->pending_book = NULL;
+
+exit:
+	g_object_unref (name_selector_dialog);
 }
 
 static void
 source_changed (ENameSelectorDialog *name_selector_dialog,
                 ESourceComboBox *source_combo_box)
 {
+	GCancellable *cancellable;
 	ESource *source;
+	gpointer parent;
 
 	source = e_source_combo_box_get_active (source_combo_box);
 
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (name_selector_dialog));
+	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
+
 	/* Remove any previous books being shown or loaded */
 	remove_books (name_selector_dialog);
 
+	cancellable = g_cancellable_new ();
+	name_selector_dialog->priv->cancellable = cancellable;
+
 	/* Start loading selected book */
-	name_selector_dialog->priv->pending_book = e_load_book_source_async (
-		source, book_opened, name_selector_dialog);
+	e_load_book_source_async (
+		source, parent, cancellable,
+		(GAsyncReadyCallback) book_loaded_cb,
+		g_object_ref (name_selector_dialog));
 }
 
 /* --------------- *
diff --git a/libedataserverui/e-name-selector-entry.c b/libedataserverui/e-name-selector-entry.c
index b0422fc..ab44bdc 100644
--- a/libedataserverui/e-name-selector-entry.c
+++ b/libedataserverui/e-name-selector-entry.c
@@ -1948,6 +1948,40 @@ setup_contact_store (ENameSelectorEntry *name_selector_entry)
 }
 
 static void
+book_loaded_cb (ESource *source,
+                GAsyncResult *result,
+                ENameSelectorEntry *name_selector_entry)
+{
+	EBook *book;
+	EContactStore *store;
+	GError *error = NULL;
+
+	book = e_load_book_source_finish (source, result, &error);
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		g_warn_if_fail (book == NULL);
+		g_error_free (error);
+		goto exit;
+	}
+
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		g_warn_if_fail (book == NULL);
+		g_error_free (error);
+		goto exit;
+	}
+
+	g_return_if_fail (E_IS_BOOK (book));
+
+	store = name_selector_entry->priv->contact_store;
+	e_contact_store_add_book (store, book);
+	g_object_unref (book);
+
+exit:
+	g_object_unref (name_selector_entry);
+}
+
+static void
 setup_default_contact_store (ENameSelectorEntry *name_selector_entry)
 {
 	GSList *groups;
@@ -1966,8 +2000,7 @@ setup_default_contact_store (ENameSelectorEntry *name_selector_entry)
 		GSList       *m;
 
 		for (m = sources; m; m = g_slist_next (m)) {
-			ESource     *source = m->data;
-			EBook       *book;
+			ESource *source = m->data;
 			const gchar *completion;
 
 			/* Skip non-completion sources */
@@ -1975,12 +2008,10 @@ setup_default_contact_store (ENameSelectorEntry *name_selector_entry)
 			if (!completion || g_ascii_strcasecmp (completion, "true"))
 				continue;
 
-			book = e_load_book_source_async (source, NULL, NULL);
-			if (!book)
-				continue;
-
-			e_contact_store_add_book (name_selector_entry->priv->contact_store, book);
-			g_object_unref (book);
+			e_load_book_source_async (
+				source, NULL, NULL,
+				(GAsyncReadyCallback) book_loaded_cb,
+				g_object_ref (name_selector_entry));
 		}
 	}
 
diff --git a/libedataserverui/e-name-selector.c b/libedataserverui/e-name-selector.c
index 96178dd..ec72835 100644
--- a/libedataserverui/e-name-selector.c
+++ b/libedataserverui/e-name-selector.c
@@ -54,84 +54,142 @@ struct _ENameSelectorPrivate {
 	ENameSelectorDialog *dialog;
 
 	GArray *sections;
+	ESourceList *source_list;
 
-	GThread *load_book_thread;
 	gboolean load_cancelled;
 	GArray *source_books;
 };
 
 G_DEFINE_TYPE (ENameSelector, e_name_selector, G_TYPE_OBJECT)
 
-static gpointer
-load_books_thread (gpointer user_data)
+static void
+name_selector_book_loaded_cb (ESource *source,
+                              GAsyncResult *result,
+                              ENameSelector *name_selector)
 {
-	ENameSelector *name_selector = user_data;
-	ENameSelectorPrivate *priv;
-	ESourceList *source_list;
-	GSList      *groups;
-	GSList      *l;
+	EBook *book;
+	GArray *sections;
+	SourceBook source_book;
+	guint ii;
+	GError *error = NULL;
+
+	book = e_load_book_source_finish (source, result, &error);
+
+	if (error != NULL) {
+		g_warning (
+			"ENameSelector: Could not load \"%s\": %s",
+			e_source_peek_name (source), error->message);
+		g_error_free (error);
+		goto exit;
+	}
 
-	/* XXX This thread is necessary because the e_book_new can block.
-	   See gnome's bug #540779 for more information. */
+	g_return_if_fail (E_IS_BOOK (book));
 
-	g_return_val_if_fail (name_selector != NULL, NULL);
+	source_book.book = book;
+	source_book.is_completion_book = TRUE;
 
-	priv = E_NAME_SELECTOR_GET_PRIVATE (name_selector);
+	g_array_append_val (name_selector->priv->source_books, source_book);
+
+	sections = name_selector->priv->sections;
+
+	for (ii = 0; ii < sections->len; ii++) {
+		EContactStore *store;
+		Section *section;
+
+		section = &g_array_index (sections, Section, ii);
+		if (section->entry == NULL)
+			continue;
+
+		store = e_name_selector_entry_peek_contact_store (
+			section->entry);
+		if (store != NULL)
+			e_contact_store_add_book (store, book);
+	}
+
+exit:
+	g_object_unref (name_selector);
+}
+
+static void
+name_selector_load_books (ENameSelector *name_selector)
+{
+	ESourceList *source_list;
+	GSList *groups;
+	GSList *iter1;
 
 	if (!e_book_get_addressbooks (&source_list, NULL)) {
 		g_warning ("ENameSelector can't find any addressbooks!");
-		return NULL;
+		return;
 	}
 
+	/* This keeps the source groups alive while the async operation
+	 * is running, without which e_book_new() can't obtain an absolute
+	 * URI for the ESource.   We drop the reference in dispose(). */
+	name_selector->priv->source_list = source_list;
+
 	groups = e_source_list_peek_groups (source_list);
 
-	for (l = groups; l && !priv->load_cancelled; l = g_slist_next (l)) {
-		ESourceGroup *group   = l->data;
-		GSList       *sources = e_source_group_peek_sources (group);
-		GSList       *m;
+	for (iter1 = groups; iter1 != NULL; iter1 = iter1->next) {
+		ESourceGroup *source_group;
+		GSList *sources;
+		GSList *iter2;
+
+		source_group = E_SOURCE_GROUP (iter1->data);
+		sources = e_source_group_peek_sources (source_group);
 
-		for (m = sources; m && !priv->load_cancelled; m = g_slist_next (m)) {
-			ESource      *source = m->data;
-			const gchar  *completion;
-			SourceBook    source_book;
+		for (iter2 = sources; iter2 != NULL; iter2 = iter2->next) {
+			ESource *source;
+			const gchar *property;
 
-			/* We're only loading completion books for now, as we don't want
-			 * unnecessary auth prompts */
-			completion = e_source_get_property (source, "completion");
-			if (!completion || g_ascii_strcasecmp (completion, "true"))
+			source = E_SOURCE (iter2->data);
+
+			/* We're only loading completion books for now,
+			 * as we don't want unnecessary authentication
+			 * prompts. */
+			property = e_source_get_property (source, "completion");
+
+			if (property == NULL)
 				continue;
 
-			source_book.book = e_load_book_source_async (source, NULL, NULL);
-			if (!source_book.book)
+			if (g_ascii_strcasecmp (property, "true") != 0)
 				continue;
 
-			source_book.is_completion_book = TRUE;
+			/* XXX Should we allow for cancellation? */
+			e_load_book_source_async (
+				source, NULL, NULL,
+				(GAsyncReadyCallback)
+				name_selector_book_loaded_cb,
+				g_object_ref (name_selector));
+		}
+	}
+}
 
-			g_array_append_val (priv->source_books, source_book);
+static void
+name_selector_dispose (GObject *object)
+{
+	ENameSelectorPrivate *priv;
+	guint ii;
 
-			if (!priv->load_cancelled) {
-				EContactStore *store;
+	priv = E_NAME_SELECTOR_GET_PRIVATE (object);
 
-				if (name_selector->priv->sections) {
-					gint j;
+	if (priv->source_list != NULL) {
+		g_object_unref (priv->source_list);
+		priv->source_list = NULL;
+	}
 
-					for (j = 0; j < name_selector->priv->sections->len && !priv->load_cancelled; j++) {
-						Section *section = &g_array_index (name_selector->priv->sections, Section, j);
+	for (ii = 0; ii < priv->source_books->len; ii++) {
+		SourceBook *source_book;
 
-						if (section->entry) {
-							store = e_name_selector_entry_peek_contact_store (section->entry);
-							if (store)
-								e_contact_store_add_book (store, source_book.book);
-						}
-					}
-				}
-			}
-		}
+		source_book = &g_array_index (
+			priv->source_books, SourceBook, ii);
+		if (source_book->book != NULL)
+			g_object_unref (source_book->book);
 	}
 
-	g_object_unref (source_list);
+	g_array_set_size (priv->source_books, 0);
 
-	return NULL;
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_name_selector_parent_class)->dispose (object);
 }
 
 static void
@@ -141,27 +199,9 @@ name_selector_finalize (GObject *object)
 
 	priv = E_NAME_SELECTOR_GET_PRIVATE (object);
 
-	if (priv->load_book_thread) {
-		priv->load_cancelled = TRUE;
-		g_thread_join (priv->load_book_thread);
-		priv->load_book_thread = NULL;
-	}
-
-	if (priv->source_books) {
-		gint i;
-
-		for (i = 0; i < priv->source_books->len; i++) {
-			SourceBook *source_book = &g_array_index (priv->source_books, SourceBook, i);
+	g_array_free (priv->source_books, TRUE);
 
-			if (source_book->book)
-				g_object_unref (source_book->book);
-		}
-
-		g_array_free (priv->source_books, TRUE);
-		priv->source_books = NULL;
-	}
-
-	/* Chain up to parent's finalize() methods. */
+	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_name_selector_parent_class)->finalize (object);
 }
 
@@ -173,6 +213,7 @@ e_name_selector_class_init (ENameSelectorClass *class)
 	g_type_class_add_private (class, sizeof (ENameSelectorPrivate));
 
 	object_class = G_OBJECT_CLASS (class);
+	object_class->dispose = name_selector_dispose;
 	object_class->finalize = name_selector_finalize;
 }
 
@@ -181,7 +222,6 @@ e_name_selector_init (ENameSelector *name_selector)
 {
 	GArray *sections;
 	GArray *source_books;
-	GThread *load_book_thread;
 
 	sections = g_array_new (FALSE, FALSE, sizeof (Section));
 	source_books = g_array_new (FALSE, FALSE, sizeof (SourceBook));
@@ -190,11 +230,8 @@ e_name_selector_init (ENameSelector *name_selector)
 	name_selector->priv->sections = sections;
 	name_selector->priv->model = e_name_selector_model_new ();
 	name_selector->priv->source_books = source_books;
-	name_selector->priv->load_book_thread = load_book_thread;
-
-	load_book_thread = g_thread_create (
-		load_books_thread, name_selector, TRUE, NULL);
 
+	name_selector_load_books (name_selector);
 }
 
 /**



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