[evolution] Bug 271481 - Provide generic Accounts editor



commit 4720799e12175042d1a70c02a1092806e3010d9d
Author: Milan Crha <mcrha redhat com>
Date:   Thu Jul 20 15:54:00 2017 +0200

    Bug 271481 - Provide generic Accounts editor

 data/ui/evolution-shell.ui                         |    1 +
 po/POTFILES.in                                     |    2 +
 src/addressbook/addressbook.error.xml              |    2 +-
 src/calendar/calendar.error.xml                    |   12 +-
 src/e-util/CMakeLists.txt                          |    3 +
 src/e-util/e-accounts-window.c                     | 2195 ++++++++++++++++++++
 src/e-util/e-accounts-window.h                     |  114 +
 src/e-util/e-util.h                                |    1 +
 src/e-util/test-accounts-window.c                  |   76 +
 src/mail/e-mail-account-store.c                    |    2 +-
 src/mail/mail.error.xml                            |    2 +-
 src/modules/CMakeLists.txt                         |    1 +
 src/modules/accounts-window/CMakeLists.txt         |   22 +
 src/modules/accounts-window/accounts-window.c      |   38 +
 .../accounts-window/e-accounts-window-editors.c    |  648 ++++++
 .../accounts-window/e-accounts-window-editors.h    |   29 +
 src/modules/mail/e-mail-shell-backend.c            |  173 ++-
 src/modules/mail/e-mail-shell-backend.h            |    7 +
 src/shell/e-shell-window-actions.c                 |   25 +
 src/shell/e-shell-window-actions.h                 |    2 +
 20 files changed, 3294 insertions(+), 61 deletions(-)
---
diff --git a/data/ui/evolution-shell.ui b/data/ui/evolution-shell.ui
index 6b34107..4d30b80 100644
--- a/data/ui/evolution-shell.ui
+++ b/data/ui/evolution-shell.ui
@@ -29,6 +29,7 @@
       <separator/>
       <placeholder name='edit-actions'/>
       <separator/>
+      <menuitem action='accounts'/>
       <placeholder name='administrative-actions'/>
       <menuitem action='preferences'/>
     </menu>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7dd8e09..da15e6d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -156,6 +156,7 @@ src/em-format/e-mail-part-headers.c
 src/em-format/e-mail-part-secure-button.c
 src/em-format/e-mail-part-utils.c
 src/e-util/ea-calendar-item.c
+src/e-util/e-accounts-window.c
 src/e-util/e-action-combo-box.c
 src/e-util/e-activity-bar.c
 src/e-util/e-activity.c
@@ -381,6 +382,7 @@ src/mail/message-list.c
 src/mail/message-list.etspec
 src/mail/searchtypes.xml.in
 src/mail/vfoldertypes.xml.in
+src/modules/accounts-window/e-accounts-window-editors.c
 src/modules/addressbook/autocompletion-config.c
 src/modules/addressbook/eab-composer-util.c
 src/modules/addressbook/e-book-shell-backend.c
diff --git a/src/addressbook/addressbook.error.xml b/src/addressbook/addressbook.error.xml
index da88d0a..34c5282 100644
--- a/src/addressbook/addressbook.error.xml
+++ b/src/addressbook/addressbook.error.xml
@@ -33,7 +33,7 @@
   </error>
 
   <error id="ask-delete-addressbook" type="question" default="GTK_RESPONSE_CANCEL">
-    <_primary>Delete address book “{0}”?</_primary>
+    <_primary>Are you sure you want to delete address book “{0}”?</_primary>
     <_secondary>This address book will be removed permanently.</_secondary>
     <button _label="Do _Not Delete" response="GTK_RESPONSE_CANCEL"/>
     <button stock="gtk-delete" response="GTK_RESPONSE_YES"/>
diff --git a/src/calendar/calendar.error.xml b/src/calendar/calendar.error.xml
index b6f043b..76ea753 100644
--- a/src/calendar/calendar.error.xml
+++ b/src/calendar/calendar.error.xml
@@ -208,42 +208,42 @@
   </error>
 
   <error id="prompt-delete-calendar" type="question" default="GTK_RESPONSE_CANCEL">
-    <_primary>Delete calendar “{0}”?</_primary>
+    <_primary>Are you sure you want to delete calendar “{0}”?</_primary>
     <_secondary>This calendar will be removed permanently.</_secondary>
     <button _label="Do _Not Delete" response="GTK_RESPONSE_CANCEL"/>
     <button stock="gtk-delete" response="GTK_RESPONSE_YES"/>
   </error>
 
   <error id="prompt-delete-task-list" type="question" default="GTK_RESPONSE_CANCEL">
-    <_primary>Delete task list “{0}”?</_primary>
+    <_primary>Are you sure you want to delete task list “{0}”?</_primary>
     <_secondary>This task list will be removed permanently.</_secondary>
     <button _label="Do _Not Delete" response="GTK_RESPONSE_CANCEL"/>
     <button stock="gtk-delete" response="GTK_RESPONSE_YES"/>
   </error>
 
   <error id="prompt-delete-memo-list" type="question" default="GTK_RESPONSE_CANCEL">
-    <_primary>Delete memo list “{0}”?</_primary>
+    <_primary>Are you sure you want to delete memo list “{0}”?</_primary>
     <_secondary>This memo list will be removed permanently.</_secondary>
     <button _label="Do _Not Delete" response="GTK_RESPONSE_NO"/>
     <button stock="gtk-delete" response="GTK_RESPONSE_YES"/>
   </error>
 
   <error id="prompt-delete-remote-calendar" type="question" default="GTK_RESPONSE_CANCEL">
-    <_primary>Delete remote calendar “{0}”?</_primary>
+    <_primary>Are you sure you want to delete remote calendar “{0}”?</_primary>
     <_secondary>This will permanently remove the calendar “{0}” from the server. Are you sure you want to 
proceed?</_secondary>
     <button _label="Do _Not Delete" response="GTK_RESPONSE_CANCEL"/>
     <button _label="_Delete From Server" response="GTK_RESPONSE_YES"/>
   </error>
 
   <error id="prompt-delete-remote-task-list" type="question" default="GTK_RESPONSE_CANCEL">
-    <_primary>Delete remote task list “{0}”?</_primary>
+    <_primary>Are you sure you want to delete remote task list “{0}”?</_primary>
     <_secondary>This will permanently remove the task list “{0}” from the server. Are you sure you want to 
proceed?</_secondary>
     <button _label="Do _Not Delete" response="GTK_RESPONSE_CANCEL"/>
     <button _label="_Delete From Server" response="GTK_RESPONSE_YES"/>
   </error>
 
   <error id="prompt-delete-remote-memo-list" type="question" default="GTK_RESPONSE_CANCEL">
-    <_primary>Delete remote memo list “{0}”?</_primary>
+    <_primary>Are you sure you want to delete remote memo list “{0}”?</_primary>
     <_secondary>This will permanently remove the memo list “{0}” from the server. Are you sure you want to 
proceed?</_secondary>
     <button _label="Do _Not Delete" response="GTK_RESPONSE_CANCEL"/>
     <button _label="_Delete From Server" response="GTK_RESPONSE_YES"/>
diff --git a/src/e-util/CMakeLists.txt b/src/e-util/CMakeLists.txt
index 7319810..3b3740b 100644
--- a/src/e-util/CMakeLists.txt
+++ b/src/e-util/CMakeLists.txt
@@ -37,6 +37,7 @@ set(DEPENDENCIES
 )
 
 set(SOURCES
+       e-accounts-window.c
        e-action-combo-box.c
        e-activity-bar.c
        e-activity-proxy.c
@@ -301,6 +302,7 @@ set(SOURCES
 
 set(HEADERS
        e-util.h
+       e-accounts-window.h
        e-action-combo-box.h
        e-activity-bar.h
        e-activity-proxy.h
@@ -764,6 +766,7 @@ endmacro(add_private_programs_simple)
 
 add_private_programs_simple(
        evolution-source-viewer
+       test-accounts-window
        test-calendar
        test-category-completion
        test-contact-store
diff --git a/src/e-util/e-accounts-window.c b/src/e-util/e-accounts-window.c
new file mode 100644
index 0000000..f47d8c6
--- /dev/null
+++ b/src/e-util/e-accounts-window.c
@@ -0,0 +1,2195 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: e-accounts-window
+ * @include: e-util/e-util.h
+ * @short_description: Accounts Window
+ *
+ * #EAccountsWindow shows all configured accounts in evolution-data-server
+ * and allows also create new, modify or remove existing accounts as well.
+ * It's extensible through #EExtension, thus it can be taught how to work
+ * with particular account types as well.
+ **/
+
+#include "evolution-config.h"
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+#include <libedataserverui/libedataserverui.h>
+
+#include "e-alert-dialog.h"
+#include "e-dialog-widgets.h"
+#include "e-misc-utils.h"
+
+#include "e-accounts-window.h"
+
+#define ADD_POPUP_KEY_KIND     "add-popup-key-kind"
+
+#define UNKNOWN_SORT_HINT      -1
+#define COLLECTIONS_SORT_HINT  0
+#define MAIL_ACCOUNTS_SORT_HINT        1
+#define ADDRESS_BOOKS_SORT_HINT        2
+#define CALENDARS_SORT_HINT    3
+#define MEMO_LISTS_SORT_HINT   4
+#define TASK_LISTS_SORT_HINT   5
+
+struct _EAccountsWindowPrivate {
+       ESourceRegistry *registry;
+
+       GtkWidget *notebook;            /* not referenced */
+       GtkWidget *button_box;          /* not referenced */
+       GtkWidget *tree_view;           /* not referenced */
+       GtkWidget *add_box;             /* not referenced */
+       GtkWidget *edit_button;         /* not referenced */
+       GtkWidget *delete_button;       /* not referenced */
+
+       GHashTable *references; /* gchar *UID ~> GtkTreeRowReference * */
+
+       gulong source_enabled_handler_id;
+       gulong source_disabled_handler_id;
+       gulong source_added_handler_id;
+       gulong source_removed_handler_id;
+       gulong source_changed_handler_id;
+};
+
+enum {
+       PROP_0,
+       PROP_REGISTRY
+};
+
+enum {
+       GET_EDITING_FLAGS,
+       ADD_SOURCE,
+       EDIT_SOURCE,
+       DELETE_SOURCE,
+       ENABLED_TOGGLED,
+       POPULATE_ADD_POPUP,
+       SELECTION_CHANGED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE_WITH_CODE (EAccountsWindow, e_accounts_window, GTK_TYPE_WINDOW,
+       G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
+
+enum {
+       COLUMN_BOOL_ENABLED,
+       COLUMN_BOOL_ENABLED_VISIBLE,
+       COLUMN_STRING_DISPLAY_NAME,
+       COLUMN_STRING_ICON_NAME,
+       COLUMN_BOOL_ICON_VISIBLE,
+       COLUMN_RGBA_COLOR,
+       COLUMN_BOOL_COLOR_VISIBLE,
+       COLUMN_STRING_TYPE,
+       COLUMN_OBJECT_SOURCE,
+       COLUMN_INT_SORT_HINT,
+       COLUMN_UINT_EDITING_FLAGS,
+       COLUMN_BOOL_DELETE_WHEN_NO_CHILDREN,
+       N_COLUMNS
+};
+
+static gint
+accounts_window_get_sort_hint_for_source (ESource *source)
+{
+       g_return_val_if_fail (E_IS_SOURCE (source), UNKNOWN_SORT_HINT);
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION))
+               return COLLECTIONS_SORT_HINT;
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT))
+               return MAIL_ACCOUNTS_SORT_HINT;
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK))
+               return ADDRESS_BOOKS_SORT_HINT;
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
+               return CALENDARS_SORT_HINT;
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST))
+               return MEMO_LISTS_SORT_HINT;
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+               return TASK_LISTS_SORT_HINT;
+
+       return UNKNOWN_SORT_HINT;
+}
+
+static gboolean
+accounts_window_emit_get_editing_flags (EAccountsWindow *accounts_window,
+                                       ESource *source,
+                                       guint *out_flags)
+{
+       gboolean handled = FALSE;
+
+       g_signal_emit (accounts_window, signals[GET_EDITING_FLAGS], 0, source, out_flags, &handled);
+
+       return handled;
+}
+
+static void
+accounts_window_emit_edit_source (EAccountsWindow *accounts_window)
+{
+       ESource *source;
+
+       source = e_accounts_window_ref_selected_source (accounts_window);
+
+       if (source) {
+               gboolean handled = FALSE;
+
+               g_signal_emit (accounts_window, signals[EDIT_SOURCE], 0, source, &handled);
+
+               g_object_unref (source);
+       }
+}
+
+static void
+accounts_window_emit_delete_source (EAccountsWindow *accounts_window)
+{
+       ESource *source;
+
+       source = e_accounts_window_ref_selected_source (accounts_window);
+
+       if (source) {
+               gboolean handled = FALSE;
+
+               g_signal_emit (accounts_window, signals[DELETE_SOURCE], 0, source, &handled);
+
+               g_object_unref (source);
+       }
+}
+
+static gboolean
+accounts_window_find_source_iter (EAccountsWindow *accounts_window,
+                                 ESource *source,
+                                 GtkTreeIter *out_iter,
+                                 GtkTreeModel **out_model)
+{
+       GtkTreeRowReference *reference;
+       GtkTreePath *path;
+       GtkTreeModel *model;
+       gboolean valid;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+       g_return_val_if_fail (out_iter != NULL, FALSE);
+
+       reference = g_hash_table_lookup (accounts_window->priv->references, e_source_get_uid (source));
+       if (!reference ||
+           !gtk_tree_row_reference_valid (reference)) {
+               g_hash_table_remove (accounts_window->priv->references, e_source_get_uid (source));
+
+               return FALSE;
+       }
+
+       path = gtk_tree_row_reference_get_path (reference);
+       if (!path)
+               return FALSE;
+
+       model = gtk_tree_row_reference_get_model (reference);
+       valid = gtk_tree_model_get_iter (model, out_iter, path);
+
+       gtk_tree_path_free (path);
+
+       if (out_model)
+               *out_model = model;
+
+       return valid;
+}
+
+static gboolean
+accounts_window_find_child_with_sort_hint (EAccountsWindow *accounts_window,
+                                          GtkTreeStore *tree_store,
+                                          GtkTreeIter *parent,
+                                          gint in_sort_hint,
+                                          GtkTreeIter *out_iter)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gint row_sort_hint = -1;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+       g_return_val_if_fail (GTK_IS_TREE_STORE (tree_store), FALSE);
+       g_return_val_if_fail (out_iter != NULL, FALSE);
+
+       model = GTK_TREE_MODEL (tree_store);
+
+       if (!gtk_tree_model_iter_nth_child (model, &iter, parent, 0))
+               return FALSE;
+
+       do {
+               gtk_tree_model_get (model, &iter, COLUMN_INT_SORT_HINT, &row_sort_hint, -1);
+
+               if (in_sort_hint == row_sort_hint) {
+                       *out_iter = iter;
+
+                       return TRUE;
+               }
+       } while (gtk_tree_model_iter_next (model, &iter));
+
+       return FALSE;
+}
+
+static gboolean
+accounts_window_find_child_with_source_uid (EAccountsWindow *accounts_window,
+                                           GtkTreeStore *tree_store,
+                                           GtkTreeIter *parent,
+                                           const gchar *source_uid,
+                                           GtkTreeIter *out_iter)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+       g_return_val_if_fail (GTK_IS_TREE_STORE (tree_store), FALSE);
+       g_return_val_if_fail (source_uid != NULL, FALSE);
+       g_return_val_if_fail (out_iter != NULL, FALSE);
+
+       model = GTK_TREE_MODEL (tree_store);
+
+       if (!gtk_tree_model_iter_nth_child (model, &iter, parent, 0))
+               return FALSE;
+
+       do {
+               ESource *source = NULL;
+
+               gtk_tree_model_get (model, &iter, COLUMN_OBJECT_SOURCE, &source, -1);
+
+               if (source && g_strcmp0 (source_uid, e_source_get_uid (source)) == 0) {
+                       g_clear_object (&source);
+
+                       *out_iter = iter;
+
+                       return TRUE;
+               }
+
+               g_clear_object (&source);
+       } while (gtk_tree_model_iter_next (model, &iter));
+
+       return FALSE;
+}
+
+static void
+accounts_window_fill_row_virtual (EAccountsWindow *accounts_window,
+                                 GtkTreeStore *tree_store,
+                                 GtkTreeIter *iter,
+                                 const gchar *display_name,
+                                 const gchar *icon_name,
+                                 gint sort_hint)
+{
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+       g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
+       g_return_if_fail (iter != NULL);
+       g_return_if_fail (display_name != NULL);
+
+       gtk_tree_store_set (tree_store, iter,
+               COLUMN_BOOL_ENABLED_VISIBLE, FALSE,
+               COLUMN_STRING_DISPLAY_NAME, display_name,
+               COLUMN_STRING_ICON_NAME, icon_name,
+               COLUMN_BOOL_ICON_VISIBLE, icon_name != NULL,
+               COLUMN_INT_SORT_HINT, sort_hint,
+               COLUMN_UINT_EDITING_FLAGS, E_SOURCE_EDITING_FLAG_NONE,
+               COLUMN_BOOL_DELETE_WHEN_NO_CHILDREN, TRUE,
+               -1);
+}
+
+static void
+accounts_window_fill_row_with_source (EAccountsWindow *accounts_window,
+                                     GtkTreeStore *tree_store,
+                                     GtkTreeIter *iter,
+                                     ESource *source,
+                                     const GSList *mail_account_slaves,
+                                     gboolean can_show_enabled)
+{
+       GtkTreePath *path;
+       gpointer extension;
+       gchar *use_type = NULL;
+       const gchar *icon_name = NULL;
+       GdkRGBA rgba;
+       gboolean rgba_set = FALSE;
+       gboolean enabled_visible = TRUE;
+       guint editing_flags = E_SOURCE_EDITING_FLAG_NONE;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+       g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
+       g_return_if_fail (iter != NULL);
+       g_return_if_fail (E_IS_SOURCE (source));
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
+               gchar *backend_name;
+
+               extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
+               backend_name = e_source_backend_dup_backend_name (extension);
+
+               if (e_source_has_extension (source, E_SOURCE_EXTENSION_GOA)) {
+                       use_type = g_strconcat ("GOA:", backend_name, NULL);
+                       icon_name = "goa-panel";
+                       enabled_visible = FALSE;
+               } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_UOA)) {
+                       use_type = g_strconcat ("UOA:", backend_name, NULL);
+                       icon_name = "credentials-preferences";
+                       enabled_visible = FALSE;
+               } else {
+                       use_type = backend_name;
+                       backend_name = NULL;
+               }
+
+               g_free (backend_name);
+       } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
+               gchar *receive_backend_name, *transport_backend_name = NULL;
+               gchar *identity_uid, *transport_uid = NULL;
+               GSList *link;
+
+               extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
+               receive_backend_name = e_source_backend_dup_backend_name (extension);
+               identity_uid = e_source_mail_account_dup_identity_uid (extension);
+               if (identity_uid) {
+                       for (link = (GSList *) mail_account_slaves; link; link = g_slist_next (link)) {
+                               ESource *subsource = link->data;
+
+                               if (g_strcmp0 (e_source_get_uid (subsource), identity_uid) == 0) {
+                                       if (e_source_has_extension (subsource, 
E_SOURCE_EXTENSION_MAIL_SUBMISSION)) {
+                                               extension = e_source_get_extension (subsource, 
E_SOURCE_EXTENSION_MAIL_SUBMISSION);
+                                               transport_uid = e_source_mail_submission_dup_transport_uid 
(extension);
+                                       }
+                                       break;
+                               }
+                       }
+               }
+
+               if (transport_uid) {
+                       for (link = (GSList *) mail_account_slaves; link; link = g_slist_next (link)) {
+                               ESource *subsource = link->data;
+
+                               if (g_strcmp0 (e_source_get_uid (subsource), transport_uid) == 0) {
+                                       if (e_source_has_extension (subsource, 
E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
+                                               extension = e_source_get_extension (subsource, 
E_SOURCE_EXTENSION_MAIL_TRANSPORT);
+                                               transport_backend_name = e_source_backend_dup_backend_name 
(extension);
+
+                                       }
+                                       break;
+                               }
+                       }
+               }
+
+               if ((receive_backend_name && !*receive_backend_name) ||
+                   g_strcmp0 (receive_backend_name, "none") == 0) {
+                       g_free (receive_backend_name);
+                       receive_backend_name = NULL;
+               }
+
+               if ((transport_backend_name && !*transport_backend_name) ||
+                   g_strcmp0 (transport_backend_name, "none") == 0) {
+                       g_free (transport_backend_name);
+                       transport_backend_name = NULL;
+               }
+
+               if (g_strcmp0 (receive_backend_name, transport_backend_name) == 0) {
+                       g_free (transport_backend_name);
+                       transport_backend_name = NULL;
+               }
+
+               if (receive_backend_name && transport_backend_name) {
+                       use_type = g_strconcat (receive_backend_name, "+", transport_backend_name, NULL);
+               } else if (receive_backend_name) {
+                       use_type = receive_backend_name;
+                       receive_backend_name = NULL;
+               } else {
+                       use_type = transport_backend_name;
+                       transport_backend_name = NULL;
+               }
+
+               g_free (receive_backend_name);
+               g_free (transport_backend_name);
+               g_free (identity_uid);
+               g_free (transport_uid);
+       } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
+               extension = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
+               use_type = e_source_backend_dup_backend_name (extension);
+       } else {
+               extension = NULL;
+
+               if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
+                       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
+               else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST))
+                       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MEMO_LIST);
+               else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+                       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
+
+               if (extension) {
+                       gchar *color;
+
+                       use_type = e_source_backend_dup_backend_name (extension);
+                       color = e_source_selectable_dup_color (extension);
+                       rgba_set = color && gdk_rgba_parse (&rgba, color);
+
+                       g_free (color);
+               }
+       }
+
+       accounts_window_emit_get_editing_flags (accounts_window, source, &editing_flags);
+
+       if ((editing_flags & E_SOURCE_EDITING_FLAG_CAN_EDIT) != 0 &&
+           !e_source_get_writable (source))
+               editing_flags = editing_flags & ~E_SOURCE_EDITING_FLAG_CAN_EDIT;
+
+       if ((editing_flags & E_SOURCE_EDITING_FLAG_CAN_DELETE) != 0 &&
+           !e_source_get_removable (source))
+               editing_flags = editing_flags & ~E_SOURCE_EDITING_FLAG_CAN_DELETE;
+
+       gtk_tree_store_set (tree_store, iter,
+               COLUMN_BOOL_ENABLED, e_source_get_enabled (source),
+               COLUMN_BOOL_ENABLED_VISIBLE, can_show_enabled && enabled_visible && (editing_flags & 
E_SOURCE_EDITING_FLAG_CAN_ENABLE) != 0,
+               COLUMN_STRING_DISPLAY_NAME, e_source_get_display_name (source),
+               COLUMN_STRING_ICON_NAME, icon_name,
+               COLUMN_BOOL_ICON_VISIBLE, icon_name != NULL,
+               COLUMN_RGBA_COLOR, rgba_set ? &rgba : NULL,
+               COLUMN_BOOL_COLOR_VISIBLE, rgba_set,
+               COLUMN_STRING_TYPE, use_type,
+               COLUMN_OBJECT_SOURCE, source,
+               COLUMN_INT_SORT_HINT, accounts_window_get_sort_hint_for_source (source),
+               COLUMN_UINT_EDITING_FLAGS, editing_flags,
+               COLUMN_BOOL_DELETE_WHEN_NO_CHILDREN, !can_show_enabled,
+               -1);
+
+       g_free (use_type);
+
+       path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_store), iter);
+       g_hash_table_insert (accounts_window->priv->references, e_source_dup_uid (source),
+               gtk_tree_row_reference_new (GTK_TREE_MODEL (tree_store), path));
+       gtk_tree_path_free (path);
+}
+
+static void
+accounts_window_fill_children (EAccountsWindow *accounts_window,
+                              GtkTreeStore *tree_store,
+                              GtkTreeIter *root,
+                              gboolean is_managed_collection,
+                              gboolean lookup_subroot,
+                              const GSList *children)
+{
+       GtkTreeIter mails_iter, books_iter, calendars_iter, memos_iter, tasks_iter;
+       gboolean mails_iter_set = FALSE, books_iter_set = FALSE, calendars_iter_set = FALSE, memos_iter_set = 
FALSE, tasks_iter_set = FALSE;
+       GSList *link;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+       g_return_if_fail (GTK_IS_TREE_STORE (tree_store));
+       g_return_if_fail (root != NULL);
+
+       for (link = (GSList *) children; link; link = g_slist_next (link)) {
+               ESource *source = link->data;
+               const GSList *mail_account_slaves = NULL;
+               const gchar *subroot_display_name;
+               const gchar *subroot_icon_name;
+               gint subroot_sort_hint;
+               gboolean *subroot_set;
+               GtkTreeIter iter, *subroot;
+
+               if (accounts_window_get_sort_hint_for_source (source) == -1)
+                       continue;
+
+               if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
+                       subroot_display_name = _("Mail Accounts");
+                       subroot_icon_name = "evolution-mail";
+                       subroot_sort_hint = MAIL_ACCOUNTS_SORT_HINT;
+                       subroot_set = &mails_iter_set;
+                       subroot = &mails_iter;
+                       mail_account_slaves = children;
+               } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
+                       subroot_display_name = _("Address Books");
+                       subroot_icon_name = "x-office-address-book";
+                       subroot_sort_hint = ADDRESS_BOOKS_SORT_HINT;
+                       subroot_set = &books_iter_set;
+                       subroot = &books_iter;
+               } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) {
+                       subroot_display_name = _("Calendars");
+                       subroot_icon_name = "x-office-calendar";
+                       subroot_sort_hint = CALENDARS_SORT_HINT;
+                       subroot_set = &calendars_iter_set;
+                       subroot = &calendars_iter;
+               } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) {
+                       subroot_display_name = _("Memo Lists");
+                       subroot_icon_name = "evolution-memos";
+                       subroot_sort_hint = MEMO_LISTS_SORT_HINT;
+                       subroot_set = &memos_iter_set;
+                       subroot = &memos_iter;
+               } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
+                       subroot_display_name = _("Task Lists");
+                       subroot_icon_name = "evolution-tasks";
+                       subroot_sort_hint = TASK_LISTS_SORT_HINT;
+                       subroot_set = &tasks_iter_set;
+                       subroot = &tasks_iter;
+               } else {
+                       continue;
+               }
+
+               if (!*subroot_set && lookup_subroot)
+                       *subroot_set = accounts_window_find_child_with_sort_hint (accounts_window, 
tree_store, root, subroot_sort_hint, subroot);
+
+               if (!*subroot_set) {
+                       *subroot_set = TRUE;
+
+                       gtk_tree_store_append (tree_store, subroot, root);
+                       accounts_window_fill_row_virtual (accounts_window, tree_store, subroot,
+                               subroot_display_name, subroot_icon_name, subroot_sort_hint);
+               }
+
+               gtk_tree_store_append (tree_store, &iter, subroot);
+
+               accounts_window_fill_row_with_source (accounts_window, tree_store, &iter, source, 
mail_account_slaves, !is_managed_collection);
+       }
+}
+
+static void
+accounts_window_fill_tree_view (EAccountsWindow *accounts_window)
+{
+       GtkTreeStore *tree_store;
+       GtkTreeModelSort *sort_model;
+       GHashTable *children; /* gchar * (parent-uid) ~> GSList { ESource * } */
+       GHashTable *top_sources; /* gchar * (uid) ~> ESource * */
+       GList *sources, *llink;
+       GSList *collections = NULL, *top_mail_accounts = NULL, *slink;
+       GtkTreeIter root;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       children = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
+       top_sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+       sources = e_source_registry_list_sources (accounts_window->priv->registry, NULL);
+       for (llink = sources; llink; llink = g_list_next (llink)) {
+               ESource *source = llink->data;
+               const gchar *parent_uid;
+
+               if (!E_IS_SOURCE (source) ||
+                   e_source_has_extension (source, E_SOURCE_EXTENSION_PROXY) ||
+                   e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_SIGNATURE))
+                       continue;
+
+               parent_uid = e_source_get_parent (source);
+               if (!parent_uid || !*parent_uid) {
+                       if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION))
+                               collections = g_slist_prepend (collections, source);
+                       else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT))
+                               top_mail_accounts = g_slist_prepend (top_mail_accounts, source);
+                       else
+                               g_hash_table_insert (top_sources, g_strdup (e_source_get_uid (source)), 
source);
+               } else {
+                       g_hash_table_insert (children, g_strdup (parent_uid), g_slist_prepend (
+                               g_slist_copy (g_hash_table_lookup (children, parent_uid)), source));
+               }
+       }
+
+       sort_model = GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (GTK_TREE_VIEW 
(accounts_window->priv->tree_view)));
+       tree_store = GTK_TREE_STORE (gtk_tree_model_sort_get_model (sort_model));
+
+       gtk_tree_store_clear (tree_store);
+       g_hash_table_remove_all (accounts_window->priv->references);
+
+       for (slink = collections; slink; slink = g_slist_next (slink)) {
+               ESource *source = slink->data;
+               gboolean is_managed_collection;
+
+               is_managed_collection = e_source_has_extension (source, E_SOURCE_EXTENSION_GOA) ||
+                       e_source_has_extension (source, E_SOURCE_EXTENSION_UOA);
+
+               gtk_tree_store_append (tree_store, &root, NULL);
+
+               accounts_window_fill_row_with_source (accounts_window, tree_store, &root, source, NULL, TRUE);
+               accounts_window_fill_children (accounts_window, tree_store, &root, is_managed_collection, 
FALSE,
+                       g_hash_table_lookup (children, e_source_get_uid (source)));
+       }
+
+       if (top_mail_accounts) {
+               gtk_tree_store_append (tree_store, &root, NULL);
+
+               accounts_window_fill_row_virtual (accounts_window, tree_store, &root,
+                       _("Mail Accounts"), "evolution-mail", MAIL_ACCOUNTS_SORT_HINT);
+
+               for (slink = top_mail_accounts; slink; slink = g_slist_next (slink)) {
+                       ESource *source = slink->data;
+                       GtkTreeIter iter;
+
+                       /* Skip 'On This Computer' and 'Search Folders' mail accounts */
+                       if (g_strcmp0 (e_source_get_uid (source), "local") == 0 ||
+                           g_strcmp0 (e_source_get_uid (source), "vfolder") == 0)
+                               continue;
+
+                       gtk_tree_store_append (tree_store, &iter, &root);
+                       accounts_window_fill_row_with_source (accounts_window, tree_store, &iter, source,
+                               g_hash_table_lookup (children, e_source_get_uid (source)), TRUE);
+               }
+       }
+
+       if (g_hash_table_size (top_sources)) {
+               /* This is getting complicated, because top_sources are On This Computer, CalDAV,
+                  CardDAV, ..., which can be under multiple roots, like the On This Computer, which
+                  is under all of Address Books, Calendars, Memo Lists and Task Lists. */
+               struct _extension_info {
+                       const gchar *extension_name;
+                       const gchar *display_name;
+                       const gchar *icon_name;
+                       gint sort_hint;
+                       GtkTreeIter *root;
+                       GHashTable *slaves; /* gchar * (UID) ~> GtkTreeIter * */
+               } infos[] = {
+                       { E_SOURCE_EXTENSION_ADDRESS_BOOK, N_("Address Books"), "x-office-address-book", 
ADDRESS_BOOKS_SORT_HINT, NULL, NULL },
+                       { E_SOURCE_EXTENSION_CALENDAR, N_("Calendars"), "x-office-calendar", 
CALENDARS_SORT_HINT, NULL, NULL },
+                       { E_SOURCE_EXTENSION_MEMO_LIST, N_("Memo Lists"), "evolution-memos", 
MEMO_LISTS_SORT_HINT, NULL, NULL },
+                       { E_SOURCE_EXTENSION_TASK_LIST, N_("Task Lists"), "evolution-tasks", 
TASK_LISTS_SORT_HINT, NULL, NULL }
+               };
+               GHashTableIter hiter;
+               gpointer value;
+               gint ii;
+
+               for (ii = 0; ii < G_N_ELEMENTS (infos); ii++) {
+                       infos[ii].slaves = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+               }
+
+               g_hash_table_iter_init (&hiter, top_sources);
+               while (g_hash_table_iter_next (&hiter, NULL, &value)) {
+                       ESource *source = value;
+
+                       for (slink = g_hash_table_lookup (children, e_source_get_uid (source)); slink; slink 
= g_slist_next (slink)) {
+                               ESource *child = slink->data;
+
+                               for (ii = 0; ii < G_N_ELEMENTS (infos); ii++) {
+                                       if (e_source_has_extension (child, infos[ii].extension_name)) {
+                                               GtkTreeIter *slave_root;
+                                               GtkTreeIter iter;
+
+                                               if (!infos[ii].root) {
+                                                       gtk_tree_store_append (tree_store, &root, NULL);
+                                                       accounts_window_fill_row_virtual (accounts_window, 
tree_store, &root,
+                                                               _(infos[ii].display_name), 
infos[ii].icon_name, infos[ii].sort_hint);
+
+                                                       infos[ii].root = g_new (GtkTreeIter, 1);
+                                                       *(infos[ii].root) = root;
+                                               }
+
+                                               slave_root = g_hash_table_lookup (infos[ii].slaves, 
e_source_get_uid (source));
+                                               if (slave_root) {
+                                                       root = *slave_root;
+                                               } else {
+                                                       gtk_tree_store_append (tree_store, &root, 
infos[ii].root);
+                                                       accounts_window_fill_row_with_source 
(accounts_window, tree_store, &root, source, NULL, FALSE);
+
+                                                       slave_root = g_new (GtkTreeIter, 1);
+                                                       *slave_root = root;
+
+                                                       g_hash_table_insert (infos[ii].slaves, 
e_source_dup_uid (source), slave_root);
+                                               }
+
+                                               gtk_tree_store_append (tree_store, &iter, &root);
+                                               accounts_window_fill_row_with_source (accounts_window, 
tree_store, &iter, child, NULL, TRUE);
+
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               for (ii = 0; ii < G_N_ELEMENTS (infos); ii++) {
+                       g_hash_table_destroy (infos[ii].slaves);
+                       g_free (infos[ii].root);
+               }
+       }
+
+       g_hash_table_destroy (children);
+       g_hash_table_destroy (top_sources);
+       g_slist_free (collections);
+       g_slist_free (top_mail_accounts);
+       g_list_free_full (sources, g_object_unref);
+}
+
+static void
+accounts_window_update_enabled (EAccountsWindow *accounts_window,
+                               ESource *source,
+                               gboolean enabled)
+{
+       GtkTreeIter iter;
+       GtkTreeModel *model = NULL;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+       g_return_if_fail (E_IS_SOURCE (source));
+
+       if (!accounts_window_find_source_iter (accounts_window, source, &iter, &model))
+               return;
+
+       gtk_tree_store_set (GTK_TREE_STORE (model), &iter, COLUMN_BOOL_ENABLED, enabled, -1);
+}
+
+static void
+accounts_window_source_enabled_cb (ESourceRegistry *registry,
+                                  ESource *source,
+                                  gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+
+       g_return_if_fail (E_IS_SOURCE (source));
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       accounts_window_update_enabled (accounts_window, source, TRUE);
+}
+
+static void
+accounts_window_source_disabled_cb (ESourceRegistry *registry,
+                                   ESource *source,
+                                   gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+
+       g_return_if_fail (E_IS_SOURCE (source));
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       accounts_window_update_enabled (accounts_window, source, FALSE);
+}
+
+static void
+accounts_window_source_added_cb (ESourceRegistry *registry,
+                                ESource *source,
+                                gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+       GtkTreeStore *tree_store;
+       GtkTreeIter iter, root;
+       GSList *children_and_siblings = NULL;
+       GList *sources, *llink;
+
+       g_return_if_fail (E_IS_SOURCE (source));
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       if (accounts_window_get_sort_hint_for_source (source) == UNKNOWN_SORT_HINT ||
+           accounts_window_find_source_iter (accounts_window, source, &iter, NULL))
+               return;
+
+       tree_store = GTK_TREE_STORE (gtk_tree_model_sort_get_model (
+               GTK_TREE_MODEL_SORT (gtk_tree_view_get_model (
+               GTK_TREE_VIEW (accounts_window->priv->tree_view)))));
+
+       sources = e_source_registry_list_sources (accounts_window->priv->registry, NULL);
+       for (llink = sources; llink; llink = g_list_next (llink)) {
+               ESource *other_source = llink->data;
+               const gchar *parent_uid;
+
+               if (!E_IS_SOURCE (other_source) ||
+                   e_source_has_extension (other_source, E_SOURCE_EXTENSION_PROXY) ||
+                   e_source_has_extension (other_source, E_SOURCE_EXTENSION_MAIL_SIGNATURE))
+                       continue;
+
+               parent_uid = e_source_get_parent (other_source);
+               if (parent_uid && *parent_uid && (
+                   g_strcmp0 (parent_uid, e_source_get_parent (source)) == 0 ||
+                   g_strcmp0 (parent_uid, e_source_get_uid (source)) == 0)) {
+                       children_and_siblings = g_slist_prepend (children_and_siblings, g_object_ref 
(other_source));
+               }
+       }
+
+       g_list_free_full (sources, g_object_unref);
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
+               gboolean is_managed_collection;
+
+               gtk_tree_store_append (tree_store, &iter, NULL);
+
+               is_managed_collection = e_source_has_extension (source, E_SOURCE_EXTENSION_GOA) ||
+                       e_source_has_extension (source, E_SOURCE_EXTENSION_UOA);
+
+               accounts_window_fill_row_with_source (accounts_window, tree_store, &iter, source, NULL, TRUE);
+               accounts_window_fill_children (accounts_window, tree_store, &iter, is_managed_collection, 
TRUE, children_and_siblings);
+       } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) && (
+                  !e_source_get_parent (source) || g_strcmp0 (e_source_get_parent (source), "") == 0)) {
+
+               /* Skip 'On This Computer' and 'Search Folders' mail accounts */
+               if (g_strcmp0 (e_source_get_uid (source), "local") != 0 &&
+                   g_strcmp0 (e_source_get_uid (source), "vfolder") != 0) {
+                       if (!accounts_window_find_child_with_sort_hint (accounts_window, tree_store, NULL, 
MAIL_ACCOUNTS_SORT_HINT, &root)) {
+                               gtk_tree_store_append (tree_store, &root, NULL);
+
+                               accounts_window_fill_row_virtual (accounts_window, tree_store, &root,
+                                       _("Mail Accounts"), "evolution-mail", MAIL_ACCOUNTS_SORT_HINT);
+                       }
+
+                       gtk_tree_store_append (tree_store, &iter, &root);
+
+                       accounts_window_fill_row_with_source (accounts_window, tree_store, &iter, source, 
children_and_siblings, TRUE);
+               }
+       } else if (e_source_get_parent (source) && g_strcmp0 (e_source_get_parent (source), "") != 0) {
+               struct _extension_info {
+                       const gchar *extension_name;
+                       const gchar *display_name;
+                       const gchar *icon_name;
+                       gint sort_hint;
+               } infos[] = {
+                       { E_SOURCE_EXTENSION_ADDRESS_BOOK, N_("Address Books"), "x-office-address-book", 
ADDRESS_BOOKS_SORT_HINT },
+                       { E_SOURCE_EXTENSION_CALENDAR, N_("Calendars"), "x-office-calendar", 
CALENDARS_SORT_HINT },
+                       { E_SOURCE_EXTENSION_MEMO_LIST, N_("Memo Lists"), "evolution-memos", 
MEMO_LISTS_SORT_HINT },
+                       { E_SOURCE_EXTENSION_TASK_LIST, N_("Task Lists"), "evolution-tasks", 
TASK_LISTS_SORT_HINT }
+               };
+               ESource *parent_source;
+               gboolean done, is_in_collection = FALSE, is_managed_collection = FALSE;
+               gint ii;
+
+               parent_source = e_source_registry_ref_source (accounts_window->priv->registry, 
e_source_get_parent (source));
+               done = !parent_source;
+
+               if (parent_source &&
+                   e_source_has_extension (parent_source, E_SOURCE_EXTENSION_COLLECTION)) {
+                       is_in_collection = TRUE;
+                       is_managed_collection = e_source_has_extension (parent_source, 
E_SOURCE_EXTENSION_GOA) ||
+                               e_source_has_extension (parent_source, E_SOURCE_EXTENSION_UOA);
+               }
+
+               for (ii = 0; !done && ii < G_N_ELEMENTS (infos); ii++) {
+                       if (e_source_has_extension (source, infos[ii].extension_name)) {
+                               GtkTreeIter root, slave_root, iter;
+
+                               if (is_in_collection) {
+                                       if (accounts_window_find_source_iter (accounts_window, parent_source, 
&iter, NULL)) {
+                                               GSList *children;
+
+                                               children = g_slist_append (NULL, source);
+
+                                               accounts_window_fill_children (accounts_window, tree_store, 
&iter, is_managed_collection, TRUE, children);
+
+                                               g_slist_free (children);
+                                       }
+
+                                       break;
+                               }
+
+                               if (!accounts_window_find_child_with_sort_hint (accounts_window, tree_store, 
NULL, infos[ii].sort_hint, &root)) {
+                                       gtk_tree_store_append (tree_store, &root, NULL);
+
+                                       accounts_window_fill_row_virtual (accounts_window, tree_store, &root,
+                                               _(infos[ii].display_name), infos[ii].icon_name, 
infos[ii].sort_hint);
+                               }
+
+                               if (!accounts_window_find_child_with_source_uid (accounts_window, tree_store, 
&root, e_source_get_parent (source), &slave_root)) {
+                                       gtk_tree_store_append (tree_store, &slave_root, &root);
+                                       accounts_window_fill_row_with_source (accounts_window, tree_store, 
&slave_root, parent_source, NULL, FALSE);
+                               }
+
+                               gtk_tree_store_append (tree_store, &iter, &slave_root);
+                               accounts_window_fill_row_with_source (accounts_window, tree_store, &iter, 
source, NULL, !is_managed_collection);
+
+                               break;
+                       }
+               }
+
+               g_clear_object (&parent_source);
+       }
+
+       g_slist_free_full (children_and_siblings, g_object_unref);
+}
+
+static void
+accounts_window_source_removed_cb (ESourceRegistry *registry,
+                                  ESource *source,
+                                  gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+       GtkTreeIter iter;
+       GtkTreeModel *model = NULL;
+       GtkTreeIter parent;
+       gboolean parent_valid;
+
+       g_return_if_fail (E_IS_SOURCE (source));
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       if (!accounts_window_find_source_iter (accounts_window, source, &iter, &model))
+               return;
+
+       parent_valid = gtk_tree_model_iter_parent (model, &parent, &iter);
+
+       gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
+       g_hash_table_remove (accounts_window->priv->references, e_source_get_uid (source));
+
+       while (parent_valid && !gtk_tree_model_iter_n_children (model, &parent)) {
+               ESource *subsource = NULL;
+               gboolean delete_when_no_children = FALSE;
+
+               iter = parent;
+               parent_valid = gtk_tree_model_iter_parent (model, &parent, &iter);
+
+               gtk_tree_model_get (model, &iter,
+                       COLUMN_OBJECT_SOURCE, &subsource,
+                       COLUMN_BOOL_DELETE_WHEN_NO_CHILDREN, &delete_when_no_children,
+                       -1);
+
+               if (!delete_when_no_children) {
+                       g_clear_object (&subsource);
+                       break;
+               }
+
+               gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
+
+               if (subsource)
+                       g_hash_table_remove (accounts_window->priv->references, e_source_get_uid (subsource));
+
+               g_clear_object (&subsource);
+       }
+}
+
+static void
+accounts_window_source_changed_cb (ESourceRegistry *registry,
+                                  ESource *source,
+                                  gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+       GtkTreeIter iter;
+       GtkTreeModel *model = NULL;
+       gpointer extension = NULL;
+       GdkRGBA rgba;
+       gboolean rgba_set = FALSE;
+
+       g_return_if_fail (E_IS_SOURCE (source));
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       if (!accounts_window_find_source_iter (accounts_window, source, &iter, &model))
+               return;
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
+               extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
+       else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST))
+               extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MEMO_LIST);
+       else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+               extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
+
+       if (extension) {
+               gchar *color;
+
+               color = e_source_selectable_dup_color (extension);
+               rgba_set = color && gdk_rgba_parse (&rgba, color);
+
+               g_free (color);
+       }
+
+       gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
+               COLUMN_BOOL_ENABLED, e_source_get_enabled (source),
+               COLUMN_STRING_DISPLAY_NAME, e_source_get_display_name (source),
+               COLUMN_RGBA_COLOR, rgba_set ? &rgba : NULL,
+               COLUMN_BOOL_COLOR_VISIBLE, rgba_set,
+               -1);
+}
+
+static void
+accounts_window_selection_changed_cb (GtkTreeSelection *selection,
+                                     gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+       GtkTreeModel *model = NULL;
+       GtkTreeIter iter;
+       ESource *source = NULL;
+       guint editing_flags = E_SOURCE_EDITING_FLAG_NONE;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+               gtk_tree_model_get (model, &iter,
+                       COLUMN_UINT_EDITING_FLAGS, &editing_flags,
+                       COLUMN_OBJECT_SOURCE, &source,
+                       -1);
+       }
+
+       gtk_widget_set_sensitive (accounts_window->priv->edit_button, (editing_flags & 
E_SOURCE_EDITING_FLAG_CAN_EDIT) != 0);
+       gtk_widget_set_sensitive (accounts_window->priv->delete_button, (editing_flags & 
E_SOURCE_EDITING_FLAG_CAN_DELETE) != 0);
+
+       g_signal_emit (accounts_window, signals[SELECTION_CHANGED], 0, source);
+
+       g_clear_object (&source);
+}
+
+static void
+accounts_window_source_written_cb (GObject *source_object,
+                                  GAsyncResult *result,
+                                  gpointer user_data)
+{
+       ESource *source;
+       EAccountsWindow *accounts_window;
+       GWeakRef *weak_ref = user_data;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_SOURCE (source_object));
+
+       source = E_SOURCE (source_object);
+
+       if (!e_source_write_finish (source, result, &error)) {
+               g_warning ("%s: Failed to save changes to source '%s' (%s): %s", G_STRFUNC,
+                       e_source_get_display_name (source),
+                       e_source_get_uid (source),
+                       error ? error->message : "Unknown error");
+       } else {
+               accounts_window = g_weak_ref_get (weak_ref);
+
+               if (accounts_window)
+                       g_signal_emit (accounts_window, signals[ENABLED_TOGGLED], 0, source);
+
+               g_clear_object (&accounts_window);
+       }
+
+       e_weak_ref_free (weak_ref);
+       g_clear_error (&error);
+}
+
+static void
+acconts_window_source_removed_cb (GObject *source_object,
+                                 GAsyncResult *result,
+                                 gpointer user_data)
+{
+       ESource *source;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_SOURCE (source_object));
+
+       source = E_SOURCE (source_object);
+
+       if (!e_source_remove_finish (source, result, &error)) {
+               g_warning ("%s: Failed to remove source '%s' (%s): %s", G_STRFUNC,
+                       e_source_get_display_name (source),
+                       e_source_get_uid (source),
+                       error ? error->message : "Unknown error");
+       }
+
+       g_clear_error (&error);
+}
+
+static void
+accounts_window_tree_view_enabled_toggled_cb (GtkCellRendererToggle *cell_renderer,
+                                             const gchar *path_string,
+                                             gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+       GtkTreeSelection *selection;
+       GtkTreePath *path;
+       GtkTreeModel *model = NULL;
+       GtkTreeIter iter;
+       gboolean set_enabled;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (accounts_window->priv->tree_view));
+
+       /* Change the selection first so we act on the correct source. */
+       path = gtk_tree_path_new_from_string (path_string);
+       gtk_tree_selection_select_path (selection, path);
+       gtk_tree_path_free (path);
+
+       set_enabled = !gtk_cell_renderer_toggle_get_active (cell_renderer);
+
+       if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+               ESource *source = NULL;
+
+               gtk_tree_model_get (model, &iter, COLUMN_OBJECT_SOURCE, &source, -1);
+
+               if (source && (e_source_get_enabled (source) ? 1 : 0) != (set_enabled ? 1 : 0)) {
+                       ESource *collection;
+
+                       e_source_set_enabled (source, set_enabled);
+
+                       if (e_source_get_writable (source))
+                               e_source_write (source, NULL, accounts_window_source_written_cb, 
e_weak_ref_new (accounts_window));
+
+                       /* Update also identity and transport sources for mail accounts */
+                       if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
+                               ESource *secondary;
+                               gchar *uid;
+
+                               uid = e_source_mail_account_dup_identity_uid (e_source_get_extension (source, 
E_SOURCE_EXTENSION_MAIL_ACCOUNT));
+                               if (uid && *uid) {
+                                       secondary = e_source_registry_ref_source 
(accounts_window->priv->registry, uid);
+
+                                       if (secondary && (e_source_get_enabled (secondary) ? 1 : 0) != 
(set_enabled ? 1 : 0)) {
+                                               e_source_set_enabled (secondary, set_enabled);
+
+                                               if (e_source_get_writable (secondary))
+                                                       e_source_write (secondary, NULL, 
accounts_window_source_written_cb, e_weak_ref_new (accounts_window));
+                                       }
+
+                                       if (secondary && e_source_has_extension (secondary, 
E_SOURCE_EXTENSION_MAIL_SUBMISSION)) {
+                                               g_free (uid);
+                                               uid = e_source_mail_submission_dup_transport_uid 
(e_source_get_extension (secondary, E_SOURCE_EXTENSION_MAIL_SUBMISSION));
+                                       } else {
+                                               g_free (uid);
+                                               uid = NULL;
+                                       }
+
+                                       g_clear_object (&secondary);
+
+                                       if (uid && *uid) {
+                                               secondary = e_source_registry_ref_source 
(accounts_window->priv->registry, uid);
+
+                                               if (secondary && (e_source_get_enabled (secondary) ? 1 : 0) 
!= (set_enabled ? 1 : 0)) {
+                                                       e_source_set_enabled (secondary, set_enabled);
+
+                                                       if (e_source_get_writable (secondary))
+                                                               e_source_write (secondary, NULL, 
accounts_window_source_written_cb, e_weak_ref_new (accounts_window));
+                                               }
+
+                                               g_clear_object (&secondary);
+                                       }
+                               }
+
+                               g_free (uid);
+                       }
+
+                       /* And finally the collection, but only to enable it, if disabled */
+                       collection = e_source_registry_find_extension (accounts_window->priv->registry, 
source, E_SOURCE_EXTENSION_COLLECTION);
+                       if (collection && set_enabled && (e_source_get_enabled (collection) ? 1 : 0) != 
(set_enabled ? 1 : 0)) {
+                               e_source_set_enabled (collection, set_enabled);
+
+                               if (e_source_get_writable (collection))
+                                       e_source_write (collection, NULL, accounts_window_source_written_cb, 
e_weak_ref_new (accounts_window));
+                       }
+               }
+
+               g_clear_object (&source);
+       }
+}
+
+static gboolean
+accounts_window_key_press_event_cb (GtkWidget *widget,
+                                   GdkEventKey *event,
+                                   gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+
+       if (event->keyval == GDK_KEY_Delete) {
+               if (gtk_widget_is_sensitive (accounts_window->priv->delete_button))
+                       gtk_button_clicked (GTK_BUTTON (accounts_window->priv->delete_button));
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+accounts_window_row_activated_cb (GtkTreeView *tree_view,
+                                 GtkTreePath *path,
+                                 GtkTreeViewColumn *column,
+                                 gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       if (gtk_widget_is_sensitive (accounts_window->priv->edit_button))
+               gtk_button_clicked (GTK_BUTTON (accounts_window->priv->edit_button));
+}
+
+static gboolean
+accounts_window_get_editing_flags_default (EAccountsWindow *accounts_window,
+                                          ESource *source,
+                                          guint *out_flags)
+{
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+       g_return_val_if_fail (out_flags != NULL, FALSE);
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
+               *out_flags = E_SOURCE_EDITING_FLAG_NONE;
+
+               if (!e_source_has_extension (source, E_SOURCE_EXTENSION_GOA) &&
+                   !e_source_has_extension (source, E_SOURCE_EXTENSION_UOA)) {
+                       *out_flags = (*out_flags) | E_SOURCE_EDITING_FLAG_CAN_ENABLE | 
E_SOURCE_EDITING_FLAG_CAN_DELETE;
+               }
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+accounts_window_delete_source_default (EAccountsWindow *accounts_window,
+                                      ESource *source)
+{
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+
+       if (e_source_get_removable (source)) {
+               const gchar *alert_tag = NULL;
+
+               if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION) ||
+                   e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT))
+                       alert_tag = "mail:ask-delete-account";
+               else if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK))
+                       alert_tag = "addressbook:ask-delete-addressbook";
+               else if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
+                       alert_tag = "calendar:prompt-delete-calendar";
+               else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST))
+                       alert_tag = "calendar:prompt-delete-memo-list";
+               else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+                       alert_tag = "calendar:prompt-delete-task-list";
+
+               if (alert_tag &&
+                   e_alert_run_dialog_for_args (GTK_WINDOW (accounts_window), alert_tag, 
e_source_get_display_name (source), NULL) == GTK_RESPONSE_YES)
+                       e_source_remove (source, NULL, acconts_window_source_removed_cb, NULL);
+       }
+
+       return TRUE;
+}
+
+static gint
+accounts_window_compare_iters_cb (GtkTreeModel *model,
+                                 GtkTreeIter *aa,
+                                 GtkTreeIter *bb,
+                                 gpointer user_data)
+{
+       gint aa_sort_hint = -1, bb_sort_hint = -1;
+       gchar *aa_display_name = NULL, *bb_display_name = NULL;
+       gint res;
+
+       if (!aa || !bb)
+               return aa == bb ? 0 : bb ? -1 : 1;
+
+       gtk_tree_model_get (model, aa, COLUMN_INT_SORT_HINT, &aa_sort_hint, -1);
+       gtk_tree_model_get (model, bb, COLUMN_INT_SORT_HINT, &bb_sort_hint, -1);
+
+       if (aa_sort_hint != bb_sort_hint)
+               return aa_sort_hint < bb_sort_hint ? -1 : 1;
+
+       gtk_tree_model_get (model, aa, COLUMN_STRING_DISPLAY_NAME, &aa_display_name, -1);
+       gtk_tree_model_get (model, bb, COLUMN_STRING_DISPLAY_NAME, &bb_display_name, -1);
+
+       if (!aa_display_name || !bb_display_name)
+               res = g_strcmp0 (aa_display_name, bb_display_name);
+       else
+               res = g_utf8_collate (aa_display_name, bb_display_name);
+
+       g_free (aa_display_name);
+       g_free (bb_display_name);
+
+       return res;
+}
+
+static GtkWidget *
+accounts_window_tree_view_new (EAccountsWindow *accounts_window)
+{
+       GtkTreeStore *tree_store;
+       GtkTreeView *tree_view;
+       GtkTreeViewColumn *column;
+       GtkTreeModel *sort_model;
+       GtkCellRenderer *cell_renderer;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), NULL);
+
+       tree_store = gtk_tree_store_new (N_COLUMNS,
+               G_TYPE_BOOLEAN, /* COLUMN_BOOL_ENABLED */
+               G_TYPE_BOOLEAN, /* COLUMN_BOOL_ENABLED_VISIBLE */
+               G_TYPE_STRING,  /* COLUMN_STRING_DISPLAY_NAME */
+               G_TYPE_STRING,  /* COLUMN_STRING_ICON_NAME */
+               G_TYPE_BOOLEAN, /* COLUMN_BOOL_ICON_VISIBLE */
+               GDK_TYPE_RGBA,  /* COLUMN_RGBA_COLOR */
+               G_TYPE_BOOLEAN, /* COLUMN_BOOL_COLOR_VISIBLE */
+               G_TYPE_STRING,  /* COLUMN_STRING_TYPE */
+               E_TYPE_SOURCE,  /* COLUMN_OBJECT_SOURCE */
+               G_TYPE_INT,     /* COLUMN_INT_SORT_HINT */
+               G_TYPE_UINT,    /* COLUMN_UINT_EDITING_FLAGS */
+               G_TYPE_BOOLEAN  /* COLUMN_BOOL_DELETE_WHEN_NO_CHILDREN */
+       );
+
+       sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree_store));
+       gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort_model),
+               accounts_window_compare_iters_cb, NULL, NULL);
+
+       tree_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (sort_model));
+
+       g_object_unref (sort_model);
+       g_object_unref (tree_store);
+
+       gtk_tree_view_set_reorderable (tree_view, FALSE);
+
+       /* 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 (accounts_window_tree_view_enabled_toggled_cb), accounts_window);
+
+       gtk_tree_view_column_add_attribute (column, cell_renderer, "active", COLUMN_BOOL_ENABLED);
+       gtk_tree_view_column_add_attribute (column, cell_renderer, "visible", COLUMN_BOOL_ENABLED_VISIBLE);
+       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_pixbuf_new ();
+       g_object_set (cell_renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL);
+       gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
+
+       gtk_tree_view_column_add_attribute (column, cell_renderer, "icon-name", COLUMN_STRING_ICON_NAME);
+       gtk_tree_view_column_add_attribute (column, cell_renderer, "visible", COLUMN_BOOL_ICON_VISIBLE);
+
+       cell_renderer = e_cell_renderer_color_new ();
+       gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
+
+       gtk_tree_view_column_add_attribute (column, cell_renderer, "rgba", COLUMN_RGBA_COLOR);
+       gtk_tree_view_column_add_attribute (column, cell_renderer, "visible", COLUMN_BOOL_COLOR_VISIBLE);
+
+       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, FALSE);
+
+       gtk_tree_view_column_add_attribute (column, cell_renderer, "text", COLUMN_STRING_DISPLAY_NAME);
+
+       gtk_tree_view_append_column (tree_view, column);
+       gtk_tree_view_set_expander_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_STRING_TYPE);
+
+       gtk_tree_view_append_column (tree_view, column);
+
+       return GTK_WIDGET (tree_view);
+}
+
+static void
+accounts_window_add_menu_position (GtkMenu *menu,
+                                  gint *x,
+                                  gint *y,
+                                  gboolean *push_in,
+                                  gpointer user_data)
+{
+       GtkRequisition menu_requisition;
+       GtkTextDirection direction;
+       GtkAllocation allocation;
+       GdkRectangle monitor;
+       GdkScreen *screen;
+       GdkWindow *window;
+       GtkWidget *widget = user_data;
+       gint monitor_num;
+
+       gtk_widget_get_preferred_size (GTK_WIDGET (menu), &menu_requisition, NULL);
+
+       window = gtk_widget_get_parent_window (widget);
+       screen = gtk_widget_get_screen (GTK_WIDGET (menu));
+       monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+       if (monitor_num < 0)
+               monitor_num = 0;
+       gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+       gtk_widget_get_allocation (widget, &allocation);
+
+       gdk_window_get_origin (window, x, y);
+       *x += allocation.x;
+       *y += allocation.y;
+
+       direction = gtk_widget_get_direction (widget);
+       if (direction == GTK_TEXT_DIR_LTR)
+               *x += MAX (allocation.width - menu_requisition.width, 0);
+       else if (menu_requisition.width > allocation.width)
+               *x -= menu_requisition.width - allocation.width;
+
+       gtk_widget_get_allocation (widget, &allocation);
+
+       if ((*y + allocation.height +
+               menu_requisition.height) <= monitor.y + monitor.height)
+               *y += allocation.height;
+       else if ((*y - menu_requisition.height) >= monitor.y)
+               *y -= menu_requisition.height;
+       else if (monitor.y + monitor.height -
+               (*y + allocation.height) > *y)
+               *y += allocation.height;
+       else
+               *y -= menu_requisition.height;
+
+       *push_in = FALSE;
+}
+
+static void
+accounts_window_add_menu_activate_cb (GObject *item,
+                                     gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+       const gchar *kind;
+       gboolean handled = FALSE;
+
+       g_return_if_fail (GTK_IS_MENU_ITEM (item));
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       kind = g_object_get_data (item, ADD_POPUP_KEY_KIND);
+
+       g_return_if_fail (kind && *kind);
+
+       g_signal_emit (accounts_window, signals[ADD_SOURCE], 0, kind, &handled);
+}
+
+static void
+accounts_window_show_add_popup (EAccountsWindow *accounts_window,
+                               GdkEventButton *event)
+{
+       struct _add_items {
+               const gchar *kind;
+               const gchar *text;
+               const gchar *icon_name;
+       } items[] = {
+               /* { "collection",      N_("Collection _Account"),      "evolution" }, */
+               { "mail",       N_("_Mail Account"),            "evolution-mail" },
+               { "book",       N_("Address _Book"),            "x-office-address-book" },
+               { "calendar",   N_("_Calendar"),                "x-office-calendar" },
+               { "memo-list",  N_("M_emo List"),               "evolution-memos" },
+               { "task-list",  N_("_Task List"),               "evolution-tasks" }
+       };
+       GtkWidget *popup_menu;
+       GtkMenuShell *menu_shell;
+       gint ii;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       popup_menu = gtk_menu_new ();
+       menu_shell = GTK_MENU_SHELL (popup_menu);
+
+       for (ii = 0; ii < G_N_ELEMENTS (items); ii++) {
+               e_accounts_window_insert_to_add_popup (accounts_window, menu_shell, items[ii].kind, 
_(items[ii].text), items[ii].icon_name);
+       }
+
+       g_signal_emit (accounts_window, signals[POPULATE_ADD_POPUP], 0, menu_shell);
+
+       g_signal_connect (popup_menu, "deactivate", G_CALLBACK (gtk_menu_detach), NULL);
+
+       gtk_widget_show_all (popup_menu);
+
+       gtk_menu_attach_to_widget (GTK_MENU (popup_menu), accounts_window->priv->add_box, NULL);
+
+       if (event) {
+               gtk_menu_popup (GTK_MENU (popup_menu), NULL, NULL,
+                       accounts_window_add_menu_position, accounts_window->priv->add_box,
+                       event->button, event->time);
+       } else {
+               gtk_menu_popup (GTK_MENU (popup_menu), NULL, NULL,
+                       accounts_window_add_menu_position, accounts_window->priv->add_box,
+                       0, gtk_get_current_event_time ());
+       }
+}
+
+static void
+accounts_window_add_clicked_cb (GtkButton *button,
+                               gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       accounts_window_show_add_popup (accounts_window, NULL);
+}
+
+static gboolean
+accounts_window_add_arrow_button_press_cb (GtkToggleButton *toggle_button,
+                                          GdkEventButton *event,
+                                          gpointer user_data)
+{
+       EAccountsWindow *accounts_window = user_data;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+
+       if (event && event->button == 1) {
+               accounts_window_show_add_popup (accounts_window, event);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static GtkWidget *
+accounts_window_create_add_box (EAccountsWindow *accounts_window)
+{
+       GtkWidget *box, *button, *arrow;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), NULL);
+
+       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+
+       gtk_style_context_add_class (gtk_widget_get_style_context (box), "linked");
+
+       button = e_dialog_button_new_with_icon ("list-add", _("_Add"));
+       gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0);
+
+       g_signal_connect (button, "clicked",
+               G_CALLBACK (accounts_window_add_clicked_cb), accounts_window);
+
+       button = gtk_toggle_button_new ();
+       gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+
+       g_signal_connect (button, "button-press-event",
+               G_CALLBACK (accounts_window_add_arrow_button_press_cb), accounts_window);
+
+       arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+       gtk_container_add (GTK_CONTAINER (button), arrow);
+
+       gtk_widget_show_all (box);
+
+       return box;
+}
+
+static void
+accounts_window_set_registry (EAccountsWindow *accounts_window,
+                             ESourceRegistry *registry)
+{
+       g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+       g_return_if_fail (accounts_window->priv->registry == NULL);
+
+       accounts_window->priv->registry = g_object_ref (registry);
+}
+
+static void
+accounts_window_set_property (GObject *object,
+                             guint property_id,
+                             const GValue *value,
+                             GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_REGISTRY:
+                       accounts_window_set_registry (
+                               E_ACCOUNTS_WINDOW (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+accounts_window_get_property (GObject *object,
+                             guint property_id,
+                             GValue *value,
+                             GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_REGISTRY:
+                       g_value_set_object (
+                               value,
+                               e_accounts_window_get_registry (
+                               E_ACCOUNTS_WINDOW (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+accounts_window_dispose (GObject *object)
+{
+       EAccountsWindow *accounts_window = E_ACCOUNTS_WINDOW (object);
+
+       if (accounts_window->priv->registry) {
+               e_signal_disconnect_notify_handler (accounts_window->priv->registry,
+                       &accounts_window->priv->source_enabled_handler_id);
+
+               e_signal_disconnect_notify_handler (accounts_window->priv->registry,
+                       &accounts_window->priv->source_disabled_handler_id);
+
+               e_signal_disconnect_notify_handler (accounts_window->priv->registry,
+                       &accounts_window->priv->source_added_handler_id);
+
+               e_signal_disconnect_notify_handler (accounts_window->priv->registry,
+                       &accounts_window->priv->source_removed_handler_id);
+
+               e_signal_disconnect_notify_handler (accounts_window->priv->registry,
+                       &accounts_window->priv->source_changed_handler_id);
+
+               g_clear_object (&accounts_window->priv->registry);
+       }
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_accounts_window_parent_class)->dispose (object);
+}
+
+static void
+accounts_window_finalize (GObject *object)
+{
+       EAccountsWindow *accounts_window = E_ACCOUNTS_WINDOW (object);
+
+       g_hash_table_destroy (accounts_window->priv->references);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_accounts_window_parent_class)->finalize (object);
+}
+
+static void
+accounts_window_constructed (GObject *object)
+{
+       EAccountsWindow *accounts_window = E_ACCOUNTS_WINDOW (object);
+       ESourceRegistry *registry;
+       GtkTreeSelection *selection;
+       GtkWidget *container;
+       GtkWidget *widget;
+       GtkGrid *grid;
+       GtkAccelGroup *accel_group;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_accounts_window_parent_class)->constructed (object);
+
+       gtk_window_set_default_size (GTK_WINDOW (accounts_window), 480, 360);
+       gtk_window_set_title (GTK_WINDOW (accounts_window), _("Evolution Accounts"));
+       gtk_container_set_border_width (GTK_CONTAINER (accounts_window), 12);
+
+       widget = gtk_notebook_new ();
+       g_object_set (G_OBJECT (widget),
+               "show-border", FALSE,
+               "show-tabs", FALSE,
+               NULL);
+
+       accounts_window->priv->notebook = widget;
+       gtk_container_add (GTK_CONTAINER (accounts_window), widget);
+
+       container = widget;
+       gtk_widget_show (widget);
+
+       widget = gtk_grid_new ();
+       gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+
+       grid = GTK_GRID (widget);
+       gtk_grid_set_column_spacing (grid, 6);
+
+       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_widget_set_hexpand (widget, TRUE);
+       gtk_widget_set_vexpand (widget, TRUE);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       container = widget;
+
+       widget = accounts_window_tree_view_new (accounts_window);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       accounts_window->priv->tree_view = widget;
+
+       g_signal_connect (
+               widget, "key-press-event",
+               G_CALLBACK (accounts_window_key_press_event_cb),
+               accounts_window);
+
+       g_signal_connect (
+               widget, "row-activated",
+               G_CALLBACK (accounts_window_row_activated_cb), accounts_window);
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+
+       g_signal_connect (selection, "changed",
+               G_CALLBACK (accounts_window_selection_changed_cb), accounts_window);
+
+       widget = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+       gtk_button_box_set_layout (GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_START);
+       gtk_box_set_spacing (GTK_BOX (widget), 6);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       accounts_window->priv->button_box = widget;
+
+       container = widget;
+
+       widget = accounts_window_create_add_box (accounts_window);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       accounts_window->priv->add_box = widget;
+
+       widget = gtk_button_new_with_mnemonic (_("_Edit"));
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       accounts_window->priv->edit_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (accounts_window_emit_edit_source), accounts_window);
+
+       widget = e_dialog_button_new_with_icon ("edit-delete", _("_Delete"));
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       accounts_window->priv->delete_button = widget;
+
+       g_signal_connect_swapped (
+               widget, "clicked",
+               G_CALLBACK (accounts_window_emit_delete_source), accounts_window);
+
+       widget = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+       gtk_button_box_set_layout (GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_END);
+       gtk_box_set_spacing (GTK_BOX (widget), 6);
+       gtk_widget_set_margin_top (widget, 12);
+       gtk_grid_attach (grid, widget, 0, 1, 2, 1);
+
+       container = widget;
+
+       widget = e_dialog_button_new_with_icon ("window-close", _("_Close"));
+       g_signal_connect_swapped (widget, "clicked",
+               G_CALLBACK (gtk_window_close), accounts_window);
+       gtk_widget_set_can_default (widget, TRUE);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       accel_group = gtk_accel_group_new ();
+       gtk_widget_add_accelerator (
+               widget, "activate", accel_group,
+               GDK_KEY_Escape, (GdkModifierType) 0,
+               GTK_ACCEL_VISIBLE);
+       gtk_window_add_accel_group (GTK_WINDOW (accounts_window), accel_group);
+
+       gtk_widget_show_all (GTK_WIDGET (grid));
+
+       /* First load extensions, thus the fill-tree-view can call them. */
+       e_extensible_load_extensions (E_EXTENSIBLE (object));
+
+       accounts_window_fill_tree_view (accounts_window);
+
+       registry = e_accounts_window_get_registry (accounts_window);
+
+       accounts_window->priv->source_enabled_handler_id =
+               g_signal_connect (registry, "source-enabled",
+                       G_CALLBACK (accounts_window_source_enabled_cb), accounts_window);
+
+       accounts_window->priv->source_disabled_handler_id =
+               g_signal_connect (registry, "source-disabled",
+                       G_CALLBACK (accounts_window_source_disabled_cb), accounts_window);
+
+       accounts_window->priv->source_added_handler_id =
+               g_signal_connect (registry, "source-added",
+                       G_CALLBACK (accounts_window_source_added_cb), accounts_window);
+
+       accounts_window->priv->source_removed_handler_id =
+               g_signal_connect (registry, "source-removed",
+                       G_CALLBACK (accounts_window_source_removed_cb), accounts_window);
+
+       accounts_window->priv->source_changed_handler_id =
+               g_signal_connect (registry, "source-changed",
+                       G_CALLBACK (accounts_window_source_changed_cb), accounts_window);
+}
+
+static void
+e_accounts_window_class_init (EAccountsWindowClass *klass)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (EAccountsWindowPrivate));
+
+       klass->get_editing_flags = accounts_window_get_editing_flags_default;
+       klass->delete_source = accounts_window_delete_source_default;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = accounts_window_set_property;
+       object_class->get_property = accounts_window_get_property;
+       object_class->dispose = accounts_window_dispose;
+       object_class->finalize = accounts_window_finalize;
+       object_class->constructed = accounts_window_constructed;
+
+       /**
+        * EAccountsWindow:registry:
+        *
+        * The #ESourceRegistry manages #ESource instances.
+        *
+        * Since: 3.26
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_REGISTRY,
+               g_param_spec_object (
+                       "registry",
+                       "Registry",
+                       "Data source registry",
+                       E_TYPE_SOURCE_REGISTRY,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EAccountsWindow::get-editing-flags:
+        * @source: an #ESource
+        * @out_flags: (out): bit-or of #ESourceEditingFlags
+        *
+        * Emitted to get editing flags for the given @source. The extensions listen
+        * to this signal and the one which can handle this @source sets @out_flags
+        * appropriately. It also returns %TRUE, to stop signal emission. If the extension
+        * cannot work with the given @source, then it simply returns %FALSE.
+        *
+        * Returns: Whether the signal had been handled by any extension.
+        *
+        * Since: 3.26
+        **/
+       signals[GET_EDITING_FLAGS] = g_signal_new (
+               "get-editing-flags",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EAccountsWindowClass, get_editing_flags),
+               g_signal_accumulator_true_handled, NULL,
+               NULL,
+               G_TYPE_BOOLEAN, 2,
+               E_TYPE_SOURCE,
+               G_TYPE_POINTER);
+
+       /**
+        * EAccountsWindow::add-source:
+        * @kind: a UTF-8 string of the kind of source to create
+        *
+        * Emitted to add (create) a new #ESource of the given kind. The extensions can listen
+        * to this signal and can step in and add the source with the appropriate editing dialog.
+        * Such extension also returns %TRUE, to stop signal emission.
+        * If the extension cannot work with the given @kind, then it returns %FALSE.
+        *
+        * Currently known kinds are "collection", "mail", "book", "calendar",  "memo-list" and
+        * "task-list". Extensions can add their own kinds with e_accounts_window_insert_to_add_popup()
+        * from EAccountsWindow::populate-add-popup signal.
+        *
+        * Returns: Whether the signal had been handled by any extension.
+        *
+        * Since: 3.26
+        **/
+       signals[ADD_SOURCE] = g_signal_new (
+               "add-source",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EAccountsWindowClass, add_source),
+               g_signal_accumulator_true_handled, NULL,
+               NULL,
+               G_TYPE_BOOLEAN, 1,
+               G_TYPE_STRING);
+
+       /**
+        * EAccountsWindow::edit-source:
+        * @source: an #ESource
+        *
+        * Emitted to edit the given @source. The extensions listen to this signal and
+        * the one which also set #EAccountsWindow::get-edit-flags for this @source
+        * will open appropriate editing dialog. It also returns %TRUE, to stop signal emission.
+        * If the extension cannot work with the given @source, then it returns %FALSE.
+        *
+        * Returns: Whether the signal had been handled by any extension.
+        *
+        * Since: 3.26
+        **/
+       signals[EDIT_SOURCE] = g_signal_new (
+               "edit-source",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EAccountsWindowClass, edit_source),
+               g_signal_accumulator_true_handled, NULL,
+               NULL,
+               G_TYPE_BOOLEAN, 1,
+               E_TYPE_SOURCE);
+
+       /**
+        * EAccountsWindow::delete-source:
+        * @source: an #ESource
+        *
+        * Emitted to delete the given @source. The extensions listen to this signal and
+        * the one which also set #EAccountsWindow::get-edit-flags for this @source
+        * will remove the source. It also returns %TRUE, to stop signal emission.
+        * If the extension cannot work with the given @source, then it returns %FALSE.
+        *
+        * Returns: Whether the signal had been handled by any extension.
+        *
+        * Since: 3.26
+        **/
+       signals[DELETE_SOURCE] = g_signal_new (
+               "delete-source",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EAccountsWindowClass, delete_source),
+               g_signal_accumulator_true_handled, NULL,
+               NULL,
+               G_TYPE_BOOLEAN, 1,
+               E_TYPE_SOURCE);
+
+       /**
+        * EAccountsWindow::enabled-toggled:
+        * @source: an #ESource
+        *
+        * Emitted after @source-s enable property had been toggled in the tree view.
+        *
+        * Since: 3.26
+        **/
+       signals[ENABLED_TOGGLED] = g_signal_new (
+               "enabled-toggled",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EAccountsWindowClass, enabled_toggled),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 1,
+               E_TYPE_SOURCE);
+
+       /**
+        * EAccountsWindow::populate-add-popup:
+        * @popup_menu: a #GtkMenuShell, the popup menu
+        *
+        * Emitted before Add popup is shown. It is already populated with default
+        * source types. The signal listener can use e_accounts_window_insert_to_add_popup()
+        * to add items to it.
+        *
+        * Since: 3.26
+        **/
+       signals[POPULATE_ADD_POPUP] = g_signal_new (
+               "populate-add-popup",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EAccountsWindowClass, populate_add_popup),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 1,
+               GTK_TYPE_MENU_SHELL);
+
+       /**
+        * EAccountsWindow::selection-changed:
+        * @source: (nullable): an #ESource, or %NULL
+        *
+        * Emitted after selection in the account tree view change. The @source is the selected #ESource,
+        * but can be %NULL, when the selected row has no associated #ESource, or nothing is selected.
+        *
+        * Since: 3.26
+        **/
+       signals[SELECTION_CHANGED] = g_signal_new (
+               "selection-changed",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (EAccountsWindowClass, selection_changed),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 1,
+               E_TYPE_SOURCE);
+}
+
+static void
+e_accounts_window_init (EAccountsWindow *accounts_window)
+{
+       accounts_window->priv = G_TYPE_INSTANCE_GET_PRIVATE (accounts_window, E_TYPE_ACCOUNTS_WINDOW, 
EAccountsWindowPrivate);
+
+       accounts_window->priv->references = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) gtk_tree_row_reference_free);
+}
+
+/**
+ * e_accounts_window_new:
+ * @registry: an #ESourceRegistry
+ *
+ * Creates a new #EAccountsWindow instance.
+ *
+ * Returns: (transfer full): an #EAccountsWindow as a #GtkWidget
+ *
+ * Since: 3.26
+ **/
+GtkWidget *
+e_accounts_window_new (ESourceRegistry *registry)
+{
+       g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+       return g_object_new (E_TYPE_ACCOUNTS_WINDOW,
+               "registry", registry,
+               NULL);
+}
+
+/**
+ * e_accounts_window_get_registry:
+ * @accounts_window: an #EAccountsWindow
+ *
+ * Returns the #ESourceRegistry passed to e_accounts_window_new().
+ *
+ * Returns: (transfer none): an #ESourceRegistry
+ *
+ * Since: 3.26
+ **/
+ESourceRegistry *
+e_accounts_window_get_registry (EAccountsWindow *accounts_window)
+{
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), NULL);
+
+       return accounts_window->priv->registry;
+}
+
+/**
+ * e_accounts_window_show_with_parent:
+ * @accounts_window: an #EAccountsWindow
+ * @parent: (nullable): a #GtkWindow, parent to show the @accounts_window on top of, or %NULL
+ *
+ * Shows the @accounts_window on top of the @parent, if not %NULL.
+ *
+ * Since: 3.26
+ **/
+void
+e_accounts_window_show_with_parent (EAccountsWindow *accounts_window,
+                                   GtkWindow *parent)
+{
+       GtkWindow *window;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+       if (parent)
+               g_return_if_fail (GTK_IS_WINDOW (parent));
+
+       window = GTK_WINDOW (accounts_window);
+
+       gtk_window_set_transient_for (window, parent);
+       gtk_window_set_position (window, parent ? GTK_WIN_POS_CENTER_ON_PARENT : GTK_WIN_POS_CENTER);
+
+       gtk_window_present (window);
+}
+
+/**
+ * e_accounts_window_ref_selected_source:
+ * @accounts_window: an #EAccountsWindow
+ *
+ * Returns: (nullable) (transfer full): Referenced selected #ESource, which should be unreffed
+ *    with g_object_unref(), when no longer needed, or %NULL, when there is no source selected.
+ *
+ * Since: 3.26
+ **/
+ESource *
+e_accounts_window_ref_selected_source (EAccountsWindow *accounts_window)
+{
+       GtkTreeSelection *selection;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       ESource *source = NULL;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), NULL);
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (accounts_window->priv->tree_view));
+       if (gtk_tree_selection_get_selected (selection, &model, &iter))
+               gtk_tree_model_get (model, &iter, COLUMN_OBJECT_SOURCE, &source, -1);
+
+       return source;
+}
+
+/**
+ * e_accounts_window_insert_to_add_popup:
+ * @accounts_window: an #EAccountsWindow
+ * @popup_menu: a #GtkMenuShell
+ * @kind: (nullable): item kind, or %NULL, when @label is "-"
+ * @label: item label, possibly with a mnemonic
+ * @icon_name: (nullable): optional icon name to use for the menu item, or %NULL
+ *
+ * Adds a new item into the @popup_menu, which will be labeled with @label.
+ * Items added this way are executed with EAccountsWindow::add-source signal.
+ *
+ * Special case "-" can be used for the @label to add a separator. In that
+ * case the @kind and the @icon_name parameters are ignored.
+ *
+ * Since: 3.26
+ **/
+void
+e_accounts_window_insert_to_add_popup (EAccountsWindow *accounts_window,
+                                      GtkMenuShell *popup_menu,
+                                      const gchar *kind,
+                                      const gchar *label,
+                                      const gchar *icon_name)
+{
+       GtkWidget *item;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+       g_return_if_fail (GTK_IS_MENU_SHELL (popup_menu));
+
+       if (g_strcmp0 (label, "-") == 0) {
+               item = gtk_separator_menu_item_new ();
+               gtk_menu_shell_append (popup_menu, item);
+
+               return;
+       }
+
+       g_return_if_fail (kind != NULL);
+       g_return_if_fail (label != NULL);
+
+       if (icon_name) {
+               item = gtk_image_menu_item_new_with_mnemonic (label);
+
+               gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+                       gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU));
+       } else {
+               item = gtk_menu_item_new_with_mnemonic (label);
+       }
+
+       g_object_set_data_full (G_OBJECT (item), ADD_POPUP_KEY_KIND, g_strdup (kind), g_free);
+
+       g_signal_connect (item, "activate", G_CALLBACK (accounts_window_add_menu_activate_cb), 
accounts_window);
+
+       gtk_menu_shell_append (popup_menu, item);
+}
+
+/**
+ * e_accounts_window_get_button_box:
+ * @accounts_window: an #EAccountsWindow
+ *
+ * Returns: (transfer none): the button box of the main page, where action
+ *    buttons are stored. It can be used to add other actions to it.
+ *
+ * Since: 3.26
+ **/
+GtkButtonBox *
+e_accounts_window_get_button_box (EAccountsWindow *accounts_window)
+{
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), NULL);
+
+       return GTK_BUTTON_BOX (accounts_window->priv->button_box);
+}
+
+/**
+ * e_accounts_window_add_page:
+ * @accounts_window: an #EAccountsWindow
+ * @content: a #GtkWidget, the page content
+ *
+ * Adds a new hidden page to the account window with content @content.
+ * The returned integer is the index of the added page, which can be used
+ * with e_accounts_window_activate_page() to make that page active.
+ *
+ * Returns: index of the added page, or -1 on error.
+ *
+ * Since: 3.26
+ **/
+gint
+e_accounts_window_add_page (EAccountsWindow *accounts_window,
+                           GtkWidget *content)
+{
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), -1);
+       g_return_val_if_fail (GTK_IS_WIDGET (content), -1);
+
+       return gtk_notebook_append_page (GTK_NOTEBOOK (accounts_window->priv->notebook), content, NULL);
+}
+
+/**
+ * e_accounts_window_activate_page:
+ * @accounts_window: an #EAccountsWindow
+ * @page_index: an index of the page to activate
+ *
+ * Activates certain page in the @accounts_window. The @page_index should
+ * be the one returned by e_accounts_window_add_page(). Using value out of
+ * bounds selects the main page, which shows listing of configured accounts.
+ *
+ * Since: 3.26
+ **/
+void
+e_accounts_window_activate_page (EAccountsWindow *accounts_window,
+                                gint page_index)
+{
+       GtkNotebook *notebook;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+       notebook = GTK_NOTEBOOK (accounts_window->priv->notebook);
+
+       if (page_index < 0 || page_index >= gtk_notebook_get_n_pages (notebook))
+               page_index = 0;
+
+       gtk_notebook_set_current_page (notebook, page_index);
+}
diff --git a/src/e-util/e-accounts-window.h b/src/e-util/e-accounts-window.h
new file mode 100644
index 0000000..1eacc80
--- /dev/null
+++ b/src/e-util/e-accounts-window.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_ACCOUNTS_WINDOW_H
+#define E_ACCOUNTS_WINDOW_H
+
+#include <gtk/gtk.h>
+#include <libedataserver/libedataserver.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ACCOUNTS_WINDOW \
+       (e_accounts_window_get_type ())
+#define E_ACCOUNTS_WINDOW(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_ACCOUNTS_WINDOW, EAccountsWindow))
+#define E_ACCOUNTS_WINDOW_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_ACCOUNTS_WINDOW, EAccountsWindowClass))
+#define E_IS_ACCOUNTS_WINDOW(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_ACCOUNTS_WINDOW))
+#define E_IS_ACCOUNTS_WINDOW_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_ACCOUNTS_WINDOW))
+#define E_ACCOUNTS_WINDOW_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_ACCOUNTS_WINDOW, EAccountsWindowClass))
+
+G_BEGIN_DECLS
+
+typedef enum {
+       E_SOURCE_EDITING_FLAG_NONE              = 0,
+       E_SOURCE_EDITING_FLAG_CAN_ENABLE        = (1 << 0),
+       E_SOURCE_EDITING_FLAG_CAN_EDIT          = (1 << 1),
+       E_SOURCE_EDITING_FLAG_CAN_DELETE        = (1 << 2)
+} ESourceEditingFlags;
+
+typedef struct _EAccountsWindow EAccountsWindow;
+typedef struct _EAccountsWindowClass EAccountsWindowClass;
+typedef struct _EAccountsWindowPrivate EAccountsWindowPrivate;
+
+/**
+ * EAccountsWindow:
+ *
+ * Contains only private data that should be read and manipulated using
+ * the functions below.
+ *
+ * Since: 3.26
+ **/
+struct _EAccountsWindow {
+       GtkWindow parent;
+       EAccountsWindowPrivate *priv;
+};
+
+struct _EAccountsWindowClass {
+       GtkWindowClass parent_class;
+
+       /* Signals */
+       gboolean        (* get_editing_flags)   (EAccountsWindow *accounts_window,
+                                                ESource *source,
+                                                guint *out_flags); /* bit-or of ESourceEditingFlags */
+       gboolean        (* add_source)          (EAccountsWindow *accounts_window,
+                                                const gchar *kind);
+       gboolean        (* edit_source)         (EAccountsWindow *accounts_window,
+                                                ESource *source);
+       gboolean        (* delete_source)       (EAccountsWindow *accounts_window,
+                                                ESource *source);
+       void            (* enabled_toggled)     (EAccountsWindow *accounts_window,
+                                                ESource *source);
+       void            (* populate_add_popup)  (EAccountsWindow *accounts_window,
+                                                GtkMenuShell *popup_menu);
+       void            (* selection_changed)   (EAccountsWindow *accounts_window,
+                                                ESource *source);
+};
+
+GType          e_accounts_window_get_type              (void) G_GNUC_CONST;
+GtkWidget *    e_accounts_window_new                   (ESourceRegistry *registry);
+ESourceRegistry *
+               e_accounts_window_get_registry          (EAccountsWindow *accounts_window);
+void           e_accounts_window_show_with_parent      (EAccountsWindow *accounts_window,
+                                                        GtkWindow *parent);
+ESource *      e_accounts_window_ref_selected_source   (EAccountsWindow *accounts_window);
+void           e_accounts_window_insert_to_add_popup   (EAccountsWindow *accounts_window,
+                                                        GtkMenuShell *popup_menu,
+                                                        const gchar *kind,
+                                                        const gchar *label,
+                                                        const gchar *icon_name);
+GtkButtonBox * e_accounts_window_get_button_box        (EAccountsWindow *accounts_window);
+gint           e_accounts_window_add_page              (EAccountsWindow *accounts_window,
+                                                        GtkWidget *content);
+void           e_accounts_window_activate_page         (EAccountsWindow *accounts_window,
+                                                        gint page_index);
+
+G_END_DECLS
+
+#endif /* E_ACCOUNTS_WINDOW_H */
diff --git a/src/e-util/e-util.h b/src/e-util/e-util.h
index 8b3d163..b448f5f 100644
--- a/src/e-util/e-util.h
+++ b/src/e-util/e-util.h
@@ -22,6 +22,7 @@
 
 #include <libedataserver/libedataserver.h>
 
+#include <e-util/e-accounts-window.h>
 #include <e-util/e-action-combo-box.h>
 #include <e-util/e-activity-bar.h>
 #include <e-util/e-activity-proxy.h>
diff --git a/src/e-util/test-accounts-window.c b/src/e-util/test-accounts-window.c
new file mode 100644
index 0000000..80051e2
--- /dev/null
+++ b/src/e-util/test-accounts-window.c
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-config.h"
+
+#include <gtk/gtk.h>
+
+#include "e-util.h"
+
+static gboolean
+delete_event_cb (GtkWidget *widget,
+                GdkEvent *event,
+                gpointer user_data)
+{
+       GMainLoop *loop = user_data;
+
+       g_main_loop_quit (loop);
+
+       return FALSE;
+}
+
+gint
+main (gint argc,
+      gchar *argv[])
+{
+       ESourceRegistry *registry;
+       GtkWidget *window;
+       GMainLoop *loop;
+       GList *modules;
+       GError *error = NULL;
+
+       gtk_init (&argc, &argv);
+
+       e_util_init_main_thread (NULL);
+
+       registry = e_source_registry_new_sync (NULL, &error);
+       if (error) {
+               g_warning ("Failed to create source registry: %s", error->message);
+               g_clear_error (&error);
+               return 1;
+       }
+
+       modules = e_module_load_all_in_directory (EVOLUTION_MODULEDIR);
+
+       loop = g_main_loop_new (NULL, FALSE);
+
+       window = e_accounts_window_new (registry);
+
+       g_signal_connect (window, "delete-event", G_CALLBACK (delete_event_cb), loop);
+
+       e_accounts_window_show_with_parent (E_ACCOUNTS_WINDOW (window), NULL);
+
+       g_main_loop_run (loop);
+       g_main_loop_unref (loop);
+
+       g_object_unref (registry);
+
+       g_list_free_full (modules, (GDestroyNotify) g_type_module_unuse);
+       e_util_cleanup_settings ();
+
+       return 0;
+}
diff --git a/src/mail/e-mail-account-store.c b/src/mail/e-mail-account-store.c
index 4000944..09b96c1 100644
--- a/src/mail/e-mail-account-store.c
+++ b/src/mail/e-mail-account-store.c
@@ -841,7 +841,7 @@ mail_account_store_remove_requested (EMailAccountStore *store,
         *       and doesn't belong here anyway.  Think of a better idea. */
 
        response = e_alert_run_dialog_for_args (
-               parent_window, "mail:ask-delete-account", NULL);
+               parent_window, "mail:ask-delete-account", camel_service_get_display_name (service), NULL);
 
        return (response == GTK_RESPONSE_YES);
 }
diff --git a/src/mail/mail.error.xml b/src/mail/mail.error.xml
index 5ebeb41..f36ee73 100644
--- a/src/mail/mail.error.xml
+++ b/src/mail/mail.error.xml
@@ -293,7 +293,7 @@ The reported error was “{0}”.</_secondary>
   </error>
 
   <error id="ask-delete-account" type="question" default="GTK_RESPONSE_NO">
-    <_primary>Are you sure you want to delete this account?</_primary>
+    <_primary>Are you sure you want to delete account “{0}”?</_primary>
     <_secondary xml:space="preserve">If you proceed, the account information will be deleted 
permanently.</_secondary>
     <button _label="Do _Not Delete" response="GTK_RESPONSE_NO"/>
     <button stock="gtk-delete" response="GTK_RESPONSE_YES"/>
diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt
index b554ec0..070a898 100644
--- a/src/modules/CMakeLists.txt
+++ b/src/modules/CMakeLists.txt
@@ -90,6 +90,7 @@ endmacro(add_webextension_editor_module)
 add_subdirectory(addressbook)
 add_subdirectory(calendar)
 add_subdirectory(mail)
+add_subdirectory(accounts-window)
 add_subdirectory(backup-restore)
 add_subdirectory(book-config-google)
 add_subdirectory(book-config-local)
diff --git a/src/modules/accounts-window/CMakeLists.txt b/src/modules/accounts-window/CMakeLists.txt
new file mode 100644
index 0000000..864076b
--- /dev/null
+++ b/src/modules/accounts-window/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(extra_deps
+       evolution-mail
+       evolution-shell
+)
+set(sources
+       accounts-window.c
+       e-accounts-window-editors.c
+       e-accounts-window-editors.h
+)
+set(extra_defines)
+set(extra_cflags)
+set(extra_incdirs)
+set(extra_ldflags)
+
+add_evolution_module(module-accounts-window
+       sources
+       extra_deps
+       extra_defines
+       extra_cflags
+       extra_incdirs
+       extra_ldflags
+)
diff --git a/src/modules/accounts-window/accounts-window.c b/src/modules/accounts-window/accounts-window.c
new file mode 100644
index 0000000..6a8e07a
--- /dev/null
+++ b/src/modules/accounts-window/accounts-window.c
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-config.h"
+
+#include <gmodule.h>
+#include <glib-object.h>
+
+#include "e-accounts-window-editors.h"
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+       e_accounts_window_editors_type_register (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/src/modules/accounts-window/e-accounts-window-editors.c 
b/src/modules/accounts-window/e-accounts-window-editors.c
new file mode 100644
index 0000000..967251a
--- /dev/null
+++ b/src/modules/accounts-window/e-accounts-window-editors.c
@@ -0,0 +1,648 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-util/e-util.h"
+#include "mail/e-mail-account-store.h"
+#include "mail/e-mail-ui-session.h"
+#include "shell/e-shell.h"
+
+#include "e-accounts-window-editors.h"
+
+/* Standard GObject macros */
+#define E_TYPE_ACCOUNTS_WINDOW_EDITORS \
+       (e_accounts_window_editors_get_type ())
+#define E_ACCOUNTS_WINDOW_EDITORS(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_ACCOUNTS_WINDOW_EDITORS, EAccountsWindowEditors))
+#define E_ACCOUNTS_WINDOW_EDITORS_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_ACCOUNTS_WINDOW_EDITORS, EAccountsWindowEditorsClass))
+#define E_IS_ACCOUNTS_WINDOW_EDITORS(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_ACCOUNTS_WINDOW_EDITORS))
+#define E_IS_ACCOUNTS_WINDOW_EDITORS_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_ACCOUNTS_WINDOW_EDITORS))
+#define E_ACCOUNTS_WINDOW_EDITORS_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_ACCOUNTS_WINDOW_EDITORS, EAccountsWindowEditorsClass))
+
+typedef struct _EAccountsWindowEditors EAccountsWindowEditors;
+typedef struct _EAccountsWindowEditorsClass EAccountsWindowEditorsClass;
+
+struct _EAccountsWindowEditors {
+       EExtension parent;
+
+       gchar *gcc_program_path;
+};
+
+struct _EAccountsWindowEditorsClass {
+       EExtensionClass parent_class;
+};
+
+GType e_accounts_window_editors_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_DYNAMIC_TYPE (EAccountsWindowEditors, e_accounts_window_editors, E_TYPE_EXTENSION)
+
+static void
+accounts_window_editors_open_goa (EAccountsWindowEditors *editors,
+                                 ESource *source)
+{
+       gchar *goa_account_id;
+       gchar *command_line;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW_EDITORS (editors));
+       g_return_if_fail (E_IS_SOURCE (source));
+       g_return_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_GOA));
+       g_return_if_fail (editors->gcc_program_path != NULL);
+
+       goa_account_id = e_source_goa_dup_account_id (e_source_get_extension (source, 
E_SOURCE_EXTENSION_GOA));
+
+       command_line = g_strjoin (
+               " ",
+               editors->gcc_program_path,
+               "online-accounts",
+               goa_account_id,
+               NULL);
+       g_spawn_command_line_async (command_line, &error);
+
+       g_free (command_line);
+       g_free (goa_account_id);
+
+       if (error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+}
+
+static void
+accounts_window_editors_open_uoa (EAccountsWindowEditors *editors,
+                                 ESource *source)
+{
+       guint uoa_account_id;
+       gchar *account_details;
+       gchar *command_line;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW_EDITORS (editors));
+       g_return_if_fail (E_IS_SOURCE (source));
+       g_return_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_UOA));
+       g_return_if_fail (editors->gcc_program_path != NULL);
+
+       uoa_account_id = e_source_uoa_get_account_id (e_source_get_extension (source, 
E_SOURCE_EXTENSION_UOA));
+
+       account_details = g_strdup_printf ("account-details=%u", uoa_account_id);
+       command_line = g_strjoin (
+               " ",
+               editors->gcc_program_path,
+               "credentials",
+               account_details,
+               NULL);
+       g_spawn_command_line_async (command_line, &error);
+       g_free (command_line);
+       g_free (account_details);
+
+       if (error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+}
+
+#define COLLECTION_EDITOR_DATA_KEY "collection-editor-data-key"
+
+typedef struct _CollectionEditorData {
+       ESource *source;
+       GtkWidget *alert_bar;
+       GtkWidget *display_name;
+       GtkWidget *mail_part;
+       GtkWidget *calendar_part;
+       GtkWidget *contacts_part;
+} CollectionEditorData;
+
+static void
+collection_editor_data_free (gpointer ptr)
+{
+       CollectionEditorData *ced = ptr;
+
+       if (ced) {
+               g_clear_object (&ced->source);
+               g_free (ced);
+       }
+}
+
+static void
+accounts_window_editors_source_written_cb (GObject *source_object,
+                                          GAsyncResult *result,
+                                          gpointer user_data)
+{
+       GtkWidget *dialog = user_data;
+       CollectionEditorData *ced;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_SOURCE (source_object));
+       g_return_if_fail (GTK_IS_DIALOG (dialog));
+
+       gtk_widget_set_sensitive (dialog, TRUE);
+
+       ced = g_object_get_data (G_OBJECT (dialog), COLLECTION_EDITOR_DATA_KEY);
+       g_return_if_fail (ced != NULL);
+
+       if (!e_source_write_finish (E_SOURCE (source_object), result, &error)) {
+               EAlert *alert;
+
+               alert = e_alert_new ("system:simple-error", error ? error->message : _("Unknown error"), 
NULL);
+
+               e_alert_bar_add_alert (E_ALERT_BAR (ced->alert_bar), alert);
+
+               g_clear_error (&error);
+       } else {
+               gtk_widget_destroy (dialog);
+       }
+}
+
+static void
+accounts_window_editors_collection_editor_response_cb (GtkWidget *dialog,
+                                                      gint response_id,
+                                                      gpointer user_data)
+{
+       CollectionEditorData *ced;
+
+       g_return_if_fail (GTK_IS_DIALOG (dialog));
+
+       ced = g_object_get_data (G_OBJECT (dialog), COLLECTION_EDITOR_DATA_KEY);
+       g_return_if_fail (ced != NULL);
+
+       if (response_id == GTK_RESPONSE_OK) {
+               ESourceCollection *collection_extension;
+               gboolean changed;
+
+               collection_extension = e_source_get_extension (ced->source, E_SOURCE_EXTENSION_COLLECTION);
+
+               changed = g_strcmp0 (e_source_get_display_name (ced->source), gtk_entry_get_text (GTK_ENTRY 
(ced->display_name))) != 0;
+
+               changed = changed || e_source_collection_get_mail_enabled (collection_extension) !=
+                       gtk_switch_get_active (GTK_SWITCH (ced->mail_part));
+
+               changed = changed || e_source_collection_get_calendar_enabled (collection_extension) !=
+                       gtk_switch_get_active (GTK_SWITCH (ced->calendar_part));
+
+               changed = changed || e_source_collection_get_contacts_enabled (collection_extension) !=
+                       gtk_switch_get_active (GTK_SWITCH (ced->contacts_part));
+
+               if (changed) {
+                       e_alert_bar_clear (E_ALERT_BAR (ced->alert_bar));
+
+                       e_source_set_display_name (ced->source, gtk_entry_get_text (GTK_ENTRY 
(ced->display_name)));
+
+                       e_source_collection_set_mail_enabled (collection_extension,
+                               gtk_switch_get_active (GTK_SWITCH (ced->mail_part)));
+
+                       e_source_collection_set_calendar_enabled (collection_extension,
+                               gtk_switch_get_active (GTK_SWITCH (ced->calendar_part)));
+
+                       e_source_collection_set_contacts_enabled (collection_extension,
+                               gtk_switch_get_active (GTK_SWITCH (ced->contacts_part)));
+
+                       gtk_widget_set_sensitive (dialog, FALSE);
+
+                       e_source_write (ced->source, NULL,
+                               accounts_window_editors_source_written_cb, dialog);
+
+                       return;
+               }
+       }
+
+       gtk_widget_destroy (dialog);
+}
+
+static void
+accounts_window_editors_collection_editor_display_name_changed_cb (GtkEntry *entry,
+                                                                  gpointer user_data)
+{
+       GtkDialog *dialog = user_data;
+       gchar *text;
+
+       g_return_if_fail (GTK_IS_ENTRY (entry));
+       g_return_if_fail (GTK_IS_DIALOG (dialog));
+
+       text = g_strdup (gtk_entry_get_text (entry));
+
+       if (text)
+               text = g_strstrip (text);
+
+       gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_OK, text && *text);
+
+       g_free (text);
+}
+
+static void
+accounts_window_editors_edit_unmanaged_collection (EAccountsWindow *accounts_window,
+                                                  ESource *source)
+{
+       GtkWidget *dialog, *widget, *label, *container;
+       GtkGrid *grid;
+       CollectionEditorData *ced;
+       ESourceCollection *collection_extension;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+       g_return_if_fail (E_IS_SOURCE (source));
+       g_return_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION));
+
+       ced = g_new0 (CollectionEditorData, 1);
+       ced->source = g_object_ref (source);
+
+       collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
+
+       dialog = gtk_dialog_new_with_buttons (_("Edit Collection"), GTK_WINDOW (accounts_window),
+               GTK_DIALOG_DESTROY_WITH_PARENT,
+               _("_Cancel"), GTK_RESPONSE_CANCEL,
+               _("_OK"), GTK_RESPONSE_OK,
+               NULL);
+
+       gtk_container_set_border_width (GTK_CONTAINER (dialog), 12);
+
+       container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+       gtk_window_set_icon_name (GTK_WINDOW (dialog), "evolution");
+
+       widget = e_alert_bar_new ();
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       ced->alert_bar = widget;
+
+       widget = gtk_grid_new ();
+       gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+
+       grid = GTK_GRID (widget);
+       gtk_grid_set_column_spacing (grid, 6);
+
+       label = gtk_label_new_with_mnemonic (_("_Name:"));
+       gtk_widget_set_halign (label, GTK_ALIGN_END);
+
+       gtk_grid_attach (grid, label, 0, 0, 1, 1);
+
+       widget = gtk_entry_new ();
+       gtk_entry_set_text (GTK_ENTRY (widget), e_source_get_display_name (source));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+       ced->display_name = widget;
+
+       gtk_grid_attach (grid, widget, 1, 0, 2, 1);
+
+       g_signal_connect (ced->display_name, "changed",
+               G_CALLBACK (accounts_window_editors_collection_editor_display_name_changed_cb), dialog);
+
+       label = gtk_label_new (_("Use for"));
+       gtk_widget_set_halign (label, GTK_ALIGN_END);
+       gtk_grid_attach (grid, label, 0, 1, 1, 1);
+
+       label = gtk_label_new_with_mnemonic (_("_Mail"));
+       gtk_widget_set_halign (label, GTK_ALIGN_START);
+       gtk_grid_attach (grid, label, 1, 1, 1, 1);
+
+       widget = gtk_switch_new ();
+       gtk_switch_set_active (GTK_SWITCH (widget), e_source_collection_get_mail_enabled 
(collection_extension));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+       ced->mail_part = widget;
+
+       gtk_grid_attach (grid, widget, 2, 1, 1, 1);
+
+       label = gtk_label_new_with_mnemonic (_("C_alendar"));
+       gtk_widget_set_halign (label, GTK_ALIGN_START);
+       gtk_grid_attach (grid, label, 1, 2, 1, 1);
+
+       widget = gtk_switch_new ();
+       gtk_switch_set_active (GTK_SWITCH (widget), e_source_collection_get_calendar_enabled 
(collection_extension));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+       ced->calendar_part = widget;
+
+       gtk_grid_attach (grid, widget, 2, 2, 1, 1);
+
+       label = gtk_label_new_with_mnemonic (_("Co_ntacts"));
+       gtk_widget_set_halign (label, GTK_ALIGN_START);
+       gtk_grid_attach (grid, label, 1, 3, 1, 1);
+
+       widget = gtk_switch_new ();
+       gtk_switch_set_active (GTK_SWITCH (widget), e_source_collection_get_contacts_enabled 
(collection_extension));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+       ced->contacts_part = widget;
+
+       gtk_grid_attach (grid, widget, 2, 3, 1, 1);
+
+       gtk_widget_show_all (GTK_WIDGET (grid));
+
+       g_object_set_data_full (G_OBJECT (dialog), COLLECTION_EDITOR_DATA_KEY, ced, 
collection_editor_data_free);
+
+       g_signal_connect (dialog, "response",
+               G_CALLBACK (accounts_window_editors_collection_editor_response_cb), NULL);
+
+       gtk_widget_show (dialog);
+}
+
+static gboolean
+accounts_window_editors_get_editing_flags_cb (EAccountsWindow *accounts_window,
+                                             ESource *source,
+                                             guint *out_flags,
+                                             gpointer user_data)
+{
+       EAccountsWindowEditors *editors = user_data;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW_EDITORS (editors), FALSE);
+       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+       g_return_val_if_fail (out_flags != NULL, FALSE);
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) ||
+           e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK) ||
+           e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR) ||
+           e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST) ||
+           e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
+               *out_flags = E_SOURCE_EDITING_FLAG_CAN_ENABLE | E_SOURCE_EDITING_FLAG_CAN_EDIT | 
E_SOURCE_EDITING_FLAG_CAN_DELETE;
+               return TRUE;
+       }
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
+               if (e_source_has_extension (source, E_SOURCE_EXTENSION_GOA) ||
+                   e_source_has_extension (source, E_SOURCE_EXTENSION_UOA)) {
+                       if (editors->gcc_program_path)
+                               *out_flags = E_SOURCE_EDITING_FLAG_CAN_EDIT;
+                       else
+                               *out_flags = E_SOURCE_EDITING_FLAG_NONE;
+               } else {
+                       *out_flags = E_SOURCE_EDITING_FLAG_CAN_ENABLE | E_SOURCE_EDITING_FLAG_CAN_EDIT | 
E_SOURCE_EDITING_FLAG_CAN_DELETE;
+               }
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+accounts_window_editors_add_source_cb (EAccountsWindow *accounts_window,
+                                      const gchar *kind,
+                                      gpointer user_data)
+{
+       EAccountsWindowEditors *editors = user_data;
+       ESourceRegistry *registry;
+       GtkWidget *config = NULL;
+       const gchar *icon_name = NULL;
+       const gchar *title = NULL;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW_EDITORS (editors), FALSE);
+       g_return_val_if_fail (kind && *kind, FALSE);
+
+       registry = e_accounts_window_get_registry (accounts_window);
+
+       if (g_strcmp0 (kind, "mail") == 0) {
+               EShellBackend *shell_backend;
+               EShell *shell = e_shell_get_default ();
+
+               if (shell) {
+                       shell_backend = e_shell_get_backend_by_name (shell, "mail");
+
+                       g_signal_emit_by_name (shell_backend, "new-account", GTK_WINDOW (accounts_window));
+               }
+
+               return TRUE;
+       } else if (g_strcmp0 (kind, "book") == 0) {
+               icon_name = "x-office-address-book";
+               title = _("New Address Book");
+               config = e_book_source_config_new (registry, NULL);
+       } else if (g_strcmp0 (kind, "calendar") == 0) {
+               icon_name = "x-office-calendar";
+               title = _("New Calendar");
+               config = e_cal_source_config_new (registry, NULL, E_CAL_CLIENT_SOURCE_TYPE_EVENTS);
+       } else if (g_strcmp0 (kind, "memo-list") == 0) {
+               icon_name = "evolution-memos";
+               title = _("New Memo List");
+               config = e_cal_source_config_new (registry, NULL, E_CAL_CLIENT_SOURCE_TYPE_MEMOS);
+       } else if (g_strcmp0 (kind, "task-list") == 0) {
+               icon_name = "evolution-tasks";
+               title = _("New Task List");
+               config = e_cal_source_config_new (registry, NULL, E_CAL_CLIENT_SOURCE_TYPE_TASKS);
+       }
+
+       if (config) {
+               GtkWidget *dialog;
+
+               dialog = e_source_config_dialog_new (E_SOURCE_CONFIG (config));
+
+               gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (accounts_window));
+               gtk_window_set_icon_name (GTK_WINDOW (dialog), icon_name);
+               gtk_window_set_title (GTK_WINDOW (dialog), title);
+
+               gtk_widget_show (dialog);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+accounts_window_editors_edit_source_cb (EAccountsWindow *accounts_window,
+                                       ESource *source,
+                                       gpointer user_data)
+{
+       EAccountsWindowEditors *editors = user_data;
+       ESourceRegistry *registry;
+       GtkWidget *config = NULL;
+       const gchar *icon_name = NULL;
+       const gchar *title = NULL;
+
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window), FALSE);
+       g_return_val_if_fail (E_IS_ACCOUNTS_WINDOW_EDITORS (editors), FALSE);
+       g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+       registry = e_accounts_window_get_registry (accounts_window);
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
+               icon_name = "x-office-address-book";
+               title = _("Address Book Properties");
+               config = e_book_source_config_new (registry, source);
+       } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) {
+               icon_name = "x-office-calendar";
+               title = _("Calendar Properties");
+               config = e_cal_source_config_new (registry, source, E_CAL_CLIENT_SOURCE_TYPE_EVENTS);
+       } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) {
+               icon_name = "evolution-memos";
+               title = _("Memo List Properties");
+               config = e_cal_source_config_new (registry, source, E_CAL_CLIENT_SOURCE_TYPE_MEMOS);
+       } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
+               icon_name = "evolution-tasks";
+               title = _("Task List Properties");
+               config = e_cal_source_config_new (registry, source, E_CAL_CLIENT_SOURCE_TYPE_TASKS);
+       }
+
+       if (config) {
+               GtkWidget *dialog;
+
+               dialog = e_source_config_dialog_new (E_SOURCE_CONFIG (config));
+
+               gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (accounts_window));
+               gtk_window_set_icon_name (GTK_WINDOW (dialog), icon_name);
+               gtk_window_set_title (GTK_WINDOW (dialog), title);
+
+               gtk_widget_show (dialog);
+
+               return TRUE;
+       } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
+               EShellBackend *shell_backend;
+               EShell *shell = e_shell_get_default ();
+
+               if (shell) {
+                       shell_backend = e_shell_get_backend_by_name (shell, "mail");
+
+                       g_signal_emit_by_name (shell_backend, "edit-account", accounts_window, source);
+               }
+
+               return TRUE;
+       } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
+               if (e_source_has_extension (source, E_SOURCE_EXTENSION_GOA)) {
+                       accounts_window_editors_open_goa (editors, source);
+               } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_UOA)) {
+                       accounts_window_editors_open_uoa (editors, source);
+               } else {
+                       accounts_window_editors_edit_unmanaged_collection (accounts_window, source);
+               }
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+accounts_window_editors_enabled_toggled_cb (EAccountsWindow *accounts_window,
+                                           ESource *source,
+                                           gpointer user_data)
+{
+       EShell *shell;
+
+       g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+       g_return_if_fail (E_IS_SOURCE (source));
+
+       shell = e_shell_get_default ();
+       if (!shell)
+               return;
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
+               EShellBackend *shell_backend;
+               EMailSession *session = NULL;
+
+               shell_backend = e_shell_get_backend_by_name (shell, "mail");
+               g_object_get (G_OBJECT (shell_backend), "session", &session, NULL);
+
+               if (session) {
+                       CamelService *service;
+
+                       service = camel_session_ref_service (CAMEL_SESSION (session), e_source_get_uid 
(source));
+
+                       if (service) {
+                               EMailAccountStore *account_store;
+
+                               account_store = e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION 
(session));
+
+                               if (e_source_get_enabled (source)) {
+                                       e_mail_account_store_enable_service (account_store, GTK_WINDOW 
(accounts_window), service);
+                               } else {
+                                       e_mail_account_store_disable_service (account_store, GTK_WINDOW 
(accounts_window), service);
+                               }
+
+                               g_object_unref (service);
+                       }
+
+                       g_object_unref (session);
+               }
+       }
+
+       if (!e_source_get_enabled (source))
+               e_shell_allow_auth_prompt_for (shell, source);
+}
+
+static void
+accounts_window_editors_constructed (GObject *object)
+{
+       EAccountsWindow *accounts_window;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_accounts_window_editors_parent_class)->constructed (object);
+
+       accounts_window = E_ACCOUNTS_WINDOW (e_extension_get_extensible (E_EXTENSION (object)));
+
+       g_signal_connect (accounts_window, "get-editing-flags",
+               G_CALLBACK (accounts_window_editors_get_editing_flags_cb), object);
+
+       g_signal_connect (accounts_window, "add-source",
+               G_CALLBACK (accounts_window_editors_add_source_cb), object);
+
+       g_signal_connect (accounts_window, "edit-source",
+               G_CALLBACK (accounts_window_editors_edit_source_cb), object);
+
+       g_signal_connect (accounts_window, "enabled-toggled",
+               G_CALLBACK (accounts_window_editors_enabled_toggled_cb), object);
+}
+
+static void
+accounts_window_editors_finalize (GObject *object)
+{
+       EAccountsWindowEditors *editors = E_ACCOUNTS_WINDOW_EDITORS (object);
+
+       g_free (editors->gcc_program_path);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_accounts_window_editors_parent_class)->finalize (object);
+}
+
+static void
+e_accounts_window_editors_class_init (EAccountsWindowEditorsClass *class)
+{
+       GObjectClass *object_class;
+       EExtensionClass *extension_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->constructed = accounts_window_editors_constructed;
+       object_class->finalize = accounts_window_editors_finalize;
+
+       extension_class = E_EXTENSION_CLASS (class);
+       extension_class->extensible_type = E_TYPE_ACCOUNTS_WINDOW;
+}
+
+static void
+e_accounts_window_editors_class_finalize (EAccountsWindowEditorsClass *class)
+{
+}
+
+static void
+e_accounts_window_editors_init (EAccountsWindowEditors *extension)
+{
+       extension->gcc_program_path = g_find_program_in_path ("gnome-control-center");
+}
+
+void
+e_accounts_window_editors_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_accounts_window_editors_register_type (type_module);
+}
diff --git a/src/modules/accounts-window/e-accounts-window-editors.h 
b/src/modules/accounts-window/e-accounts-window-editors.h
new file mode 100644
index 0000000..1418f11
--- /dev/null
+++ b/src/modules/accounts-window/e-accounts-window-editors.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_ACCOUNTS_WINDOW_EDITORS_H
+#define E_ACCOUNTS_WINDOW_EDITORS_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void e_accounts_window_editors_type_register (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_ACCOUNTS_WINDOW_EDITORS_H */
diff --git a/src/modules/mail/e-mail-shell-backend.c b/src/modules/mail/e-mail-shell-backend.c
index 70a3383..5f299ee 100644
--- a/src/modules/mail/e-mail-shell-backend.c
+++ b/src/modules/mail/e-mail-shell-backend.c
@@ -68,6 +68,14 @@ struct _EMailShellBackendPrivate {
        gpointer editor;    /* weak pointer, when editing a mail account */
 };
 
+enum {
+       NEW_ACCOUNT,
+       EDIT_ACCOUNT,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
 G_DEFINE_DYNAMIC_TYPE (
        EMailShellBackend,
        e_mail_shell_backend,
@@ -910,6 +918,73 @@ mail_shell_backend_start (EShellBackend *shell_backend)
        }
 }
 
+static void
+mail_shell_backend_new_account_default (EMailShellBackend *mail_shell_backend,
+                                       GtkWindow *parent)
+{
+       GtkWidget *assistant;
+       EMailBackend *backend;
+       EMailSession *session;
+
+       g_return_if_fail (E_IS_MAIL_SHELL_BACKEND (mail_shell_backend));
+
+       assistant = mail_shell_backend->priv->assistant;
+
+       if (assistant != NULL) {
+               gtk_window_present (GTK_WINDOW (assistant));
+               return;
+       }
+
+       backend = E_MAIL_BACKEND (mail_shell_backend);
+       session = e_mail_backend_get_session (backend);
+
+       if (assistant == NULL)
+               assistant = e_mail_config_assistant_new (session);
+
+       gtk_window_set_transient_for (GTK_WINDOW (assistant), parent);
+       gtk_widget_show (assistant);
+
+       mail_shell_backend->priv->assistant = assistant;
+
+       g_object_add_weak_pointer (
+               G_OBJECT (mail_shell_backend->priv->assistant),
+               &mail_shell_backend->priv->assistant);
+}
+
+static void
+mail_shell_backend_edit_account_default (EMailShellBackend *mail_shell_backend,
+                                        GtkWindow *parent,
+                                        ESource *mail_account)
+{
+       EMailShellBackendPrivate *priv;
+       EMailBackend *backend;
+       EMailSession *session;
+
+       g_return_if_fail (E_IS_MAIL_SHELL_BACKEND (mail_shell_backend));
+       g_return_if_fail (E_IS_SOURCE (mail_account));
+
+       priv = mail_shell_backend->priv;
+
+       backend = E_MAIL_BACKEND (mail_shell_backend);
+       session = e_mail_backend_get_session (backend);
+
+       if (priv->editor != NULL) {
+               gtk_window_present (GTK_WINDOW (priv->editor));
+               return;
+       }
+
+       priv->editor = e_mail_config_window_new (session, mail_account);
+       gtk_window_set_transient_for (GTK_WINDOW (priv->editor), parent);
+       g_object_add_weak_pointer (G_OBJECT (priv->editor), &priv->editor);
+
+       g_signal_connect (
+               priv->editor, "changes-committed",
+               G_CALLBACK (mail_shell_backend_changes_committed_cb),
+               mail_shell_backend);
+
+       gtk_widget_show (priv->editor);
+}
+
 static gboolean
 mail_shell_backend_delete_junk_policy_decision (EMailBackend *backend)
 {
@@ -1028,6 +1103,47 @@ e_mail_shell_backend_class_init (EMailShellBackendClass *class)
                mail_shell_backend_delete_junk_policy_decision;
        mail_backend_class->empty_trash_policy_decision =
                mail_shell_backend_empty_trash_policy_decision;
+
+       class->new_account = mail_shell_backend_new_account_default;
+       class->edit_account = mail_shell_backend_edit_account_default;
+
+       /**
+        * EMailShellBackend::new-account:
+        * @parent: a #GtkWindow parent for the editor
+        *
+        * Opens wizard to create a new mail account.
+        *
+        * Since: 3.26
+        **/
+       signals[NEW_ACCOUNT] = g_signal_new (
+               "new-account",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMailShellBackendClass, new_account),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 1,
+               GTK_TYPE_WINDOW);
+
+       /**
+        * EMailShellBackend::edit-account:
+        * @parent: a #GtkWindow parent for the editor
+        * @mail_account: an #ESource for the mail account
+        *
+        * Edits account represented by the @source.
+        *
+        * Since: 3.26
+        **/
+       signals[EDIT_ACCOUNT] = g_signal_new (
+               "edit-account",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMailShellBackendClass, edit_account),
+               NULL, NULL,
+               NULL,
+               G_TYPE_NONE, 2,
+               GTK_TYPE_WINDOW,
+               E_TYPE_SOURCE);
 }
 
 static void
@@ -1053,70 +1169,23 @@ e_mail_shell_backend_type_register (GTypeModule *type_module)
 
 void
 e_mail_shell_backend_new_account (EMailShellBackend *mail_shell_backend,
-                                  GtkWindow *parent)
+                                 GtkWindow *parent)
 {
-       GtkWidget *assistant;
-       EMailBackend *backend;
-       EMailSession *session;
-
        g_return_if_fail (mail_shell_backend != NULL);
        g_return_if_fail (E_IS_MAIL_SHELL_BACKEND (mail_shell_backend));
 
-       assistant = mail_shell_backend->priv->assistant;
-
-       if (assistant != NULL) {
-               gtk_window_present (GTK_WINDOW (assistant));
-               return;
-       }
-
-       backend = E_MAIL_BACKEND (mail_shell_backend);
-       session = e_mail_backend_get_session (backend);
-
-       if (assistant == NULL)
-               assistant = e_mail_config_assistant_new (session);
-
-       gtk_window_set_transient_for (GTK_WINDOW (assistant), parent);
-       gtk_widget_show (assistant);
-
-       mail_shell_backend->priv->assistant = assistant;
-
-       g_object_add_weak_pointer (
-               G_OBJECT (mail_shell_backend->priv->assistant),
-               &mail_shell_backend->priv->assistant);
+       g_signal_emit (mail_shell_backend, signals[NEW_ACCOUNT], 0, parent);
 }
 
 void
 e_mail_shell_backend_edit_account (EMailShellBackend *mail_shell_backend,
-                                   GtkWindow *parent,
-                                   ESource *mail_account)
+                                  GtkWindow *parent,
+                                  ESource *mail_account)
 {
-       EMailShellBackendPrivate *priv;
-       EMailBackend *backend;
-       EMailSession *session;
-
        g_return_if_fail (E_IS_MAIL_SHELL_BACKEND (mail_shell_backend));
        g_return_if_fail (E_IS_SOURCE (mail_account));
 
-       priv = mail_shell_backend->priv;
-
-       backend = E_MAIL_BACKEND (mail_shell_backend);
-       session = e_mail_backend_get_session (backend);
-
-       if (priv->editor != NULL) {
-               gtk_window_present (GTK_WINDOW (priv->editor));
-               return;
-       }
-
-       priv->editor = e_mail_config_window_new (session, mail_account);
-       gtk_window_set_transient_for (GTK_WINDOW (priv->editor), parent);
-       g_object_add_weak_pointer (G_OBJECT (priv->editor), &priv->editor);
-
-       g_signal_connect (
-               priv->editor, "changes-committed",
-               G_CALLBACK (mail_shell_backend_changes_committed_cb),
-               mail_shell_backend);
-
-       gtk_widget_show (priv->editor);
+       g_signal_emit (mail_shell_backend, signals[EDIT_ACCOUNT], 0, parent, mail_account);
 }
 
 /******************* Code below here belongs elsewhere. *******************/
diff --git a/src/modules/mail/e-mail-shell-backend.h b/src/modules/mail/e-mail-shell-backend.h
index 5ea53b0..8941919 100644
--- a/src/modules/mail/e-mail-shell-backend.h
+++ b/src/modules/mail/e-mail-shell-backend.h
@@ -56,6 +56,13 @@ struct _EMailShellBackend {
 
 struct _EMailShellBackendClass {
        EMailBackendClass parent_class;
+
+       /* Signals */
+       void            (* new_account)         (EMailShellBackend *mail_shell_backend,
+                                                GtkWindow *parent);
+       void            (* edit_account)        (EMailShellBackend *mail_shell_backend,
+                                                GtkWindow *parent,
+                                                ESource *mail_account);
 };
 
 GType          e_mail_shell_backend_get_type   (void);
diff --git a/src/shell/e-shell-window-actions.c b/src/shell/e-shell-window-actions.c
index b7305f7..5de741e 100644
--- a/src/shell/e-shell-window-actions.c
+++ b/src/shell/e-shell-window-actions.c
@@ -37,6 +37,24 @@ action_about_cb (GtkAction *action,
        e_shell_utils_run_help_about (e_shell_window_get_shell (shell_window));
 }
 
+static void
+action_accounts_cb (GtkAction *action,
+                   EShellWindow *shell_window)
+{
+       ESourceRegistry *registry;
+       EShell *shell;
+       GtkWidget *accounts_window;
+
+       g_return_if_fail (E_IS_SHELL_WINDOW (shell_window));
+
+       shell = e_shell_window_get_shell (shell_window);
+       registry = e_shell_get_registry (shell);
+
+       accounts_window = e_accounts_window_new (registry);
+
+       e_accounts_window_show_with_parent (E_ACCOUNTS_WINDOW (accounts_window), GTK_WINDOW (shell_window));
+}
+
 /**
  * E_SHELL_WINDOW_ACTION_CLOSE:
  * @window: an #EShellWindow
@@ -780,6 +798,13 @@ static GtkActionEntry shell_entries[] = {
          N_("Show information about Evolution"),
          G_CALLBACK (action_about_cb) },
 
+       { "accounts",
+         NULL,
+         N_("_Accounts"),
+         NULL,
+         N_("Configure Evolution Accounts"),
+         G_CALLBACK (action_accounts_cb) },
+
        { "close",
          "window-close",
          N_("_Close Window"),
diff --git a/src/shell/e-shell-window-actions.h b/src/shell/e-shell-window-actions.h
index b3e37fb..592e318 100644
--- a/src/shell/e-shell-window-actions.h
+++ b/src/shell/e-shell-window-actions.h
@@ -30,6 +30,8 @@
 /* Actions */
 #define E_SHELL_WINDOW_ACTION_ABOUT(window) \
        E_SHELL_WINDOW_ACTION ((window), "about")
+#define E_SHELL_WINDOW_ACTION_ACCOUNTS(window) \
+       E_SHELL_WINDOW_ACTION ((window), "accounts")
 #define E_SHELL_WINDOW_ACTION_CLOSE(window) \
        E_SHELL_WINDOW_ACTION ((window), "close")
 #define E_SHELL_WINDOW_ACTION_CONTENTS(window) \


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