[evolution/account-mgmt: 7/54] Adapt libeutil to the new ESource API.



commit 6d1d256e75d95f1b77eeca9b28af82145318bde9
Author: Matthew Barnes <mbarnes redhat com>
Date:   Tue Dec 7 15:30:15 2010 -0600

    Adapt libeutil to the new ESource API.

 e-util/Makefile.am                                 |    6 +-
 e-util/e-async-utils.c                             |  149 ++++
 .../e-account-utils.h => e-util/e-async-utils.h    |   30 +-
 e-util/e-plugin-util.c                             |  497 -------------
 e-util/e-plugin-util.h                             |   45 --
 e-util/e-source-util.c                             |  187 +++++
 e-util/e-source-util.h                             |   42 ++
 e-util/e-util.c                                    |   46 +-
 e-util/e-util.h                                    |   10 +-
 libemail-utils/e-account-utils.c                   |  252 -------
 libemail-utils/e-signature-list.c                  |  501 -------------
 libemail-utils/e-signature-list.h                  |   91 ---
 libemail-utils/e-signature-utils.c                 |  336 ---------
 libemail-utils/e-signature-utils.h                 |   40 -
 libemail-utils/e-signature.c                       |  749 --------------------
 libemail-utils/e-signature.h                       |   90 ---
 widgets/misc/Makefile.am                           |    8 +-
 widgets/misc/e-account-combo-box.c                 |  514 --------------
 widgets/misc/e-account-combo-box.h                 |   85 ---
 widgets/misc/e-mail-account-manager.c              |  384 ++++++++++
 widgets/misc/e-mail-account-manager.h              |   77 ++
 widgets/misc/e-mail-account-tree-view.c            |  575 +++++++++++++++
 widgets/misc/e-mail-account-tree-view.h            |   83 +++
 widgets/misc/e-mail-identity-combo-box.c           |  374 ++++++++++
 widgets/misc/e-mail-identity-combo-box.h           |   76 ++
 widgets/misc/test-mail-accounts.c                  |   61 ++
 26 files changed, 2047 insertions(+), 3261 deletions(-)
---
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 63fce88..9601970 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -19,6 +19,7 @@ privsolib_LTLIBRARIES = libeutil.la
 
 eutilinclude_HEADERS = 				\
 	e-activity.h				\
+	e-async-utils.h				\
 	e-bit-array.h				\
 	e-categories-config.h			\
 	e-charset.h				\
@@ -38,10 +39,10 @@ eutilinclude_HEADERS = 				\
 	e-print.h				\
 	e-plugin.h				\
 	e-plugin-ui.h				\
-	e-plugin-util.h				\
 	e-selection.h				\
 	e-sorter.h				\
 	e-sorter-array.h			\
+	e-source-util.h				\
 	e-stock-request.h			\
 	e-text-event-processor-emacs-like.h	\
 	e-text-event-processor-types.h		\
@@ -84,6 +85,7 @@ libeutil_la_CPPFLAGS =							\
 libeutil_la_SOURCES =				\
 	$(eutilinclude_HEADERS)			\
 	e-activity.c				\
+	e-async-utils.c				\
 	e-bit-array.c				\
 	e-categories-config.c			\
 	e-charset.c				\
@@ -102,11 +104,11 @@ libeutil_la_SOURCES =				\
 	e-poolv.c				\
 	e-plugin.c				\
 	e-plugin-ui.c				\
-	e-plugin-util.c				\
 	e-print.c				\
 	e-selection.c				\
 	e-sorter.c				\
 	e-sorter-array.c			\
+	e-source-util.c				\
 	e-stock-request.c			\
 	e-text-event-processor-emacs-like.c	\
 	e-text-event-processor.c		\
diff --git a/e-util/e-async-utils.c b/e-util/e-async-utils.c
new file mode 100644
index 0000000..acc8443
--- /dev/null
+++ b/e-util/e-async-utils.c
@@ -0,0 +1,149 @@
+/*
+ * e-async-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-async-utils
+ * @short_description: utilities for running asynchronous methods
+ * @include: e-util/e-async-utils.h
+ *
+ * #EAsyncClosure provides a simple way to run an asynchronous function
+ * synchronously without blocking a running #GMainLoop.
+ *
+ * 1) Create an #EAsyncClosure with e_async_closure_new().
+ *
+ * 2) Call the asynchronous function passing e_async_closure_callback() as
+ *    the #GAsyncReadyCallback argument and the #EAsyncClosure as the data
+ *    argument.
+ *
+ * 3) Call e_async_closure_wait() and collect the #GAsyncResult.
+ *
+ * 4) Call the corresponding asynchronous "finish" function, passing the
+ *    #GAsyncResult returned by e_async_closure_wait().
+ *
+ * 5) If needed, repeat steps 2-4 for additional asynchronous functions
+ *    using the same #EAsyncClosure.
+ *
+ * 6) Finally, free the #EAsyncClosure with e_async_closure_free().
+ **/
+
+#include "e-async-utils.h"
+
+struct _EAsyncClosure {
+	GMainLoop *loop;
+	GMainContext *context;
+	GAsyncResult *result;
+};
+
+/**
+ * e_async_closure_new:
+ *
+ * Creates a new #EAsyncClosure for use with asynchronous functions.
+ *
+ * Returns: a new #EAsyncClosure
+ **/
+EAsyncClosure *
+e_async_closure_new (void)
+{
+	EAsyncClosure *closure;
+
+	closure = g_slice_new0 (EAsyncClosure);
+	closure->context = g_main_context_new ();
+	closure->loop = g_main_loop_new (closure->context, FALSE);
+
+	g_main_context_push_thread_default (closure->context);
+
+	return closure;
+}
+
+/**
+ * e_async_closure_wait:
+ * @closure: an #EAsyncClosure
+ *
+ * Call this function immediately after starting an asynchronous operation.
+ * The function waits for the asynchronous operation to complete and returns
+ * its #GAsyncResult to be passed to the operation's "finish" function.
+ *
+ * This function can be called repeatedly on the same #EAsyncClosure to
+ * easily string together multiple asynchronous operations.
+ *
+ * Returns: a #GAsyncResult
+ **/
+GAsyncResult *
+e_async_closure_wait (EAsyncClosure *closure)
+{
+	g_return_val_if_fail (closure != NULL, NULL);
+
+	g_main_loop_run (closure->loop);
+
+	return closure->result;
+}
+
+/**
+ * e_async_closure_free:
+ * @closure: an #EAsyncClosure
+ *
+ * Frees the @closure and the resources it holds.
+ **/
+void
+e_async_closure_free (EAsyncClosure *closure)
+{
+	g_return_if_fail (closure != NULL);
+
+	g_main_context_pop_thread_default (closure->context);
+
+	g_main_loop_unref (closure->loop);
+	g_main_context_unref (closure->context);
+
+	if (closure->result != NULL)
+		g_object_unref (closure->result);
+
+	g_slice_free (EAsyncClosure, closure);
+}
+
+/**
+ * e_async_closure_callback:
+ * @object: a #GObject
+ * @result: a #GAsyncResult
+ * @closure: an #EAsyncClosure
+ *
+ * Pass this function as the #GAsyncReadyCallback argument of an asynchronous
+ * function, and the #EAsyncClosure as the data argument.
+ *
+ * This causes e_async_closure_wait() to terminate and return @result.
+ **/
+void
+e_async_closure_callback (GObject *object,
+                          GAsyncResult *result,
+                          gpointer closure)
+{
+	EAsyncClosure *real_closure;
+
+	g_return_if_fail (G_IS_OBJECT (object));
+	g_return_if_fail (G_IS_ASYNC_RESULT (result));
+	g_return_if_fail (closure != NULL);
+
+	real_closure = closure;
+
+	/* Replace any previous result. */
+	if (real_closure->result != NULL)
+		g_object_unref (real_closure->result);
+	real_closure->result = g_object_ref (result);
+
+	g_main_loop_quit (real_closure->loop);
+}
+
diff --git a/libemail-utils/e-account-utils.h b/e-util/e-async-utils.h
similarity index 54%
rename from libemail-utils/e-account-utils.h
rename to e-util/e-async-utils.h
index d7dbd28..002b266 100644
--- a/libemail-utils/e-account-utils.h
+++ b/e-util/e-async-utils.h
@@ -1,4 +1,6 @@
 /*
+ * e-async-utils.h
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
@@ -12,26 +14,26 @@
  * You should have received a copy of the GNU Lesser General Public
  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  *
- * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  */
 
-#ifndef E_ACCOUNT_UTILS_H
-#define E_ACCOUNT_UTILS_H
+#ifndef E_ASYNC_UTILS_H
+#define E_ASYNC_UTILS_H
 
-#include <camel/camel.h>
-#include <libedataserver/e-account.h>
-#include <libedataserver/e-account-list.h>
+#include <gio/gio.h>
 
 G_BEGIN_DECLS
 
-EAccountList *	e_get_account_list		(void);
-EAccount *	e_get_default_account		(void);
-void		e_set_default_account		(EAccount *account);
-EAccount *	e_get_account_by_name		(const gchar *name);
-EAccount *	e_get_account_by_uid		(const gchar *uid);
-EAccount *	e_get_any_enabled_account	(void);
-EAccount *	e_get_default_transport		(void);
+typedef struct _EAsyncClosure EAsyncClosure;
+
+EAsyncClosure *	e_async_closure_new		(void);
+GAsyncResult *	e_async_closure_wait		(EAsyncClosure *closure);
+void		e_async_closure_free		(EAsyncClosure *closure);
+
+/* Use as a GAsyncReadyCallback. */
+void		e_async_closure_callback	(GObject *object,
+						 GAsyncResult *result,
+						 gpointer closure);
 
 G_END_DECLS
 
-#endif /* E_ACCOUNT_UTILS_H */
+#endif /* E_ASYNC_UTILS_H */
diff --git a/e-util/e-source-util.c b/e-util/e-source-util.c
new file mode 100644
index 0000000..61b7897
--- /dev/null
+++ b/e-util/e-source-util.c
@@ -0,0 +1,187 @@
+/*
+ * e-source-util.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-source-util.h"
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+	EActivity *activity;
+	ESource *source;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+	if (context->activity != NULL)
+		g_object_unref (context->activity);
+
+	if (context->source != NULL)
+		g_object_unref (context->source);
+
+	g_slice_free (AsyncContext, context);
+}
+
+static void
+source_util_remove_cb (ESource *source,
+                       GAsyncResult *result,
+                       AsyncContext *context)
+{
+	EActivity *activity;
+	EAlertSink *alert_sink;
+	GError *error = NULL;
+
+	activity = context->activity;
+	alert_sink = e_activity_get_alert_sink (activity);
+
+	e_source_remove_finish (source, result, &error);
+
+	if (e_activity_handle_cancellation (activity, error)) {
+		g_error_free (error);
+
+	} else if (error != NULL) {
+		e_alert_submit (
+			alert_sink,
+			"source:remove-source-fail",
+			e_source_get_display_name (context->source),
+			error->message, NULL);
+		g_error_free (error);
+
+	} else {
+		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
+	}
+
+	async_context_free (context);
+}
+
+/**
+ * e_source_util_remove:
+ * @source: the #ESource to be removed
+ * @alert_sink: an #EAlertSink
+ *
+ * Requests the D-Bus service to delete the key files for @source and all of
+ * its descendants and broadcast their removal to all clients.  If an error
+ * occurs, an #EAlert will be posted to @alert_sink.
+ *
+ * This function does not block.  The returned #EActivity can either be
+ * ignored or passed to something that can display activity status to the
+ * user, such as e_shell_backend_add_activity().
+ *
+ * Returns: an #EActivity to track the operation
+ **/
+EActivity *
+e_source_util_remove (ESource *source,
+                      EAlertSink *alert_sink)
+{
+	AsyncContext *context;
+	GCancellable *cancellable;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	g_return_val_if_fail (E_IS_ALERT_SINK (alert_sink), NULL);
+
+	cancellable = g_cancellable_new ();
+
+	context = g_slice_new0 (AsyncContext);
+	context->activity = e_activity_new ();
+	context->source = g_object_ref (source);
+
+	e_activity_set_alert_sink (context->activity, alert_sink);
+	e_activity_set_cancellable (context->activity, cancellable);
+
+	e_source_remove (
+		source, cancellable, (GAsyncReadyCallback)
+		source_util_remove_cb, context);
+
+	g_object_unref (cancellable);
+
+	return context->activity;
+}
+
+static void
+source_util_write_cb (ESource *source,
+                      GAsyncResult *result,
+                      AsyncContext *context)
+{
+	EActivity *activity;
+	EAlertSink *alert_sink;
+	GError *error = NULL;
+
+	activity = context->activity;
+	alert_sink = e_activity_get_alert_sink (activity);
+
+	e_source_write_finish (source, result, &error);
+
+	if (e_activity_handle_cancellation (activity, error)) {
+		g_error_free (error);
+
+	} else if (error != NULL) {
+		e_alert_submit (
+			alert_sink,
+			"source:submit-data-fail",
+			error->message, NULL);
+		g_error_free (error);
+
+	} else {
+		e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
+	}
+
+	async_context_free (context);
+}
+
+/**
+ * e_source_util_write:
+ * @source: an #ESource
+ * @alert_sink: an #EAlertSink
+ *
+ * Submits the current contents of @source to the D-Bus service to be
+ * written to disk and broadcast to other clients.  If an error occurs,
+ * an #EAlert will be posted to @alert_sink.
+ *
+ * This function does not block.  The returned #EActivity can either be
+ * ignored or passed to something that can display activity status to the
+ * user, such as e_shell_backend_add_activity().
+ *
+ * Returns: an #EActivity to track the operation
+ **/
+EActivity *
+e_source_util_write (ESource *source,
+                     EAlertSink *alert_sink)
+{
+	AsyncContext *context;
+	GCancellable *cancellable;
+
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+	g_return_val_if_fail (E_IS_ALERT_SINK (alert_sink), NULL);
+
+	cancellable = g_cancellable_new ();
+
+	context = g_slice_new0 (AsyncContext);
+	context->activity = e_activity_new ();
+
+	e_activity_set_alert_sink (context->activity, alert_sink);
+	e_activity_set_cancellable (context->activity, cancellable);
+
+	e_source_write (
+		source, cancellable, (GAsyncReadyCallback)
+		source_util_write_cb, context);
+
+	g_object_unref (cancellable);
+
+	return context->activity;
+}
+
diff --git a/e-util/e-source-util.h b/e-util/e-source-util.h
new file mode 100644
index 0000000..589e91b
--- /dev/null
+++ b/e-util/e-source-util.h
@@ -0,0 +1,42 @@
+/*
+ * e-source-util.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/* These functions combine asynchronous ESource and ESourceRegistry methods
+ * with Evolution's EActivity and EAlert facilities to offer an easy-to-use,
+ * "fire-and-forget" API for ESource operations.  Use these in situations
+ * where it's sufficient to just display an error message if the operation
+ * fails, and you don't need to operate on the result. */
+
+#ifndef E_SOURCE_UTIL_H
+#define E_SOURCE_UTIL_H
+
+#include <libedataserver/e-source.h>
+
+#include <e-util/e-activity.h>
+#include <libevolution-utils/e-alert-sink.h>
+
+G_BEGIN_DECLS
+
+EActivity *	e_source_util_remove		(ESource *source,
+						 EAlertSink *alert_sink);
+EActivity *	e_source_util_write		(ESource *source,
+						 EAlertSink *alert_sink);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_UTIL_H */
diff --git a/e-util/e-util.c b/e-util/e-util.c
index 7157351..38c1b85 100644
--- a/e-util/e-util.c
+++ b/e-util/e-util.c
@@ -53,7 +53,6 @@
 #include <camel/camel.h>
 #include <libedataserver/e-data-server-util.h>
 #include <libedataserver/e-categories.h>
-#include <libedataserver/e-source-list.h>
 
 #include "filter/e-filter-option.h"
 
@@ -1334,31 +1333,6 @@ e_util_get_searchable_categories (void)
 }
 
 /**
- * e_util_set_source_combo_box_list:
- * @source_combo_box: an #ESourceComboBox
- * @source_gconf_path: GConf path with sources to use in an #ESourceList
- *
- * Sets an #ESourceList of a given GConf path to an #ESourceComboBox.
- **/
-void
-e_util_set_source_combo_box_list (GtkWidget *source_combo_box,
-                                  const gchar *source_gconf_path)
-{
-	ESourceList *source_list;
-	GConfClient *gconf_client;
-
-	g_return_if_fail (source_combo_box != NULL);
-	g_return_if_fail (source_gconf_path != NULL);
-
-	gconf_client = gconf_client_get_default ();
-	source_list = e_source_list_new_for_gconf (
-		gconf_client, source_gconf_path);
-	g_object_set (source_combo_box, "source-list", source_list, NULL);
-	g_object_unref (source_list);
-	g_object_unref (gconf_client);
-}
-
-/**
  * e_binding_transform_color_to_string:
  * @binding: a #GBinding
  * @source_value: a #GValue of type #GDK_TYPE_COLOR
@@ -1435,7 +1409,7 @@ e_binding_transform_string_to_color (GBinding *binding,
  * @binding: a #GBinding
  * @source_value: a #GValue of type #E_TYPE_SOURCE
  * @target_value: a #GValue of type #G_TYPE_STRING
- * @source_list: an #ESourceList
+ * @registry: an #ESourceRegistry
  *
  * Transforms an #ESource object to its UID string.
  *
@@ -1445,14 +1419,14 @@ gboolean
 e_binding_transform_source_to_uid (GBinding *binding,
                                    const GValue *source_value,
                                    GValue *target_value,
-                                   ESourceList *source_list)
+                                   ESourceRegistry *registry)
 {
 	ESource *source;
 	const gchar *string;
 	gboolean success = FALSE;
 
 	g_return_val_if_fail (G_IS_BINDING (binding), FALSE);
-	g_return_val_if_fail (E_IS_SOURCE_LIST (source_list), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
 
 	source = g_value_get_object (source_value);
 	if (E_IS_SOURCE (source)) {
@@ -1469,34 +1443,34 @@ e_binding_transform_source_to_uid (GBinding *binding,
  * @binding: a #GBinding
  * @source_value: a #GValue of type #G_TYPE_STRING
  * @target_value: a #GValue of type #E_TYPE_SOURCe
- * @source_list: an #ESourceList
+ * @registry: an #ESourceRegistry
  *
  * Transforms an #ESource UID string to the corresponding #ESource object
- * in @source_list.
+ * in @registry.
  *
- * Returns: %TRUE if @source_list had an #ESource object with a matching
+ * Returns: %TRUE if @registry had an #ESource object with a matching
  *          UID string
  **/
 gboolean
 e_binding_transform_uid_to_source (GBinding *binding,
                                    const GValue *source_value,
                                    GValue *target_value,
-                                   ESourceList *source_list)
+                                   ESourceRegistry *registry)
 {
 	ESource *source;
 	const gchar *string;
 	gboolean success = FALSE;
 
 	g_return_val_if_fail (G_IS_BINDING (binding), FALSE);
-	g_return_val_if_fail (E_IS_SOURCE_LIST (source_list), FALSE);
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
 
 	string = g_value_get_string (source_value);
 	if (string == NULL || *string == '\0')
 		return FALSE;
 
-	source = e_source_list_peek_source_by_uid (source_list, string);
+	source = e_source_registry_ref_source (registry, string);
 	if (source != NULL) {
-		g_value_set_object (target_value, source);
+		g_value_take_object (target_value, source);
 		success = TRUE;
 	}
 
diff --git a/e-util/e-util.h b/e-util/e-util.h
index bd9ad2d..af22324 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -32,7 +32,7 @@
 #include <e-util/e-marshal.h>
 #include <e-util/e-util-enums.h>
 
-#include <libedataserver/e-source-list.h>
+#include <libedataserver/e-source-registry.h>
 
 /* e_get_user_data_dir() used to live here, so #include its new home
  * for backward-compatibility (not that we really care about that). */
@@ -131,10 +131,6 @@ GSList *	e_util_get_category_filter_options
 						(void);
 GList *		e_util_get_searchable_categories (void);
 
-void		e_util_set_source_combo_box_list
-						(GtkWidget *source_combo_box,
-						 const gchar *source_gconf_path);
-
 /* Useful GBinding transform functions */
 gboolean	e_binding_transform_color_to_string
 						(GBinding *binding,
@@ -150,12 +146,12 @@ gboolean	e_binding_transform_source_to_uid
 						(GBinding *binding,
 						 const GValue *source_value,
 						 GValue *target_value,
-						 ESourceList *source_list);
+						 ESourceRegistry *registry);
 gboolean	e_binding_transform_uid_to_source
 						(GBinding *binding,
 						 const GValue *source_value,
 						 GValue *target_value,
-						 ESourceList *source_list);
+						 ESourceRegistry *registry);
 
 G_END_DECLS
 
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index 411dcb3..bd0bea5 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -5,7 +5,6 @@ widgetsincludedir = $(privincludedir)/misc
 ui_DATA = e-send-options.ui
 
 widgetsinclude_HEADERS =			\
-	e-account-combo-box.h			\
 	e-action-combo-box.h			\
 	e-activity-bar.h			\
 	e-activity-proxy.h			\
@@ -38,6 +37,9 @@ widgetsinclude_HEADERS =			\
 	e-image-chooser.h			\
 	e-import-assistant.h			\
 	e-interval-chooser.h			\
+	e-mail-account-manager.h		\
+	e-mail-account-tree-view.h		\
+	e-mail-identity-combo-box.h		\
 	e-map.h					\
 	e-menu-tool-action.h			\
 	e-menu-tool-button.h			\
@@ -92,7 +94,6 @@ libemiscwidgets_la_CPPFLAGS =						\
 
 libemiscwidgets_la_SOURCES =			\
 	$(widgetsinclude_HEADERS)		\
-	e-account-combo-box.c			\
 	e-action-combo-box.c			\
 	e-activity-bar.c			\
 	e-activity-proxy.c			\
@@ -125,6 +126,9 @@ libemiscwidgets_la_SOURCES =			\
 	e-image-chooser.c			\
 	e-import-assistant.c			\
 	e-interval-chooser.c			\
+	e-mail-account-manager.c		\
+	e-mail-account-tree-view.c		\
+	e-mail-identity-combo-box.c		\
 	e-map.c					\
 	e-menu-tool-action.c			\
 	e-menu-tool-button.c			\
diff --git a/widgets/misc/e-mail-account-manager.c b/widgets/misc/e-mail-account-manager.c
new file mode 100644
index 0000000..fd5e999
--- /dev/null
+++ b/widgets/misc/e-mail-account-manager.c
@@ -0,0 +1,384 @@
+/*
+ * e-mail-account-manager.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-mail-account-manager.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "e-mail-account-tree-view.h"
+
+#define E_MAIL_ACCOUNT_MANAGER_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_MAIL_ACCOUNT_MANAGER, EMailAccountManagerPrivate))
+
+struct _EMailAccountManagerPrivate {
+	ESourceRegistry *registry;
+
+	GtkWidget *tree_view;		/* not referenced */
+	GtkWidget *add_button;		/* not referenced */
+	GtkWidget *edit_button;		/* not referenced */
+	GtkWidget *delete_button;	/* not referenced */
+	GtkWidget *default_button;	/* not referenced */
+};
+
+enum {
+	PROP_0,
+	PROP_REGISTRY
+};
+
+enum {
+	ADD_ACCOUNT,
+	EDIT_ACCOUNT,
+	DELETE_ACCOUNT,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (
+	EMailAccountManager,
+	e_mail_account_manager,
+	GTK_TYPE_TABLE)
+
+static gboolean
+mail_account_manager_key_press_event_cb (EMailAccountManager *manager,
+                                         GdkEventKey *event)
+{
+	if (event->keyval == GDK_KEY_Delete) {
+		e_mail_account_manager_delete_account (manager);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+mail_account_manager_selection_changed_cb (EMailAccountManager *manager,
+                                           GtkTreeSelection *selection)
+{
+	EMailAccountTreeView *tree_view;
+	ESourceRegistry *registry;
+	ESource *default_source;
+	ESource *source;
+	GtkWidget *add_button;
+	GtkWidget *edit_button;
+	GtkWidget *delete_button;
+	GtkWidget *default_button;
+	gboolean sensitive;
+
+	add_button = manager->priv->add_button;
+	edit_button = manager->priv->edit_button;
+	delete_button = manager->priv->delete_button;
+	default_button = manager->priv->default_button;
+
+	registry = e_mail_account_manager_get_registry (manager);
+	tree_view = E_MAIL_ACCOUNT_TREE_VIEW (manager->priv->tree_view);
+
+	source = e_mail_account_tree_view_get_selected_source (tree_view);
+	default_source = e_source_registry_get_default_mail_account (registry);
+
+	if (source == NULL)
+		gtk_widget_grab_focus (add_button);
+
+	sensitive = (source != NULL);
+	gtk_widget_set_sensitive (edit_button, sensitive);
+
+	sensitive = (source != NULL);
+	gtk_widget_set_sensitive (delete_button, sensitive);
+
+	sensitive = (source != NULL && source != default_source);
+	gtk_widget_set_sensitive (default_button, sensitive);
+}
+
+static void
+mail_account_manager_set_registry (EMailAccountManager *manager,
+                                   ESourceRegistry *registry)
+{
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+	g_return_if_fail (manager->priv->registry == NULL);
+
+	manager->priv->registry = g_object_ref (registry);
+}
+
+static void
+mail_account_manager_set_property (GObject *object,
+                                   guint property_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_REGISTRY:
+			mail_account_manager_set_registry (
+				E_MAIL_ACCOUNT_MANAGER (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_account_manager_get_property (GObject *object,
+                                   guint property_id,
+                                   GValue *value,
+                                   GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_REGISTRY:
+			g_value_set_object (
+				value,
+				e_mail_account_manager_get_registry (
+				E_MAIL_ACCOUNT_MANAGER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_account_manager_dispose (GObject *object)
+{
+	EMailAccountManagerPrivate *priv;
+
+	priv = E_MAIL_ACCOUNT_MANAGER_GET_PRIVATE (object);
+
+	if (priv->registry != NULL) {
+		g_object_unref (priv->registry);
+		priv->registry = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_mail_account_manager_parent_class)->dispose (object);
+}
+
+static void
+mail_account_manager_constructed (GObject *object)
+{
+	EMailAccountManager *manager;
+	ESourceRegistry *registry;
+	GtkTreeSelection *selection;
+	GtkWidget *container;
+	GtkWidget *widget;
+
+	manager = E_MAIL_ACCOUNT_MANAGER (object);
+	registry = e_mail_account_manager_get_registry (manager);
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_mail_account_manager_parent_class)->
+		constructed (object);
+
+	gtk_table_resize (GTK_TABLE (manager), 1, 2);
+	gtk_table_set_col_spacings (GTK_TABLE (manager), 6);
+	gtk_table_set_row_spacings (GTK_TABLE (manager), 12);
+
+	container = GTK_WIDGET (manager);
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (
+		GTK_SCROLLED_WINDOW (widget),
+		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (
+		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+	gtk_table_attach (
+		GTK_TABLE (container), widget, 0, 1, 0, 1,
+		GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = e_mail_account_tree_view_new (registry);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	manager->priv->tree_view = widget;  /* not referenced */
+	gtk_widget_show (widget);
+
+	g_signal_connect_swapped (
+		widget, "key-press-event",
+		G_CALLBACK (mail_account_manager_key_press_event_cb),
+		manager);
+
+	g_signal_connect_swapped (
+		widget, "row-activated",
+		G_CALLBACK (e_mail_account_manager_edit_account),
+		manager);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+
+	g_signal_connect_swapped (
+		selection, "changed",
+		G_CALLBACK (mail_account_manager_selection_changed_cb),
+		manager);
+
+	container = GTK_WIDGET (manager);
+
+	widget = gtk_vbutton_box_new ();
+	gtk_button_box_set_layout (
+		GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_START);
+	gtk_box_set_spacing (GTK_BOX (widget), 6);
+	gtk_table_attach (
+		GTK_TABLE (container), widget,
+		1, 2, 0, 2, 0, GTK_FILL, 0, 0);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_button_new_from_stock (GTK_STOCK_ADD);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	manager->priv->add_button = widget;  /* not referenced */
+	gtk_widget_show (widget);
+
+	g_signal_connect_swapped (
+		widget, "clicked",
+		G_CALLBACK (e_mail_account_manager_add_account), manager);
+
+	widget = gtk_button_new_from_stock (GTK_STOCK_EDIT);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	manager->priv->edit_button = widget;  /* not referenced */
+	gtk_widget_show (widget);
+
+	g_signal_connect_swapped (
+		widget, "clicked",
+		G_CALLBACK (e_mail_account_manager_edit_account), manager);
+
+	widget = gtk_button_new_from_stock (GTK_STOCK_DELETE);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	manager->priv->delete_button = widget;  /* not referenced */
+	gtk_widget_show (widget);
+
+	g_signal_connect_swapped (
+		widget, "clicked",
+		G_CALLBACK (e_mail_account_manager_delete_account), manager);
+
+	widget = gtk_button_new_with_mnemonic (_("De_fault"));
+	gtk_button_set_image (
+		GTK_BUTTON (widget),
+		gtk_image_new_from_icon_name (
+		"emblem-default", GTK_ICON_SIZE_BUTTON));
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	manager->priv->default_button = widget;  /* not referenced */
+	gtk_widget_show (widget);
+
+	g_signal_connect_swapped (
+		widget, "clicked",
+		G_CALLBACK (e_mail_account_tree_view_enable_selected),
+		manager->priv->tree_view);
+}
+
+static void
+e_mail_account_manager_class_init (EMailAccountManagerClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (EMailAccountManagerPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = mail_account_manager_set_property;
+	object_class->get_property = mail_account_manager_get_property;
+	object_class->dispose = mail_account_manager_dispose;
+	object_class->constructed = mail_account_manager_constructed;
+
+	/* XXX If we moved the account editor to /widgets/misc we
+	 *     could handle adding and editing accounts directly. */
+
+	g_object_class_install_property (
+		object_class,
+		PROP_REGISTRY,
+		g_param_spec_object (
+			"registry",
+			"Registry",
+			NULL,
+			E_TYPE_SOURCE_REGISTRY,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY));
+
+	signals[ADD_ACCOUNT] = g_signal_new (
+		"add-account",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EMailAccountManagerClass, add_account),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	signals[EDIT_ACCOUNT] = g_signal_new (
+		"edit-account",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EMailAccountManagerClass, edit_account),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	signals[DELETE_ACCOUNT] = g_signal_new (
+		"delete-account",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EMailAccountManagerClass, delete_account),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+}
+
+static void
+e_mail_account_manager_init (EMailAccountManager *manager)
+{
+	manager->priv = E_MAIL_ACCOUNT_MANAGER_GET_PRIVATE (manager);
+}
+
+GtkWidget *
+e_mail_account_manager_new (ESourceRegistry *registry)
+{
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	return g_object_new (
+		E_TYPE_MAIL_ACCOUNT_MANAGER,
+		"registry", registry, NULL);
+}
+
+void
+e_mail_account_manager_add_account (EMailAccountManager *manager)
+{
+	g_return_if_fail (E_IS_MAIL_ACCOUNT_MANAGER (manager));
+
+	g_signal_emit (manager, signals[ADD_ACCOUNT], 0);
+}
+
+void
+e_mail_account_manager_edit_account (EMailAccountManager *manager)
+{
+	g_return_if_fail (E_IS_MAIL_ACCOUNT_MANAGER (manager));
+
+	g_signal_emit (manager, signals[EDIT_ACCOUNT], 0);
+}
+
+void
+e_mail_account_manager_delete_account (EMailAccountManager *manager)
+{
+	g_return_if_fail (E_IS_MAIL_ACCOUNT_MANAGER (manager));
+
+	g_signal_emit (manager, signals[DELETE_ACCOUNT], 0);
+}
+
+ESourceRegistry *
+e_mail_account_manager_get_registry (EMailAccountManager *manager)
+{
+	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_MANAGER (manager), NULL);
+
+	return manager->priv->registry;
+}
diff --git a/widgets/misc/e-mail-account-manager.h b/widgets/misc/e-mail-account-manager.h
new file mode 100644
index 0000000..4afbe70
--- /dev/null
+++ b/widgets/misc/e-mail-account-manager.h
@@ -0,0 +1,77 @@
+/*
+ * e-mail-account-manager.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_ACCOUNT_MANAGER_H
+#define E_MAIL_ACCOUNT_MANAGER_H
+
+#include <gtk/gtk.h>
+#include <libedataserver/e-source-registry.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_ACCOUNT_MANAGER \
+	(e_mail_account_manager_get_type ())
+#define E_MAIL_ACCOUNT_MANAGER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_MAIL_ACCOUNT_MANAGER, EMailAccountManager))
+#define E_MAIL_ACCOUNT_MANAGER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_MAIL_ACCOUNT_MANAGER, EMailAccountManagerClass))
+#define E_IS_MAIL_ACCOUNT_MANAGER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_MAIL_ACCOUNT_MANAGER))
+#define E_IS_MAIL_ACCOUNT_MANAGER_CLASS(cls) \
+	(G_TYPE_CHECK_INSTANCE_CLASS \
+	((cls), E_TYPE_MAIL_ACCOUNT_MANAGER))
+#define E_MAIL_ACCOUNT_MANAGER_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_MAIL_ACCOUNT_MANAGER, EMailAccountManagerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailAccountManager EMailAccountManager;
+typedef struct _EMailAccountManagerClass EMailAccountManagerClass;
+typedef struct _EMailAccountManagerPrivate EMailAccountManagerPrivate;
+
+struct _EMailAccountManager {
+	GtkTable parent;
+	EMailAccountManagerPrivate *priv;
+};
+
+struct _EMailAccountManagerClass {
+	GtkTableClass parent_class;
+
+	void		(*add_account)		(EMailAccountManager *manager);
+	void		(*edit_account)		(EMailAccountManager *manager);
+	void		(*delete_account)	(EMailAccountManager *manager);
+};
+
+GType		e_mail_account_manager_get_type	(void) G_GNUC_CONST;
+GtkWidget *	e_mail_account_manager_new	(ESourceRegistry *registry);
+void		e_mail_account_manager_add_account
+						(EMailAccountManager *manager);
+void		e_mail_account_manager_edit_account
+						(EMailAccountManager *manager);
+void		e_mail_account_manager_delete_account
+						(EMailAccountManager *manager);
+ESourceRegistry *
+		e_mail_account_manager_get_registry
+						(EMailAccountManager *manager);
+
+G_END_DECLS
+
+#endif /* E_MAIL_ACCOUNT_MANAGER_H */
diff --git a/widgets/misc/e-mail-account-tree-view.c b/widgets/misc/e-mail-account-tree-view.c
new file mode 100644
index 0000000..45d6a0d
--- /dev/null
+++ b/widgets/misc/e-mail-account-tree-view.c
@@ -0,0 +1,575 @@
+/*
+ * e-mail-account-tree-view.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-mail-account-tree-view.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include <libedataserver/e-source-mail-account.h>
+
+#define E_MAIL_ACCOUNT_TREE_VIEW_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_MAIL_ACCOUNT_TREE_VIEW, EMailAccountTreeViewPrivate))
+
+#define SOURCE_IS_MAIL_ACCOUNT(source) \
+	(e_source_has_extension ((source), E_SOURCE_EXTENSION_MAIL_ACCOUNT))
+
+struct _EMailAccountTreeViewPrivate {
+	ESourceRegistry *registry;
+	guint refresh_idle_id;
+};
+
+enum {
+	PROP_0,
+	PROP_REGISTRY
+};
+
+enum {
+	ENABLE_SELECTED,
+	DISABLE_SELECTED,
+	LAST_SIGNAL
+};
+
+enum {
+	COLUMN_DISPLAY_NAME,
+	COLUMN_BACKEND_NAME,
+	COLUMN_DEFAULT,
+	COLUMN_ENABLED,
+	COLUMN_UID
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (
+	EMailAccountTreeView,
+	e_mail_account_tree_view,
+	GTK_TYPE_TREE_VIEW)
+
+static gboolean
+mail_account_tree_view_refresh_idle_cb (EMailAccountTreeView *tree_view)
+{
+	/* The refresh function will clear the idle ID. */
+	e_mail_account_tree_view_refresh (tree_view);
+
+	return FALSE;
+}
+
+static void
+mail_account_tree_view_registry_changed (ESourceRegistry *registry,
+                                         ESource *source,
+                                         EMailAccountTreeView *tree_view)
+{
+	/* If the ESource in question has a "Mail Account" extension,
+	 * schedule a refresh of the tree model.  Otherwise ignore it.
+	 * We use an idle callback to limit how frequently we refresh
+	 * the tree model, in case the registry is emitting lots of
+	 * signals at once. */
+
+	if (!SOURCE_IS_MAIL_ACCOUNT (source))
+		return;
+
+	if (tree_view->priv->refresh_idle_id > 0)
+		return;
+
+	tree_view->priv->refresh_idle_id = gdk_threads_add_idle (
+		(GSourceFunc) mail_account_tree_view_refresh_idle_cb,
+		tree_view);
+}
+
+static void
+mail_account_tree_view_enabled_toggled_cb (GtkCellRendererToggle *cell_renderer,
+                                           const gchar *path_string,
+                                           EMailAccountTreeView *tree_view)
+{
+	GtkTreeSelection *selection;
+	GtkTreePath *path;
+
+	/* Chain the selection first so we enable or disable the
+	 * correct account. */
+	path = gtk_tree_path_new_from_string (path_string);
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+	gtk_tree_selection_select_path (selection, path);
+	gtk_tree_path_free (path);
+
+	if (gtk_cell_renderer_toggle_get_active (cell_renderer))
+		e_mail_account_tree_view_disable_selected (tree_view);
+	else
+		e_mail_account_tree_view_enable_selected (tree_view);
+}
+
+static void
+mail_account_tree_view_set_registry (EMailAccountTreeView *tree_view,
+                                     ESourceRegistry *registry)
+{
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+	g_return_if_fail (tree_view->priv->registry == NULL);
+
+	tree_view->priv->registry = g_object_ref (registry);
+
+	g_signal_connect (
+		registry, "source-added",
+		G_CALLBACK (mail_account_tree_view_registry_changed),
+		tree_view);
+
+	g_signal_connect (
+		registry, "source-changed",
+		G_CALLBACK (mail_account_tree_view_registry_changed),
+		tree_view);
+
+	g_signal_connect (
+		registry, "source-removed",
+		G_CALLBACK (mail_account_tree_view_registry_changed),
+		tree_view);
+}
+
+static void
+mail_account_tree_view_set_property (GObject *object,
+                                     guint property_id,
+                                     const GValue *value,
+                                     GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_REGISTRY:
+			mail_account_tree_view_set_registry (
+				E_MAIL_ACCOUNT_TREE_VIEW (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_account_tree_view_get_property (GObject *object,
+                                     guint property_id,
+                                     GValue *value,
+                                     GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_REGISTRY:
+			g_value_set_object (
+				value,
+				e_mail_account_tree_view_get_registry (
+				E_MAIL_ACCOUNT_TREE_VIEW (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_account_tree_view_dispose (GObject *object)
+{
+	EMailAccountTreeViewPrivate *priv;
+
+	priv = E_MAIL_ACCOUNT_TREE_VIEW_GET_PRIVATE (object);
+
+	if (priv->registry != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->registry, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (priv->registry);
+		priv->registry = NULL;
+	}
+
+	if (priv->refresh_idle_id > 0) {
+		g_source_remove (priv->refresh_idle_id);
+		priv->refresh_idle_id = 0;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_mail_account_tree_view_parent_class)->
+		dispose (object);
+}
+
+static void
+mail_account_tree_view_constructed (GObject *object)
+{
+	GtkTreeView *tree_view;
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *cell_renderer;
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_mail_account_tree_view_parent_class)->
+		constructed (object);
+
+	tree_view = GTK_TREE_VIEW (object);
+	gtk_tree_view_set_headers_visible (tree_view, TRUE);
+
+	/* Column: Enabled */
+
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_set_expand (column, FALSE);
+	gtk_tree_view_column_set_title (column, _("Enabled"));
+
+	cell_renderer = gtk_cell_renderer_toggle_new ();
+	gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
+
+	g_signal_connect (
+		cell_renderer, "toggled",
+		G_CALLBACK (mail_account_tree_view_enabled_toggled_cb),
+		tree_view);
+
+	gtk_tree_view_column_add_attribute (
+		column, cell_renderer, "active", COLUMN_ENABLED);
+
+	gtk_tree_view_append_column (tree_view, column);
+
+	/* Column: Account Name */
+
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_set_expand (column, TRUE);
+	gtk_tree_view_column_set_title (column, _("Account Name"));
+
+	cell_renderer = gtk_cell_renderer_text_new ();
+	g_object_set (cell_renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+	gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
+
+	gtk_tree_view_column_add_attribute (
+		column, cell_renderer, "text", COLUMN_DISPLAY_NAME);
+
+	cell_renderer = gtk_cell_renderer_text_new ();
+	g_object_set (cell_renderer, "text", _("Default"), NULL);
+	gtk_tree_view_column_pack_end (column, cell_renderer, FALSE);
+
+	gtk_tree_view_column_add_attribute (
+		column, cell_renderer, "visible", COLUMN_DISPLAY_NAME);
+
+	cell_renderer = gtk_cell_renderer_pixbuf_new ();
+	g_object_set (
+		cell_renderer, "icon-name", "emblem-default",
+		"stock-size", GTK_ICON_SIZE_MENU, NULL);
+	gtk_tree_view_column_pack_end (column, cell_renderer, FALSE);
+
+	gtk_tree_view_column_add_attribute (
+		column, cell_renderer, "visible", COLUMN_DISPLAY_NAME);
+
+	gtk_tree_view_append_column (tree_view, column);
+
+	/* Column: Type */
+
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_set_expand (column, FALSE);
+	gtk_tree_view_column_set_title (column, _("Type"));
+
+	cell_renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
+
+	gtk_tree_view_column_add_attribute (
+		column, cell_renderer, "text", COLUMN_BACKEND_NAME);
+
+	gtk_tree_view_append_column (tree_view, column);
+
+	e_mail_account_tree_view_refresh (E_MAIL_ACCOUNT_TREE_VIEW (object));
+}
+
+static void
+mail_account_tree_view_enable_selected (EMailAccountTreeView *tree_view)
+{
+	ESource *source;
+	ESourceMailAccount *mail_account;
+	const gchar *extension_name;
+	GError *error = NULL;
+
+	source = e_mail_account_tree_view_get_selected_source (tree_view);
+
+	if (source == NULL)
+		return;
+
+	/* The source should already be a mail account. */
+	g_return_if_fail (SOURCE_IS_MAIL_ACCOUNT (source));
+
+	extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+	mail_account = e_source_get_extension (source, extension_name);
+
+	/* Avoid unnecessary signal emissions and disk writes. */
+	if (e_source_mail_account_get_enabled (mail_account))
+		return;
+
+	e_source_mail_account_set_enabled (mail_account, TRUE);
+
+	if (!e_source_sync (source, &error)) {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+}
+
+static void
+mail_account_tree_view_disable_selected (EMailAccountTreeView *tree_view)
+{
+	ESource *source;
+	ESourceMailAccount *mail_account;
+	const gchar *extension_name;
+	GError *error = NULL;
+
+	source = e_mail_account_tree_view_get_selected_source (tree_view);
+
+	if (source == NULL)
+		return;
+
+	/* The source should already be a mail account. */
+	g_return_if_fail (SOURCE_IS_MAIL_ACCOUNT (source));
+
+	extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+	mail_account = e_source_get_extension (source, extension_name);
+
+	/* Avoid unnecessary signal emissions and disk writes. */
+	if (!e_source_mail_account_get_enabled (mail_account))
+		return;
+
+	e_source_mail_account_set_enabled (mail_account, FALSE);
+
+	if (!e_source_sync (source, &error)) {
+		g_warning ("%s", error->message);
+		g_error_free (error);
+	}
+}
+
+static void
+e_mail_account_tree_view_class_init (EMailAccountTreeViewClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (EMailAccountTreeViewPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = mail_account_tree_view_set_property;
+	object_class->get_property = mail_account_tree_view_get_property;
+	object_class->dispose = mail_account_tree_view_dispose;
+	object_class->constructed = mail_account_tree_view_constructed;
+
+	class->enable_selected = mail_account_tree_view_enable_selected;
+	class->disable_selected = mail_account_tree_view_disable_selected;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_REGISTRY,
+		g_param_spec_object (
+			"registry",
+			"Registry",
+			NULL,
+			E_TYPE_SOURCE_REGISTRY,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY |
+			G_PARAM_STATIC_STRINGS));
+
+	signals[ENABLE_SELECTED] = g_signal_new (
+		"enable-selected",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EMailAccountTreeViewClass, enable_selected),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	signals[DISABLE_SELECTED] = g_signal_new (
+		"disable-selected",
+		G_TYPE_FROM_CLASS (class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EMailAccountTreeViewClass, disable_selected),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+}
+
+static void
+e_mail_account_tree_view_init (EMailAccountTreeView *tree_view)
+{
+	tree_view->priv = E_MAIL_ACCOUNT_TREE_VIEW_GET_PRIVATE (tree_view);
+}
+
+GtkWidget *
+e_mail_account_tree_view_new (ESourceRegistry *registry)
+{
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	return g_object_new (
+		E_TYPE_MAIL_ACCOUNT_TREE_VIEW,
+		"registry", registry, NULL);
+}
+
+void
+e_mail_account_tree_view_refresh (EMailAccountTreeView *tree_view)
+{
+	ESourceRegistry *registry;
+	GtkTreeModel *tree_model;
+	ESource *default_source;
+	ESource *source;
+	GList *list, *link;
+	const gchar *extension_name;
+	gchar *saved_uid = NULL;
+
+	g_return_if_fail (E_IS_MAIL_ACCOUNT_TREE_VIEW (tree_view));
+
+	if (tree_view->priv->refresh_idle_id > 0) {
+		g_source_remove (tree_view->priv->refresh_idle_id);
+		tree_view->priv->refresh_idle_id = 0;
+	}
+
+	registry = e_mail_account_tree_view_get_registry (tree_view);
+	tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+
+	source = e_mail_account_tree_view_get_selected_source (tree_view);
+	if (source != NULL)
+		saved_uid = g_strdup (e_source_get_uid (source));
+
+	default_source = e_source_registry_get_default_mail_account (registry);
+
+	gtk_list_store_clear (GTK_LIST_STORE (tree_model));
+
+	extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+	list = e_source_registry_list_sources (registry, extension_name);
+
+	for (link = list; link != NULL; link = g_list_next (link)) {
+		ESourceMailAccount *mail_account;
+		GtkTreeIter iter;
+		const gchar *backend_name;
+		const gchar *display_name;
+		const gchar *uid;
+		gboolean is_default;
+		gboolean is_enabled;
+
+		source = E_SOURCE (link->data);
+		mail_account = e_source_get_extension (source, extension_name);
+
+		display_name = e_source_get_display_name (source);
+		backend_name = e_source_get_backend_name (source);
+		is_default = e_source_equal (source, default_source);
+		is_enabled = e_source_mail_account_get_enabled (mail_account);
+		uid = e_source_get_uid (source);
+
+		gtk_list_store_append (GTK_LIST_STORE (tree_model), &iter);
+
+		gtk_list_store_set (
+			GTK_LIST_STORE (tree_model), &iter,
+			COLUMN_DISPLAY_NAME, display_name,
+			COLUMN_BACKEND_NAME, backend_name,
+			COLUMN_DEFAULT, is_default,
+			COLUMN_ENABLED, is_enabled,
+			COLUMN_UID, uid, -1);
+	}
+
+	g_list_free (list);
+
+	/* Try and restore the previous selected source,
+	 * or else just pick the default mail account. */
+
+	source = NULL;
+
+	if (saved_uid != NULL) {
+		source = e_source_registry_lookup_by_uid (registry, saved_uid);
+		g_free (saved_uid);
+	}
+
+	if (source == NULL)
+		source = default_source;
+
+	if (source != NULL)
+		e_mail_account_tree_view_set_selected_source (
+			tree_view, source);
+}
+
+void
+e_mail_account_tree_view_enable_selected (EMailAccountTreeView *tree_view)
+{
+	g_return_if_fail (E_IS_MAIL_ACCOUNT_TREE_VIEW (tree_view));
+
+	g_signal_emit (tree_view, signals[ENABLE_SELECTED], 0);
+}
+
+void
+e_mail_account_tree_view_disable_selected (EMailAccountTreeView *tree_view)
+{
+	g_return_if_fail (E_IS_MAIL_ACCOUNT_TREE_VIEW (tree_view));
+
+	g_signal_emit (tree_view, signals[DISABLE_SELECTED], 0);
+}
+
+ESourceRegistry *
+e_mail_account_tree_view_get_registry (EMailAccountTreeView *tree_view)
+{
+	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_TREE_VIEW (tree_view), NULL);
+
+	return tree_view->priv->registry;
+}
+
+ESource *
+e_mail_account_tree_view_get_selected_source (EMailAccountTreeView *tree_view)
+{
+	ESourceRegistry *registry;
+	GtkTreeSelection *selection;
+	GtkTreeModel *tree_model;
+	GtkTreeIter iter;
+	ESource *source;
+	gchar *uid;
+
+	g_return_val_if_fail (E_IS_MAIL_ACCOUNT_TREE_VIEW (tree_view), NULL);
+
+	registry = e_mail_account_tree_view_get_registry (tree_view);
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+
+	if (!gtk_tree_selection_get_selected (selection, &tree_model, &iter))
+		return NULL;
+
+	gtk_tree_model_get (tree_model, &iter, COLUMN_UID, &uid, -1);
+	source = e_source_registry_lookup_by_uid (registry, uid);
+	g_free (uid);
+
+	return source;
+}
+
+void
+e_mail_account_tree_view_set_selected_source (EMailAccountTreeView *tree_view,
+                                              ESource *source)
+{
+	ESourceRegistry *registry;
+	GtkTreeSelection *selection;
+	GtkTreeModel *tree_model;
+	GtkTreeIter iter;
+	gboolean valid;
+
+	g_return_if_fail (E_IS_MAIL_ACCOUNT_TREE_VIEW (tree_view));
+	g_return_if_fail (E_IS_SOURCE (source));
+
+	/* It is a programming error to pass an ESource that has no
+	 * "Mail Account" extension. */
+	g_return_if_fail (SOURCE_IS_MAIL_ACCOUNT (source));
+
+	registry = e_mail_account_tree_view_get_registry (tree_view);
+	tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+
+	valid = gtk_tree_model_get_iter_first (tree_model, &iter);
+
+	while (valid) {
+		ESource *candidate;
+		gchar *uid;
+
+		gtk_tree_model_get (tree_model, &iter, COLUMN_UID, &uid, -1);
+		candidate = e_source_registry_lookup_by_uid (registry, uid);
+		g_free (uid);
+
+		if (candidate != NULL && e_source_equal (source, candidate)) {
+			gtk_tree_selection_select_iter (selection, &iter);
+			break;
+		}
+
+		valid = gtk_tree_model_iter_next (tree_model, &iter);
+	}
+}
diff --git a/widgets/misc/e-mail-account-tree-view.h b/widgets/misc/e-mail-account-tree-view.h
new file mode 100644
index 0000000..4bf9099
--- /dev/null
+++ b/widgets/misc/e-mail-account-tree-view.h
@@ -0,0 +1,83 @@
+/*
+ * e-mail-account-tree-view.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_ACCOUNT_TREE_VIEW_H
+#define E_MAIL_ACCOUNT_TREE_VIEW_H
+
+#include <gtk/gtk.h>
+#include <libedataserver/e-source-registry.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_ACCOUNT_TREE_VIEW \
+	(e_mail_account_tree_view_get_type ())
+#define E_MAIL_ACCOUNT_TREE_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_MAIL_ACCOUNT_TREE_VIEW, EMailAccountTreeView))
+#define E_MAIL_ACCOUNT_TREE_VIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_MAIL_ACCOUNT_TREE_VIEW, EMailAccountTreeViewClass))
+#define E_IS_MAIL_ACCOUNT_TREE_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_MAIL_ACCOUNT_TREE_VIEW))
+#define E_IS_MAIL_ACCOUNT_TREE_VIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_MAIL_ACCOUNT_TREE_VIEW))
+#define E_MAIL_ACCOUNT_TREE_VIEW_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_MAIL_ACCOUNT_TREE_VIEW, EMailAccountTreeViewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailAccountTreeView EMailAccountTreeView;
+typedef struct _EMailAccountTreeViewClass EMailAccountTreeViewClass;
+typedef struct _EMailAccountTreeViewPrivate EMailAccountTreeViewPrivate;
+
+struct _EMailAccountTreeView {
+	GtkTreeView parent;
+	EMailAccountTreeViewPrivate *priv;
+};
+
+struct _EMailAccountTreeViewClass {
+	GtkTreeViewClass parent_class;
+
+	void	(*enable_selected)	(EMailAccountTreeView *tree_view);
+	void	(*disable_selected)	(EMailAccountTreeView *tree_view);
+};
+
+GType		e_mail_account_tree_view_get_type
+					(void) G_GNUC_CONST;
+GtkWidget *	e_mail_account_tree_view_new
+					(ESourceRegistry *registry);
+void		e_mail_account_tree_view_refresh
+					(EMailAccountTreeView *tree_view);
+void		e_mail_account_tree_view_enable_selected
+					(EMailAccountTreeView *tree_view);
+void		e_mail_account_tree_view_disable_selected
+					(EMailAccountTreeView *tree_view);
+ESourceRegistry *
+		e_mail_account_tree_view_get_registry
+					(EMailAccountTreeView *tree_view);
+ESource *	e_mail_account_tree_view_get_selected_source
+					(EMailAccountTreeView *tree_view);
+void		e_mail_account_tree_view_set_selected_source
+					(EMailAccountTreeView *tree_view,
+					 ESource *source);
+
+G_END_DECLS
+
+#endif /* E_MAIL_ACCOUNT_TREE_VIEW_H */
diff --git a/widgets/misc/e-mail-identity-combo-box.c b/widgets/misc/e-mail-identity-combo-box.c
new file mode 100644
index 0000000..4b83015
--- /dev/null
+++ b/widgets/misc/e-mail-identity-combo-box.c
@@ -0,0 +1,374 @@
+/*
+ * e-mail-identity-combo-box.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-mail-identity-combo-box.h"
+
+#include <libedataserver/e-source-mail-account.h>
+#include <libedataserver/e-source-mail-identity.h>
+
+#define E_MAIL_IDENTITY_COMBO_BOX_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_MAIL_IDENTITY_COMBO_BOX, EMailIdentityComboBoxPrivate))
+
+#define SOURCE_IS_MAIL_IDENTITY(source) \
+	(e_source_has_extension ((source), E_SOURCE_EXTENSION_MAIL_IDENTITY))
+
+struct _EMailIdentityComboBoxPrivate {
+	ESourceRegistry *registry;
+	guint refresh_idle_id;
+};
+
+enum {
+	PROP_0,
+	PROP_REGISTRY
+};
+
+enum {
+	COLUMN_DISPLAY_NAME,
+	COLUMN_UID
+};
+
+G_DEFINE_TYPE (
+	EMailIdentityComboBox,
+	e_mail_identity_combo_box,
+	GTK_TYPE_COMBO_BOX)
+
+static gboolean
+mail_identity_combo_box_refresh_idle_cb (EMailIdentityComboBox *combo_box)
+{
+	/* The refresh function will clear the idle ID. */
+	e_mail_identity_combo_box_refresh (combo_box);
+
+	return FALSE;
+}
+
+static void
+mail_identity_combo_box_registry_changed (ESourceRegistry *registry,
+                                          ESource *source,
+                                          EMailIdentityComboBox *combo_box)
+{
+	/* If the ESource in question has a "Mail Identity" extension,
+	 * schedule a refresh of the tree model.  Otherwise ignore it.
+	 * We use an idle callback to limit how frequently we refresh
+	 * the tree model, in case the registry is emitting lots of
+	 * signals at once. */
+
+	if (!SOURCE_IS_MAIL_IDENTITY (source))
+		return;
+
+	if (combo_box->priv->refresh_idle_id > 0)
+		return;
+
+	combo_box->priv->refresh_idle_id = gdk_threads_add_idle (
+		(GSourceFunc) mail_identity_combo_box_refresh_idle_cb,
+		combo_box);
+}
+
+static ESource *
+mail_identity_combo_box_get_default (EMailIdentityComboBox *combo_box)
+{
+	ESource *source;
+	ESourceRegistry *registry;
+	ESourceMailAccount *mail_account;
+	const gchar *extension_name;
+	const gchar *uid;
+
+	extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+	registry = e_mail_identity_combo_box_get_registry (combo_box);
+	source = e_source_registry_get_default_mail_account (registry);
+
+	if (source == NULL)
+		return NULL;
+
+	if (!e_source_has_extension (source, extension_name))
+		return NULL;
+
+	mail_account = e_source_get_extension (source, extension_name);
+	uid = e_source_mail_account_get_identity (mail_account);
+
+	if (uid == NULL)
+		return NULL;
+
+	return e_source_registry_lookup_by_uid (registry, uid);
+}
+
+static void
+mail_identity_combo_box_set_registry (EMailIdentityComboBox *combo_box,
+                                      ESourceRegistry *registry)
+{
+	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+	g_return_if_fail (combo_box->priv->registry == NULL);
+
+	combo_box->priv->registry = g_object_ref (registry);
+
+	g_signal_connect (
+		registry, "source-added",
+		G_CALLBACK (mail_identity_combo_box_registry_changed),
+		combo_box);
+
+	g_signal_connect (
+		registry, "source-changed",
+		G_CALLBACK (mail_identity_combo_box_registry_changed),
+		combo_box);
+
+	g_signal_connect (
+		registry, "source-removed",
+		G_CALLBACK (mail_identity_combo_box_registry_changed),
+		combo_box);
+}
+
+static void
+mail_identity_combo_box_set_property (GObject *object,
+                                      guint property_id,
+                                      const GValue *value,
+                                      GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_REGISTRY:
+			mail_identity_combo_box_set_registry (
+				E_MAIL_IDENTITY_COMBO_BOX (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_identity_combo_box_get_property (GObject *object,
+                                      guint property_id,
+                                      GValue *value,
+                                      GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_REGISTRY:
+			g_value_set_object (
+				value,
+				e_mail_identity_combo_box_get_registry (
+				E_MAIL_IDENTITY_COMBO_BOX (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_identity_combo_box_dispose (GObject *object)
+{
+	EMailIdentityComboBoxPrivate *priv;
+
+	priv = E_MAIL_IDENTITY_COMBO_BOX_GET_PRIVATE (object);
+
+	if (priv->registry != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->registry, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (priv->registry);
+		priv->registry = NULL;
+	}
+
+	if (priv->refresh_idle_id > 0) {
+		g_source_remove (priv->refresh_idle_id);
+		priv->refresh_idle_id = 0;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_mail_identity_combo_box_parent_class)->
+		dispose (object);
+}
+
+static void
+mail_identity_combo_box_constructed (GObject *object)
+{
+	GtkListStore *list_store;
+	GtkComboBox *combo_box;
+	GtkCellLayout *cell_layout;
+	GtkCellRenderer *cell_renderer;
+
+	/* Chain up to parent's constructed() method. */
+	G_OBJECT_CLASS (e_mail_identity_combo_box_parent_class)->
+		constructed (object);
+
+	combo_box = GTK_COMBO_BOX (object);
+	cell_layout = GTK_CELL_LAYOUT (object);
+
+	list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+	gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (list_store));
+	gtk_combo_box_set_id_column (combo_box, COLUMN_UID);
+	g_object_unref (list_store);
+
+	cell_renderer = gtk_cell_renderer_text_new ();
+	gtk_cell_layout_pack_start (cell_layout, cell_renderer, TRUE);
+	gtk_cell_layout_add_attribute (
+		cell_layout, cell_renderer, "text", COLUMN_DISPLAY_NAME);
+
+	e_mail_identity_combo_box_refresh (E_MAIL_IDENTITY_COMBO_BOX (object));
+}
+
+static void
+e_mail_identity_combo_box_class_init (EMailIdentityComboBoxClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (EMailIdentityComboBoxPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = mail_identity_combo_box_set_property;
+	object_class->get_property = mail_identity_combo_box_get_property;
+	object_class->dispose = mail_identity_combo_box_dispose;
+	object_class->constructed = mail_identity_combo_box_constructed;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_REGISTRY,
+		g_param_spec_object (
+			"registry",
+			"Registry",
+			NULL,
+			E_TYPE_SOURCE_REGISTRY,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY |
+			G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_mail_identity_combo_box_init (EMailIdentityComboBox *combo_box)
+{
+	combo_box->priv = E_MAIL_IDENTITY_COMBO_BOX_GET_PRIVATE (combo_box);
+}
+
+GtkWidget *
+e_mail_identity_combo_box_new (ESourceRegistry *registry)
+{
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+	return g_object_new (
+		E_TYPE_MAIL_IDENTITY_COMBO_BOX,
+		"registry", registry, NULL);
+}
+
+void
+e_mail_identity_combo_box_refresh (EMailIdentityComboBox *combo_box)
+{
+	ESourceRegistry *registry;
+	GtkTreeModel *tree_model;
+	ESource *source;
+	GList *list, *link;
+	const gchar *extension_name;
+	gchar *saved_uid = NULL;
+
+	g_return_if_fail (E_IS_MAIL_IDENTITY_COMBO_BOX (combo_box));
+
+	if (combo_box->priv->refresh_idle_id > 0) {
+		g_source_remove (combo_box->priv->refresh_idle_id);
+		combo_box->priv->refresh_idle_id = 0;
+	}
+
+	registry = e_mail_identity_combo_box_get_registry (combo_box);
+	tree_model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+
+	source = e_mail_identity_combo_box_get_active_source (combo_box);
+	if (source != NULL)
+		saved_uid = g_strdup (e_source_get_uid (source));
+
+	gtk_list_store_clear (GTK_LIST_STORE (tree_model));
+
+	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
+	list = e_source_registry_list_sources (registry, extension_name);
+
+	for (link = list; link != NULL; link = g_list_next (link)) {
+		GtkTreeIter iter;
+		const gchar *display_name;
+		const gchar *uid;
+
+		source = E_SOURCE (link->data);
+		display_name = e_source_get_display_name (source);
+		uid = e_source_get_uid (source);
+
+		gtk_list_store_append (GTK_LIST_STORE (tree_model), &iter);
+
+		gtk_list_store_set (
+			GTK_LIST_STORE (tree_model), &iter,
+			COLUMN_DISPLAY_NAME, display_name,
+			COLUMN_UID, uid, -1);
+	}
+
+	g_list_free (list);
+
+	/* Try and restore the previous selected source, or else pick
+	 * the default identity of the default mail account.  If even
+	 * that fails, just pick the first item. */
+
+	source = NULL;
+
+	if (saved_uid != NULL) {
+		source = e_source_registry_lookup_by_uid (registry, saved_uid);
+		g_free (saved_uid);
+	}
+
+	if (source == NULL)
+		source = mail_identity_combo_box_get_default (combo_box);
+
+	if (source != NULL)
+		e_mail_identity_combo_box_set_active_source (combo_box, source);
+	else
+		gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
+}
+
+ESourceRegistry *
+e_mail_identity_combo_box_get_registry (EMailIdentityComboBox *combo_box)
+{
+	g_return_val_if_fail (E_IS_MAIL_IDENTITY_COMBO_BOX (combo_box), NULL);
+
+	return combo_box->priv->registry;
+}
+
+ESource *
+e_mail_identity_combo_box_get_active_source (EMailIdentityComboBox *combo_box)
+{
+	ESourceRegistry *registry;
+	ESource *source = NULL;
+	const gchar *uid;
+
+	g_return_val_if_fail (E_IS_MAIL_IDENTITY_COMBO_BOX (combo_box), NULL);
+
+	registry = e_mail_identity_combo_box_get_registry (combo_box);
+	uid = gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo_box));
+
+	if (uid != NULL)
+		source = e_source_registry_lookup_by_uid (registry, uid);
+
+	return source;
+}
+
+void
+e_mail_identity_combo_box_set_active_source (EMailIdentityComboBox *combo_box,
+                                             ESource *active_source)
+{
+	const gchar *uid;
+
+	g_return_if_fail (E_IS_MAIL_IDENTITY_COMBO_BOX (combo_box));
+	g_return_if_fail (E_IS_SOURCE (active_source));
+
+	/* It is a programming error to pass an ESource that has no
+	 * "Mail Identity" extension. */
+	g_return_if_fail (SOURCE_IS_MAIL_IDENTITY (active_source));
+
+	uid = e_source_get_uid (active_source);
+	gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), uid);
+}
diff --git a/widgets/misc/e-mail-identity-combo-box.h b/widgets/misc/e-mail-identity-combo-box.h
new file mode 100644
index 0000000..b40957d
--- /dev/null
+++ b/widgets/misc/e-mail-identity-combo-box.h
@@ -0,0 +1,76 @@
+/*
+ * e-mail-identity-combo-box.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_IDENTITY_COMBO_BOX_H
+#define E_MAIL_IDENTITY_COMBO_BOX_H
+
+#include <gtk/gtk.h>
+#include <libedataserver/e-source-registry.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_IDENTITY_COMBO_BOX \
+	(e_mail_identity_combo_box_get_type ())
+#define E_MAIL_IDENTITY_COMBO_BOX(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_MAIL_IDENTITY_COMBO_BOX, EMailIdentityComboBox))
+#define E_MAIL_IDENTITY_COMBO_BOX_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_MAIL_IDENTITY_COMBO_BOX, EMailIdentityComboBoxClass))
+#define E_IS_MAIL_IDENTITY_COMBO_BOX(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_MAIL_IDENTITY_COMBO_BOX))
+#define E_IS_MAIL_IDENTITY_COMBO_BOX_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_MAIL_IDENTITY_COMBO_BOX))
+#define E_MAIL_IDENTITY_COMBO_BOX_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_MAIL_IDENTITY_COMBO_BOX, EMailIdentityComboBoxClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailIdentityComboBox EMailIdentityComboBox;
+typedef struct _EMailIdentityComboBoxClass EMailIdentityComboBoxClass;
+typedef struct _EMailIdentityComboBoxPrivate EMailIdentityComboBoxPrivate;
+
+struct _EMailIdentityComboBox {
+	GtkComboBox parent;
+	EMailIdentityComboBoxPrivate *priv;
+};
+
+struct _EMailIdentityComboBoxClass {
+	GtkComboBoxClass parent_class;
+};
+
+GType		e_mail_identity_combo_box_get_type
+					(void) G_GNUC_CONST;
+GtkWidget *	e_mail_identity_combo_box_new
+					(ESourceRegistry *registry);
+void		e_mail_identity_combo_box_refresh
+					(EMailIdentityComboBox *combo_box);
+ESourceRegistry *
+		e_mail_identity_combo_box_get_registry
+					(EMailIdentityComboBox *combo_box);
+ESource *	e_mail_identity_combo_box_get_active_source
+					(EMailIdentityComboBox *combo_box);
+void		e_mail_identity_combo_box_set_active_source
+					(EMailIdentityComboBox *combo_box,
+					 ESource *active_source);
+
+G_END_DECLS
+
+#endif /* E_MAIL_IDENTITY_COMBO_BOX_H */
diff --git a/widgets/misc/test-mail-accounts.c b/widgets/misc/test-mail-accounts.c
new file mode 100644
index 0000000..92069b4
--- /dev/null
+++ b/widgets/misc/test-mail-accounts.c
@@ -0,0 +1,61 @@
+/*
+ * test-mail-accounts.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <misc/e-mail-account-manager.h>
+#include <misc/e-mail-identity-combo-box.h>
+
+gint
+main (gint argc, gchar **argv)
+{
+	ESourceRegistry *registry;
+	GtkWidget *container;
+	GtkWidget *widget;
+	GtkWidget *window;
+
+	gtk_init (&argc, &argv);
+
+	registry = e_source_registry_get_default ();
+
+	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_title (GTK_WINDOW (window), "Mail Sources");
+	gtk_widget_set_default_size (GTK_WINDOW (window), 400, 400);
+	gtk_container_set_border_width (GTK_CONTAINER (window), 12);
+	gtk_widget_show (window);
+
+	g_signal_connect (
+		window, "delete-event",
+		G_CALLBACK (gtk_main_quit), NULL);
+
+	widget = gtk_vbox_new (FALSE, 12);
+	gtk_container_add (GTK_CONTAINER (window), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = e_mail_identity_combo_box_new (registry);
+	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+	gtk_widget_show (widget);
+
+	widget = e_mail_account_manager_new (registry);
+	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+	gtk_widget_show (widget);
+
+	gtk_main ();
+
+	return 0;
+}



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