[evolution/webkit] mark-all-read: Reimplement to not block.



commit cce05778f0efdd32aeee59ada52d776c95445742
Author: Matthew Barnes <mbarnes redhat com>
Date:   Wed May 11 21:36:34 2011 -0400

    mark-all-read: Reimplement to not block.
    
    I'm mainly trying to get rid of mail_get_folder(), but I'm also tired of
    this plugin blocking the UI.  If there's a lot of messages the lockup is
    really noticable.

 plugins/mark-all-read/mark-all-read.c |  267 +++++++++++++++++++++++++--------
 1 files changed, 204 insertions(+), 63 deletions(-)
---
diff --git a/plugins/mark-all-read/mark-all-read.c b/plugins/mark-all-read/mark-all-read.c
index 20b5a2d..1ec6603 100644
--- a/plugins/mark-all-read/mark-all-read.c
+++ b/plugins/mark-all-read/mark-all-read.c
@@ -29,10 +29,10 @@
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 #include <e-util/e-plugin-ui.h>
+
+#include <mail/e-mail-folder-utils.h>
 #include <mail/em-folder-tree.h>
 #include <mail/em-utils.h>
-#include <mail/mail-ops.h>
-#include <mail/mail-mt.h>
 
 #include <shell/e-shell-sidebar.h>
 #include <shell/e-shell-view.h>
@@ -44,6 +44,13 @@
 	N_("Do you want to mark messages as read in the current folder " \
 	   "only, or in the current folder as well as all subfolders?")
 
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+	EActivity *activity;
+	GQueue folder_names;
+};
+
 enum {
 	MARK_ALL_READ_CANCEL,
 	MARK_ALL_READ_CURRENT_FOLDER,
@@ -54,6 +61,19 @@ gboolean	e_plugin_ui_init		(GtkUIManager *ui_manager,
 						 EShellView *shell_view);
 gint e_plugin_lib_enable (EPlugin *ep, gint enable);
 
+static void
+async_context_free (AsyncContext *context)
+{
+	if (context->activity != NULL)
+		g_object_unref (context->activity);
+
+	/* This should be empty already, but just to be sure... */
+	while (!g_queue_is_empty (&context->folder_names))
+		g_free (g_queue_pop_head (&context->folder_names));
+
+	g_slice_free (AsyncContext, context);
+}
+
 gint
 e_plugin_lib_enable (EPlugin *ep, gint enable)
 {
@@ -327,114 +347,235 @@ scan_folder_tree_for_unread (const gchar *folder_uri)
 }
 
 static void
-mark_all_as_read (CamelFolder *folder)
+collect_folder_names (GQueue *folder_names,
+                      CamelFolderInfo *folder_info)
 {
-	GPtrArray *uids;
-	gint i;
-
-	uids =  camel_folder_get_uids (folder);
-	camel_folder_freeze (folder);
-	for (i=0;i<uids->len;i++)
-		camel_folder_set_message_flags (
-			folder, uids->pdata[i],
-			CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
-	camel_folder_thaw (folder);
-	camel_folder_free_uids (folder, uids);
+	while (folder_info != NULL) {
+		if (folder_info->child != NULL)
+			collect_folder_names (
+				folder_names, folder_info->child);
+
+		g_queue_push_tail (
+			folder_names, g_strdup (folder_info->full_name));
+
+		folder_info = folder_info->next;
+	}
 }
 
-static gboolean
-mar_all_sub_folders (CamelStore *store,
-                     CamelFolderInfo *fi,
-                     GCancellable *cancellable,
-                     GError **error)
+static void
+mar_got_folder (CamelStore *store,
+                GAsyncResult *result,
+                AsyncContext *context)
 {
-	while (fi) {
-		CamelFolder *folder;
+	EAlertSink *alert_sink;
+	GCancellable *cancellable;
+	CamelFolder *folder;
+	gchar *folder_name;
+	GError *error = NULL;
 
-		if (fi->child) {
-			if (!mar_all_sub_folders (
-				store, fi->child, cancellable, error))
-				return FALSE;
-		}
+	alert_sink = e_activity_get_alert_sink (context->activity);
+	cancellable = e_activity_get_cancellable (context->activity);
 
-		folder = camel_store_get_folder_sync (
-			store, fi->full_name, 0, cancellable, error);
-		if (folder == NULL)
-			return FALSE;
+	folder = camel_store_get_folder_finish (store, result, &error);
 
-		if (!CAMEL_IS_VEE_FOLDER (folder))
-			mark_all_as_read (folder);
+	if (e_activity_handle_cancellation (context->activity, error)) {
+		g_warn_if_fail (folder == NULL);
+		async_context_free (context);
+		g_error_free (error);
+		return;
 
-		fi = fi->next;
+	} else if (error != NULL) {
+		g_warn_if_fail (folder == NULL);
+		e_alert_submit (
+			alert_sink, "folder-open",
+			error->message, NULL);
+		async_context_free (context);
+		g_error_free (error);
+		return;
 	}
 
-	return TRUE;
+	g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+	/* XXX Skip virtual folders.  I guess this is intended for
+	 *     virtual Junk and Trash folders.  But what if I want
+	 *     to mark messages in a Search Folder? */
+	if (!CAMEL_IS_VEE_FOLDER (folder)) {
+		GPtrArray *uids;
+		gint ii;
+
+		camel_folder_freeze (folder);
+
+		uids = camel_folder_get_uids (folder);
+
+		for (ii = 0; ii < uids->len; ii++)
+			camel_folder_set_message_flags (
+				folder, uids->pdata[ii],
+				CAMEL_MESSAGE_SEEN,
+				CAMEL_MESSAGE_SEEN);
+
+		camel_folder_free_uids (folder, uids);
+
+		camel_folder_thaw (folder);
+	}
+
+	g_object_unref (folder);
+
+	/* If the folder name queue is empty, we're done. */
+	if (g_queue_is_empty (&context->folder_names)) {
+		e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
+		async_context_free (context);
+		return;
+	}
+
+	folder_name = g_queue_pop_head (&context->folder_names);
+
+	camel_store_get_folder (
+		store, folder_name, 0,
+		G_PRIORITY_DEFAULT, cancellable,
+		(GAsyncReadyCallback) mar_got_folder, context);
+
+	g_free (folder_name);
 }
 
 static void
-mar_got_folder (gchar *folder_uri,
-                CamelFolder *folder,
-                gpointer data)
+mar_got_folder_info (CamelStore *store,
+                     GAsyncResult *result,
+                     AsyncContext *context)
 {
+	EAlertSink *alert_sink;
+	GCancellable *cancellable;
 	CamelFolderInfo *folder_info;
-	CamelStore *parent_store;
-	const gchar *full_name;
+	gchar *folder_name;
 	gint response;
+	GError *error = NULL;
 
-	/* FIXME we have to disable the menu item */
-	if (!folder)
-		return;
+	alert_sink = e_activity_get_alert_sink (context->activity);
+	cancellable = e_activity_get_cancellable (context->activity);
 
-	full_name = camel_folder_get_full_name (folder);
-	parent_store = camel_folder_get_parent_store (folder);
+	folder_info = camel_store_get_folder_info_finish (
+		store, result, &error);
 
-	/* FIXME Not passing a GCancellable or GError here. */
-	folder_info = camel_store_get_folder_info_sync (
-		parent_store, full_name,
-		CAMEL_STORE_FOLDER_INFO_RECURSIVE |
-		CAMEL_STORE_FOLDER_INFO_FAST, NULL, NULL);
+	if (e_activity_handle_cancellation (context->activity, error)) {
+		g_warn_if_fail (folder_info == NULL);
+		async_context_free (context);
+		g_error_free (error);
+		return;
+
+	/* XXX This EAlert primary text isn't technically correct since
+	 *     we're just collecting folder tree info and haven't actually
+	 *     opened any folders yet, but the user doesn't need to know. */
+	} else if (error != NULL) {
+		g_warn_if_fail (folder_info == NULL);
+		e_alert_submit (
+			alert_sink, "folder-open",
+			error->message, NULL);
+		async_context_free (context);
+		g_error_free (error);
+		return;
+	}
 
-	if (folder_info == NULL)
-		goto exit;
+	g_return_if_fail (folder_info != NULL);
 
 	response = prompt_user (folder_info->child != NULL);
 
-	if (response == MARK_ALL_READ_CANCEL)
+	if (response == MARK_ALL_READ_CURRENT_FOLDER)
+		g_queue_push_tail (
+			&context->folder_names,
+			g_strdup (folder_info->full_name));
+
+	if (response == MARK_ALL_READ_WITH_SUBFOLDERS)
+		collect_folder_names (&context->folder_names, folder_info);
+
+	camel_store_free_folder_info (store, folder_info);
+
+	/* If the user cancelled, we're done. */
+	if (g_queue_is_empty (&context->folder_names)) {
+		e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED);
+		async_context_free (context);
 		return;
+	}
 
-	if (response == MARK_ALL_READ_CURRENT_FOLDER)
-		mark_all_as_read (folder);
-	else if (response == MARK_ALL_READ_WITH_SUBFOLDERS)
-		/* FIXME Not passing a GCancellable or GError here. */
-		mar_all_sub_folders (parent_store, folder_info, NULL, NULL);
+	folder_name = g_queue_pop_head (&context->folder_names);
+
+	camel_store_get_folder (
+		store, folder_name, 0,
+		G_PRIORITY_DEFAULT, cancellable,
+		(GAsyncReadyCallback) mar_got_folder, context);
 
-exit:
-	camel_store_free_folder_info (parent_store, folder_info);
+	g_free (folder_name);
 }
 
 static void
 action_mail_mark_read_recursive_cb (GtkAction *action,
                                     EShellView *shell_view)
 {
+	EAlertSink *alert_sink;
+	GCancellable *cancellable;
+	EShellBackend *shell_backend;
+	EShellContent *shell_content;
 	EShellSidebar *shell_sidebar;
 	EMFolderTree *folder_tree;
 	EMailSession *session;
+	AsyncContext *context;
+	CamelStore *store = NULL;
+	CamelStoreGetFolderInfoFlags flags;
+	gchar *folder_name = NULL;
 	gchar *folder_uri;
+	GError *error = NULL;
 
+	shell_backend = e_shell_view_get_shell_backend (shell_view);
+	shell_content = e_shell_view_get_shell_content (shell_view);
 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+
 	g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL);
 
 	session = em_folder_tree_get_session (folder_tree);
-
 	folder_uri = em_folder_tree_get_selected_uri (folder_tree);
 	g_return_if_fail (folder_uri != NULL);
 
-	mail_get_folder (
-		session, folder_uri, 0, mar_got_folder,
-		NULL, mail_msg_unordered_push);
+	e_mail_folder_uri_parse (
+		CAMEL_SESSION (session), folder_uri,
+		&store, &folder_name, &error);
 
 	g_object_unref (folder_tree);
 	g_free (folder_uri);
+
+	/* Failure here is unlikely enough that we don't need an
+	 * EAlert for it, but we should still leave a breadcrumb. */
+	if (error != NULL) {
+		g_warning ("%s", error->message);
+		return;
+	}
+
+	g_return_if_fail (CAMEL_IS_STORE (store));
+	g_return_if_fail (folder_name != NULL);
+
+	/* Open the selected folder asynchronously. */
+
+	context = g_slice_new0 (AsyncContext);
+	context->activity = e_activity_new ();
+	g_queue_init (&context->folder_names);
+
+	alert_sink = E_ALERT_SINK (shell_content);
+	e_activity_set_alert_sink (context->activity, alert_sink);
+
+	cancellable = camel_operation_new ();
+	e_activity_set_cancellable (context->activity, cancellable);
+
+	e_shell_backend_add_activity (shell_backend, context->activity);
+
+	flags = CAMEL_STORE_FOLDER_INFO_RECURSIVE |
+		CAMEL_STORE_FOLDER_INFO_FAST;
+
+	camel_store_get_folder_info (
+		store, folder_name, flags,
+		G_PRIORITY_DEFAULT, cancellable,
+		(GAsyncReadyCallback) mar_got_folder_info, context);
+
+	g_object_unref (cancellable);
+
+	g_object_unref (store);
+	g_free (folder_name);
 }
 
 static GtkActionEntry entries[] = {



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