[geary/wip/714104-refine-account-dialog: 144/180] Introduce some common interfaces for account editor panes.



commit c2c605a2b6269399d0b401f08b8df40206c0670a
Author: Michael James Gratton <mike vee net>
Date:   Thu Jun 14 22:27:45 2018 +1000

    Introduce some common interfaces for account editor panes.
    
    This allows the panes themselves to manage their own headers, command
    stacks, and so on. Break out the account list pane out so it's handled in
    the same way as the others.

 po/POTFILES.in                                     |   3 +-
 src/client/accounts/accounts-editor-edit-pane.vala | 136 ++++++---
 src/client/accounts/accounts-editor-list-pane.vala | 215 ++++++++++++++
 .../accounts/accounts-editor-remove-pane.vala      |  32 +-
 src/client/accounts/accounts-editor-row.vala       |  13 +-
 .../accounts/accounts-editor-servers-pane.vala     |  54 +++-
 src/client/accounts/accounts-editor.vala           | 322 ++++++---------------
 src/client/meson.build                             |   1 +
 ui/accounts_editor.ui                              | 179 ------------
 ui/accounts_editor_edit_pane.ui                    |  64 ++++
 ui/accounts_editor_list_pane.ui                    |  71 +++++
 ui/accounts_editor_remove_pane.ui                  |  33 +++
 ui/accounts_editor_servers_pane.ui                 |  52 ++++
 ui/org.gnome.Geary.gresource.xml                   |   2 +-
 14 files changed, 704 insertions(+), 473 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b1ee0817..fdf925a6 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -19,6 +19,7 @@ src/client/accounts/account-manager.vala
 src/client/accounts/account-spinner-page.vala
 src/client/accounts/accounts-editor.vala
 src/client/accounts/accounts-editor-edit-pane.vala
+src/client/accounts/accounts-editor-list-pane.vala
 src/client/accounts/accounts-editor-remove-pane.vala
 src/client/accounts/accounts-editor-row.vala
 src/client/accounts/accounts-editor-servers-pane.vala
@@ -412,8 +413,8 @@ src/mailer/main.vala
 ui/account_cannot_remove.glade
 ui/account_list.glade
 ui/account_spinner.glade
-ui/accounts_editor.ui
 ui/accounts_editor_edit_pane.ui
+ui/accounts_editor_list_pane.ui
 ui/accounts_editor_remove_pane.ui
 ui/accounts_editor_servers_pane.ui
 ui/certificate_warning_dialog.glade
diff --git a/src/client/accounts/accounts-editor-edit-pane.vala 
b/src/client/accounts/accounts-editor-edit-pane.vala
index 260e9aa5..7d7758df 100644
--- a/src/client/accounts/accounts-editor-edit-pane.vala
+++ b/src/client/accounts/accounts-editor-edit-pane.vala
@@ -6,17 +6,23 @@
  */
 
 /**
- * The main account editor window.
+ * An account editor pane for editing a specific account's preferences.
  */
 [GtkTemplate (ui = "/org/gnome/Geary/accounts_editor_edit_pane.ui")]
-public class Accounts.EditorEditPane : Gtk.Grid {
+internal class Accounts.EditorEditPane : Gtk.Grid, EditorPane, AccountPane {
 
 
-    /** The editor this pane belongs to. */
-    internal weak Editor editor; // circular ref
+    internal Geary.AccountInformation account { get ; protected set; }
 
-    /** The account being displayed by this pane. */
-    internal Geary.AccountInformation account;
+    /** Command stack for edit pane user commands. */
+    internal Application.CommandStack commands {
+        get; private set; default = new Application.CommandStack();
+    }
+
+    protected weak Accounts.Editor editor { get; set; }
+
+    [GtkChild]
+    private Gtk.HeaderBar header;
 
     [GtkChild]
     private Gtk.ListBox details_list;
@@ -33,9 +39,11 @@ public class Accounts.EditorEditPane : Gtk.Grid {
     [GtkChild]
     private Gtk.ListBox settings_list;
 
+    [GtkChild]
+    private Gtk.Button undo_button;
 
-    public EditorEditPane(Editor editor,
-                          Geary.AccountInformation account) {
+
+    public EditorEditPane(Editor editor, Geary.AccountInformation account) {
         this.editor = editor;
         this.account = account;
 
@@ -47,7 +55,7 @@ public class Accounts.EditorEditPane : Gtk.Grid {
                  in account.get_sender_mailboxes()) {
             this.senders_list.add(new MailboxRow(account, sender));
         }
-        this.senders_list.add(new AddMailboxRow(this));
+        this.senders_list.add(new AddMailboxRow());
 
         this.signature_preview = new ClientWebView(
             ((GearyApplication) editor.application).config
@@ -77,7 +85,7 @@ public class Accounts.EditorEditPane : Gtk.Grid {
                 // view no longer the focus widget
                 if (!this.signature_preview.is_focus &&
                     this.signature_changed) {
-                    editor.commands.execute.begin(
+                    this.commands.execute.begin(
                         new SignatureChangedCommand(
                             this.signature_preview, account
                         ),
@@ -95,7 +103,22 @@ public class Accounts.EditorEditPane : Gtk.Grid {
         this.signature_frame.add(this.signature_preview);
 
         this.settings_list.set_header_func(Editor.seperator_headers);
-        this.settings_list.add(new EmailPrefetchRow(editor, this.account));
+        this.settings_list.add(new EmailPrefetchRow(this));
+
+        this.account.information_changed.connect(on_account_changed);
+        update_header();
+
+        this.commands.executed.connect(on_command);
+        this.commands.undone.connect(on_command);
+        this.commands.redone.connect(on_command);
+    }
+
+    ~EditorEditPane() {
+        this.account.information_changed.disconnect(on_account_changed);
+
+        this.commands.executed.disconnect(on_command);
+        this.commands.undone.disconnect(on_command);
+        this.commands.redone.disconnect(on_command);
     }
 
     internal string? get_default_name() {
@@ -111,11 +134,50 @@ public class Accounts.EditorEditPane : Gtk.Grid {
         return name;
     }
 
+    internal Gtk.HeaderBar get_header() {
+        return this.header;
+    }
+
+    internal void pane_shown() {
+        update_actions();
+    }
+
+    internal void undo() {
+        this.commands.undo.begin(null);
+    }
+
+    internal void redo() {
+        this.commands.redo.begin(null);
+    }
+
+    private void update_actions() {
+        this.editor.get_action(GearyController.ACTION_UNDO).set_enabled(
+            this.commands.can_undo
+        );
+        this.editor.get_action(GearyController.ACTION_REDO).set_enabled(
+            this.commands.can_redo
+        );
+
+        Application.Command next_undo = this.commands.peek_undo();
+        this.undo_button.set_tooltip_text(
+            (next_undo != null && next_undo.undo_label != null)
+            ? next_undo.undo_label : ""
+        );
+    }
+
+    private void on_account_changed() {
+        update_header();
+    }
+
+    private void on_command() {
+        update_actions();
+    }
+
     [GtkCallback]
     private void on_setting_activated(Gtk.ListBoxRow row) {
-        EditorRow? setting = row as EditorRow;
+        EditorRow<EditorEditPane>? setting = row as EditorRow<EditorEditPane>;
         if (setting != null) {
-            setting.activated(this.editor);
+            setting.activated(this);
         }
     }
 
@@ -129,10 +191,15 @@ public class Accounts.EditorEditPane : Gtk.Grid {
         this.editor.push(new EditorRemovePane(this.editor, this.account));
     }
 
+    [GtkCallback]
+    private void on_back_button_clicked() {
+        this.editor.pop();
+    }
+
 }
 
 
-private class Accounts.NicknameRow : AccountRow<Gtk.Label> {
+private class Accounts.NicknameRow : AccountRow<EditorEditPane,Gtk.Label> {
 
 
     public NicknameRow(Geary.AccountInformation account) {
@@ -146,7 +213,7 @@ private class Accounts.NicknameRow : AccountRow<Gtk.Label> {
         update();
     }
 
-    public override void activated(Accounts.Editor editor) {
+    public override void activated(EditorEditPane pane) {
         EditorPopover popover = new EditorPopover();
 
         string? value = this.account.nickname;
@@ -155,11 +222,11 @@ private class Accounts.NicknameRow : AccountRow<Gtk.Label> {
         entry.set_placeholder_text(value ?? "");
         entry.set_width_chars(20);
         entry.activate.connect(() => {
-                editor.commands.execute.begin(
+                pane.commands.execute.begin(
                     new PropertyCommand<string>(
                         this.account,
                         this.account,
-                        "nickname",
+                        Geary.AccountInformation.PROP_NICKNAME,
                         entry.get_text(),
                         // Translators: Tooltip used to undo changing
                         // the name of an account. The string
@@ -192,30 +259,25 @@ private class Accounts.NicknameRow : AccountRow<Gtk.Label> {
 }
 
 
-private class Accounts.AddMailboxRow : AddRow {
+private class Accounts.AddMailboxRow : AddRow<EditorEditPane> {
 
 
-    private EditorEditPane edit_pane;
-
-
-    public AddMailboxRow(EditorEditPane edit_pane) {
-        this.edit_pane = edit_pane;
-
+    public AddMailboxRow() {
         // Translators: Tooltip for adding a new email sender/from
         // address's address to an account
         this.set_tooltip_text(_("Add a new sender email address"));
     }
 
-    public override void activated(Accounts.Editor editor) {
+    public override void activated(EditorEditPane pane) {
         MailboxEditorPopover popover = new MailboxEditorPopover(
-            this.edit_pane.get_default_name() ?? "", "", false
+            pane.get_default_name() ?? "", "", false
         );
         popover.activated.connect(() => {
-                editor.commands.execute.begin(
+                pane.commands.execute.begin(
                     new AppendMailboxCommand(
                         (Gtk.ListBox) get_parent(),
                         new MailboxRow(
-                            this.edit_pane.account,
+                            pane.account,
                             new Geary.RFC822.MailboxAddress(
                                 popover.display_name,
                                 popover.address
@@ -233,7 +295,7 @@ private class Accounts.AddMailboxRow : AddRow {
 }
 
 
-private class Accounts.MailboxRow : AccountRow<Gtk.Label> {
+private class Accounts.MailboxRow : AccountRow<EditorEditPane,Gtk.Label> {
 
 
     internal Geary.RFC822.MailboxAddress mailbox;
@@ -247,14 +309,14 @@ private class Accounts.MailboxRow : AccountRow<Gtk.Label> {
         update();
     }
 
-    public override void activated(Accounts.Editor editor) {
+    public override void activated(EditorEditPane pane) {
         MailboxEditorPopover popover = new MailboxEditorPopover(
             this.mailbox.name ?? "",
             this.mailbox.address,
             this.account.get_sender_mailboxes().size > 1
         );
         popover.activated.connect(() => {
-                editor.commands.execute.begin(
+                pane.commands.execute.begin(
                     new UpdateMailboxCommand(
                         this,
                         new Geary.RFC822.MailboxAddress(
@@ -267,7 +329,7 @@ private class Accounts.MailboxRow : AccountRow<Gtk.Label> {
                 popover.popdown();
             });
         popover.remove_clicked.connect(() => {
-                editor.commands.execute.begin(
+                pane.commands.execute.begin(
                     new RemoveMailboxCommand(this), null
                 );
                 popover.popdown();
@@ -603,7 +665,8 @@ internal class Accounts.SignatureChangedCommand : Application.Command {
 }
 
 
-private class Accounts.EmailPrefetchRow : AccountRow<Gtk.ComboBoxText> {
+private class Accounts.EmailPrefetchRow :
+    AccountRow<EditorEditPane,Gtk.ComboBoxText> {
 
 
     private static bool row_separator(Gtk.TreeModel model, Gtk.TreeIter iter) {
@@ -613,10 +676,9 @@ private class Accounts.EmailPrefetchRow : AccountRow<Gtk.ComboBoxText> {
     }
 
 
-    public EmailPrefetchRow(Accounts.Editor editor,
-                            Geary.AccountInformation account) {
+    public EmailPrefetchRow(EditorEditPane pane) {
         base(
-            account,
+            pane.account,
             // Translators: This label describes the account
             // preference for the length of time (weeks, months or
             // years) that past email should be downloaded.
@@ -642,7 +704,7 @@ private class Accounts.EmailPrefetchRow : AccountRow<Gtk.ComboBoxText> {
         update();
 
         this.value.changed.connect(() => {
-                editor.commands.execute.begin(
+                pane.commands.execute.begin(
                     new PropertyCommand<int>(
                         this.account,
                         this.account,
diff --git a/src/client/accounts/accounts-editor-list-pane.vala 
b/src/client/accounts/accounts-editor-list-pane.vala
new file mode 100644
index 00000000..bc47fb5e
--- /dev/null
+++ b/src/client/accounts/accounts-editor-list-pane.vala
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2018 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later).  See the COPYING file in this distribution.
+ */
+
+/**
+ * An account editor pane for listing all known accounts.
+ */
+[GtkTemplate (ui = "/org/gnome/Geary/accounts_editor_list_pane.ui")]
+internal class Accounts.EditorListPane : Gtk.Grid, EditorPane {
+
+
+    private static int ordinal_sort(Gtk.ListBoxRow a, Gtk.ListBoxRow b) {
+        AccountListRow? account_a = a as AccountListRow;
+        AccountListRow? account_b = b as AccountListRow;
+
+        if (account_a == null) {
+            return (account_b == null) ? 0 : 1;
+        } else if (account_b == null) {
+            return -1;
+        }
+
+        return Geary.AccountInformation.compare_ascending(
+            account_a.account, account_b.account
+        );
+    }
+
+
+    protected weak Accounts.Editor editor { get; set; }
+
+    private AccountManager accounts { get; private set; }
+
+    [GtkChild]
+    private Gtk.HeaderBar header;
+
+    [GtkChild]
+    private Gtk.ListBox accounts_list;
+
+    private Gee.Map<Geary.AccountInformation,EditorEditPane> edit_pane_cache =
+        new Gee.HashMap<Geary.AccountInformation,EditorEditPane>();
+
+
+    public EditorListPane(Editor editor) {
+        this.editor = editor;
+        this.accounts =
+            ((GearyApplication) editor.application).controller.account_manager;
+
+        this.accounts_list.set_header_func(Editor.seperator_headers);
+        this.accounts_list.set_sort_func(ordinal_sort);
+
+        foreach (Geary.AccountInformation account in this.accounts.iterable()) {
+            add_account(account, this.accounts.get_status(account));
+        }
+
+        this.accounts_list.add(new AddRow<EditorServersPane>());
+
+        this.accounts.account_added.connect(on_account_added);
+        this.accounts.account_status_changed.connect(on_account_status_changed);
+        this.accounts.account_removed.connect(on_account_removed);
+    }
+
+    public override void destroy() {
+        this.accounts.account_added.disconnect(on_account_added);
+        this.accounts.account_status_changed.disconnect(on_account_status_changed);
+        this.accounts.account_removed.disconnect(on_account_removed);
+
+        this.edit_pane_cache.clear();
+        base.destroy();
+    }
+
+    /** Adds a new account to the list. */
+    internal void add_account(Geary.AccountInformation account,
+                             AccountManager.Status status) {
+        this.accounts_list.add(new AccountListRow(account, status));
+    }
+
+    /** Removes an account from the list. */
+    internal void remove_account(Geary.AccountInformation account) {
+        AccountListRow? row = get_account_row(account);
+        if (row != null) {
+            this.accounts_list.remove(row);
+        }
+    }
+
+    internal Gtk.HeaderBar get_header() {
+        return this.header;
+    }
+    
+    private AccountListRow? get_account_row(Geary.AccountInformation account) {
+        AccountListRow? row = null;
+        this.accounts_list.foreach((child) => {
+                AccountListRow? account_row = child as AccountListRow;
+                if (account_row != null && account_row.account == account) {
+                    row = account_row;
+                }
+            });
+        return row;
+    }
+
+    private void on_account_added(Geary.AccountInformation account,
+                                  AccountManager.Status status) {
+        add_account(account, status);
+    }
+
+    private void on_account_status_changed(Geary.AccountInformation account,
+                                           AccountManager.Status status) {
+        AccountListRow? row = get_account_row(account);
+        if (row != null) {
+            row.update(status);
+        }
+    }
+
+    private void on_account_removed(Geary.AccountInformation account) {
+        remove_account(account);
+    }
+
+    [GtkCallback]
+    private void on_accounts_list_row_activated(Gtk.ListBoxRow activated) {
+        AccountListRow? row = activated as AccountListRow;
+        if (row != null) {
+            Geary.AccountInformation account = row.account;
+            EditorEditPane? edit_pane = this.edit_pane_cache.get(account);
+            if (edit_pane == null) {
+                edit_pane = new EditorEditPane(this.editor, account);
+                this.edit_pane_cache.set(account, edit_pane);
+            }
+            this.editor.push(edit_pane);
+        }
+    }
+
+}
+
+
+private class Accounts.AccountListRow : EditorRow<EditorListPane> {
+
+
+    internal Geary.AccountInformation account;
+
+    private Gtk.Image unavailable_icon = new Gtk.Image.from_icon_name(
+        "dialog-warning-symbolic", Gtk.IconSize.BUTTON
+    );
+    private Gtk.Label account_name = new Gtk.Label("");
+    private Gtk.Label account_details = new Gtk.Label("");
+
+
+    public AccountListRow(Geary.AccountInformation account,
+                          AccountManager.Status status) {
+        this.account = account;
+
+        this.account_name.show();
+        this.account_name.set_hexpand(true);
+        this.account_name.halign = Gtk.Align.START;
+
+        this.account_details.show();
+
+        this.layout.add(this.unavailable_icon);
+        this.layout.add(this.account_name);
+        this.layout.add(this.account_details);
+
+        update(status);
+    }
+
+    public void update(AccountManager.Status status) {
+        if (status != AccountManager.Status.UNAVAILABLE) {
+            this.unavailable_icon.hide();
+            this.set_tooltip_text("");
+        } else {
+            this.unavailable_icon.show();
+            this.set_tooltip_text(
+                _("This account has encountered a problem and is unavailable")
+            );
+        }
+
+        string name = this.account.nickname;
+        if (Geary.String.is_empty(name)) {
+            name = account.primary_mailbox.to_address_display("", "");
+        }
+        this.account_name.set_text(name);
+
+        string? details = this.account.service_label;
+        switch (account.service_provider) {
+        case Geary.ServiceProvider.GMAIL:
+            details = _("GMail");
+            break;
+
+        case Geary.ServiceProvider.OUTLOOK:
+            details = _("Outlook.com");
+            break;
+
+        case Geary.ServiceProvider.YAHOO:
+            details = _("Yahoo");
+            break;
+        }
+        this.account_details.set_text(details);
+
+        if (status == AccountManager.Status.ENABLED) {
+            this.account_name.get_style_context().remove_class(
+                Gtk.STYLE_CLASS_DIM_LABEL
+            );
+            this.account_details.get_style_context().remove_class(
+                Gtk.STYLE_CLASS_DIM_LABEL
+            );
+        } else {
+            this.account_name.get_style_context().add_class(
+                Gtk.STYLE_CLASS_DIM_LABEL
+            );
+            this.account_details.get_style_context().add_class(
+                Gtk.STYLE_CLASS_DIM_LABEL
+            );
+        }
+    }
+
+}
diff --git a/src/client/accounts/accounts-editor-remove-pane.vala 
b/src/client/accounts/accounts-editor-remove-pane.vala
index 13b63649..57542e57 100644
--- a/src/client/accounts/accounts-editor-remove-pane.vala
+++ b/src/client/accounts/accounts-editor-remove-pane.vala
@@ -6,14 +6,18 @@
  */
 
 /**
- * The main account editor window.
+ * An account editor pane for removing an account from the client.
  */
 [GtkTemplate (ui = "/org/gnome/Geary/accounts_editor_remove_pane.ui")]
-public class Accounts.EditorRemovePane : Gtk.Grid {
+internal class Accounts.EditorRemovePane : Gtk.Grid, EditorPane, AccountPane {
 
 
-    private weak Editor editor; // circular ref
-    private Geary.AccountInformation account;
+    internal Geary.AccountInformation account { get ; protected set; }
+
+    protected weak Accounts.Editor editor { get; set; }
+
+    [GtkChild]
+    private Gtk.HeaderBar header;
 
     [GtkChild]
     private Gtk.Stack confirm_stack;
@@ -41,6 +45,21 @@ public class Accounts.EditorRemovePane : Gtk.Grid {
         this.remove_label.set_text(
             this.remove_label.get_text().printf(account.nickname)
         );
+
+        this.account.information_changed.connect(on_account_changed);
+        update_header();
+    }
+
+    ~EditorRemovePane() {
+        this.account.information_changed.disconnect(on_account_changed);
+    }
+
+    internal Gtk.HeaderBar get_header() {
+        return this.header;
+    }
+
+    private void on_account_changed() {
+        update_header();
     }
 
     [GtkCallback]
@@ -50,4 +69,9 @@ public class Accounts.EditorRemovePane : Gtk.Grid {
         this.confirm_stack.set_visible_child_name("remove");
     }
 
+    [GtkCallback]
+    private void on_back_button_clicked() {
+        this.editor.pop();
+    }
+
 }
diff --git a/src/client/accounts/accounts-editor-row.vala b/src/client/accounts/accounts-editor-row.vala
index 38718b29..38ac0719 100644
--- a/src/client/accounts/accounts-editor-row.vala
+++ b/src/client/accounts/accounts-editor-row.vala
@@ -6,7 +6,7 @@
  */
 
 
-internal class Accounts.EditorRow : Gtk.ListBoxRow {
+internal class Accounts.EditorRow<PaneType> : Gtk.ListBoxRow {
 
 
     protected Gtk.Grid layout { get; private set; default = new Gtk.Grid(); }
@@ -22,14 +22,14 @@ internal class Accounts.EditorRow : Gtk.ListBoxRow {
         this.show();
     }
 
-    public virtual void activated(Accounts.Editor editor) {
+    public virtual void activated(PaneType pane) {
         // No-op by default
     }
 
 }
 
 
-internal class Accounts.LabelledEditorRow<V> : EditorRow {
+internal class Accounts.LabelledEditorRow<PaneType,V> : EditorRow<PaneType> {
 
 
     protected Gtk.Label label { get; private set; default = new Gtk.Label(""); }
@@ -64,7 +64,7 @@ internal class Accounts.LabelledEditorRow<V> : EditorRow {
 }
 
 
-internal class Accounts.AddRow : EditorRow {
+internal class Accounts.AddRow<PaneType> : EditorRow<PaneType> {
 
 
     public AddRow() {
@@ -81,7 +81,8 @@ internal class Accounts.AddRow : EditorRow {
 }
 
 
-internal abstract class Accounts.AccountRow<V> : LabelledEditorRow<V> {
+internal abstract class Accounts.AccountRow<PaneType,V> :
+    LabelledEditorRow<PaneType,V> {
 
 
     internal Geary.AccountInformation account { get; private set; }
@@ -108,7 +109,7 @@ internal abstract class Accounts.AccountRow<V> : LabelledEditorRow<V> {
 }
 
 
-private abstract class Accounts.ServiceRow<V> : AccountRow<V> {
+private abstract class Accounts.ServiceRow<PaneType,V> : AccountRow<PaneType,V> {
 
 
     internal Geary.ServiceInformation service { get; private set; }
diff --git a/src/client/accounts/accounts-editor-servers-pane.vala 
b/src/client/accounts/accounts-editor-servers-pane.vala
index 7a25d695..8da25e90 100644
--- a/src/client/accounts/accounts-editor-servers-pane.vala
+++ b/src/client/accounts/accounts-editor-servers-pane.vala
@@ -7,14 +7,18 @@
  */
 
 /**
- * The main account editor window.
+ * An account editor pane for editing server details for an account.
  */
 [GtkTemplate (ui = "/org/gnome/Geary/accounts_editor_servers_pane.ui")]
-public class Accounts.EditorServersPane : Gtk.Grid {
+internal class Accounts.EditorServersPane : Gtk.Grid, EditorPane, AccountPane {
 
 
-    private weak Editor editor; // circular ref
-    private Geary.AccountInformation account;
+    internal Geary.AccountInformation account { get ; protected set; }
+
+    protected weak Accounts.Editor editor { get; set; }
+
+    [GtkChild]
+    private Gtk.HeaderBar header;
 
     [GtkChild]
     private Gtk.ListBox details_list;
@@ -43,6 +47,17 @@ public class Accounts.EditorServersPane : Gtk.Grid {
 
         this.sending_list.set_header_func(Editor.seperator_headers);
         build_service(account.smtp, this.sending_list);
+
+        this.account.information_changed.connect(on_account_changed);
+        update_header();
+    }
+
+    ~EditorServersPane() {
+        this.account.information_changed.disconnect(on_account_changed);
+    }
+
+    internal Gtk.HeaderBar get_header() {
+        return this.header;
     }
 
     private void build_service(Geary.ServiceInformation service,
@@ -52,10 +67,24 @@ public class Accounts.EditorServersPane : Gtk.Grid {
         settings_list.add(new ServiceAuthRow(this.account, service));
     }
 
+    [GtkCallback]
+    private void on_cancel_button_clicked() {
+        this.editor.pop();
+    }
+
+    [GtkCallback]
+    private void on_apply_button_clicked() {
+    }
+
+    private void on_account_changed() {
+        update_header();
+    }
+
 }
 
 
-private class Accounts.ServiceProviderRow : AccountRow<Gtk.Label> {
+private class Accounts.ServiceProviderRow :
+    AccountRow<EditorServersPane,Gtk.Label> {
 
 
     public ServiceProviderRow(Geary.AccountInformation account) {
@@ -96,7 +125,8 @@ private class Accounts.ServiceProviderRow : AccountRow<Gtk.Label> {
 }
 
 
-private class Accounts.AccountProviderRow : AccountRow<Gtk.Label> {
+private class Accounts.AccountProviderRow :
+    AccountRow<EditorServersPane,Gtk.Label> {
 
 
     public AccountProviderRow(Geary.AccountInformation account) {
@@ -129,7 +159,8 @@ private class Accounts.AccountProviderRow : AccountRow<Gtk.Label> {
 }
 
 
-private class Accounts.SaveDraftsRow : AccountRow<Gtk.Switch> {
+private class Accounts.SaveDraftsRow :
+    AccountRow<EditorServersPane,Gtk.Switch> {
 
 
     public SaveDraftsRow(Geary.AccountInformation account) {
@@ -151,7 +182,8 @@ private class Accounts.SaveDraftsRow : AccountRow<Gtk.Switch> {
 }
 
 
-private class Accounts.ServiceHostRow : ServiceRow<Gtk.Label> {
+private class Accounts.ServiceHostRow :
+    ServiceRow<EditorServersPane,Gtk.Label> {
 
     public ServiceHostRow(Geary.AccountInformation account,
                           Geary.ServiceInformation service) {
@@ -191,7 +223,8 @@ private class Accounts.ServiceHostRow : ServiceRow<Gtk.Label> {
 }
 
 
-private class Accounts.ServiceSecurityRow : ServiceRow<Gtk.ComboBoxText> {
+private class Accounts.ServiceSecurityRow :
+    ServiceRow<EditorServersPane,Gtk.ComboBoxText> {
 
     private const string INSECURE_ICON = "channel-insecure-symbolic";
     private const string SECURE_ICON = "channel-secure-symbolic";
@@ -246,7 +279,8 @@ private class Accounts.ServiceSecurityRow : ServiceRow<Gtk.ComboBoxText> {
 }
 
 
-private class Accounts.ServiceAuthRow : ServiceRow<Gtk.Label> {
+private class Accounts.ServiceAuthRow :
+    ServiceRow<EditorServersPane,Gtk.Label> {
 
     public ServiceAuthRow(Geary.AccountInformation account,
                           Geary.ServiceInformation service) {
diff --git a/src/client/accounts/accounts-editor.vala b/src/client/accounts/accounts-editor.vala
index 48102c6b..b293f38e 100644
--- a/src/client/accounts/accounts-editor.vala
+++ b/src/client/accounts/accounts-editor.vala
@@ -8,7 +8,6 @@
 /**
  * The main account editor window.
  */
-[GtkTemplate (ui = "/org/gnome/Geary/accounts_editor.ui")]
 public class Accounts.Editor : Gtk.Dialog {
 
 
@@ -27,123 +26,73 @@ public class Accounts.Editor : Gtk.Dialog {
         }
     }
 
-    private static int ordinal_sort(Gtk.ListBoxRow a, Gtk.ListBoxRow b) {
-        AccountListRow? account_a = a as AccountListRow;
-        AccountListRow? account_b = b as AccountListRow;
-
-        if (account_a == null) {
-            return (account_b == null) ? 0 : 1;
-        } else if (account_b == null) {
-            return -1;
-        }
-
-        return Geary.AccountInformation.compare_ascending(
-            account_a.account, account_b.account
-        );
-    }
-
-
-    /** The command stack for this pane. */
-    internal Application.CommandStack commands {
-        get; private set; default = new Application.CommandStack();
-    }
 
     /** The current account being edited, if any. */
     private Geary.AccountInformation selected_account {
         get; private set; default = null;
     }
 
-    private AccountManager accounts;
-
     private SimpleActionGroup actions = new SimpleActionGroup();
 
-    [GtkChild]
-    private Gtk.HeaderBar default_header;
 
-    [GtkChild]
-    private Gtk.Stack editor_panes;
+    private Gtk.Stack editor_panes = new Gtk.Stack();
 
-    [GtkChild]
-    private Gtk.Button back_button;
-
-    [GtkChild]
-    private Gtk.Button undo_button;
-
-    [GtkChild]
-    private Gtk.Grid list_pane;
-
-    [GtkChild]
-    private Gtk.ListBox accounts_list;
-
-    private Gee.LinkedList<Gtk.Widget> editor_pane_stack =
-        new Gee.LinkedList<Gtk.Widget>();
+    private Gee.LinkedList<EditorPane> editor_pane_stack =
+        new Gee.LinkedList<EditorPane>();
 
 
     public Editor(GearyApplication application, Gtk.Window parent) {
         this.application = application;
-        this.accounts = application.controller.account_manager;
 
-        this.actions.add_action_entries(ACTION_ENTRIES, this);
-        insert_action_group("win", this.actions);
-
-        set_titlebar(this.default_header);
+        set_default_size(700, 450);
+        set_icon_name(GearyApplication.APP_ID);
+        set_modal(true);
+        set_title(_("Accounts"));
         set_transient_for(parent);
-        //set_modal(true);
-        set_modal(false);
-
-        // XXX Glade 3.22 won't let us set this
-        get_content_area().border_width = 2;
-
-        this.accounts_list.set_header_func(seperator_headers);
-        this.accounts_list.set_sort_func(ordinal_sort);
 
-        this.editor_pane_stack.add(list_pane);
+        get_content_area().border_width = 0;
+        get_content_area().add(this.editor_panes);
 
-        foreach (Geary.AccountInformation account in accounts.iterable()) {
-            add_account(account, accounts.get_status(account));
-        }
-
-        this.accounts_list.add(new AddRow());
-
-        this.accounts.account_added.connect(on_account_added);
-        this.accounts.account_status_changed.connect(on_account_status_changed);
-        this.accounts.account_removed.connect(on_account_removed);
+        this.editor_panes.set_transition_type(
+            Gtk.StackTransitionType.SLIDE_LEFT_RIGHT
+        );
+        this.editor_panes.notify["visible-child"].connect(on_pane_changed);
+        this.editor_panes.show();
 
-        this.commands.executed.connect(on_command);
-        this.commands.undone.connect(on_command);
-        this.commands.redone.connect(on_command);
+        this.actions.add_action_entries(ACTION_ENTRIES, this);
+        insert_action_group("win", this.actions);
 
         get_action(GearyController.ACTION_UNDO).set_enabled(false);
         get_action(GearyController.ACTION_REDO).set_enabled(false);
-    }
 
-    ~Editor() {
-        this.commands.executed.disconnect(on_command);
-        this.commands.undone.disconnect(on_command);
-        this.commands.redone.disconnect(on_command);
+        push(new EditorListPane(this));
+    }
 
-        this.accounts.account_added.disconnect(on_account_added);
-        this.accounts.account_status_changed.disconnect(on_account_status_changed);
-        this.accounts.account_removed.disconnect(on_account_removed);
+    public override void destroy() {
+        this.editor_panes.notify["visible-child"].disconnect(on_pane_changed);
+        base.destroy();
     }
 
-    internal void push(Gtk.Widget child) {
-        // Since keep old, already-popped panes around (see pop for
+    internal void push(EditorPane pane) {
+        // Since we keep old, already-popped panes around (see pop for
         // details), when a new pane is pushed on they need to be
         // truncated.
-        Gtk.Widget current = this.editor_panes.get_visible_child();
+        EditorPane current = get_current_pane();
         int target_length = this.editor_pane_stack.index_of(current) + 1;
         while (target_length < this.editor_pane_stack.size) {
-            Gtk.Widget old = this.editor_pane_stack.remove_at(target_length);
+            EditorPane old = this.editor_pane_stack.remove_at(target_length);
             this.editor_panes.remove(old);
         }
 
+        get_action(GearyController.ACTION_UNDO).set_enabled(false);
+        get_action(GearyController.ACTION_REDO).set_enabled(false);
+
         // Now push the new pane on
-        this.editor_pane_stack.add(child);
-        this.editor_panes.add(child);
-        this.editor_panes.set_visible_child(child);
-        this.back_button.show();
-        this.undo_button.show();
+        this.editor_pane_stack.add(pane);
+        this.editor_panes.add(pane);
+        this.editor_panes.set_visible_child(pane);
+
+        pane.pane_shown();
     }
 
     internal void pop() {
@@ -151,192 +100,95 @@ public class Accounts.Editor : Gtk.Dialog {
         // there won't be any transition between them - the old one
         // will simply disappear. So we need to keep old, popped panes
         // around until a new one is pushed on.
-        //
-        // XXX work out a way to reuse the old ones if we go back to
-        // them?
-        Gtk.Widget current = this.editor_panes.get_visible_child();
-        int next = this.editor_pane_stack.index_of(current) - 1;
-
-        this.editor_panes.set_visible_child(this.editor_pane_stack.get(next));
+        EditorPane current = get_current_pane();
+        int prev_index = this.editor_pane_stack.index_of(current) - 1;
+        EditorPane prev = this.editor_pane_stack.get(prev_index);
+        this.editor_panes.set_visible_child(prev);
 
-        // Don't carry commands over from one pane to another
-        this.commands.clear();
-        get_action(GearyController.ACTION_UNDO).set_enabled(false);
-        get_action(GearyController.ACTION_REDO).set_enabled(false);
-
-        if (next == 0) {
+        if (prev_index == 0) {
             this.selected_account = null;
-            this.back_button.hide();
-            this.undo_button.hide();
         }
     }
 
-    private void add_account(Geary.AccountInformation account,
-                             AccountManager.Status status) {
-        this.accounts_list.add(new AccountListRow(account, status));
-    }
-
-    private void show_account(Geary.AccountInformation account) {
-        this.selected_account = account;
-        push(new EditorEditPane(this, account));
-    }
-
-    private AccountListRow? get_account_row(Geary.AccountInformation account) {
-        AccountListRow? row = null;
-        this.accounts_list.foreach((child) => {
-                AccountListRow? account_row = child as AccountListRow;
-                if (account_row != null && account_row.account == account) {
-                    row = account_row;
-                }
-            });
-        return row;
-    }
-
-    private inline GLib.SimpleAction get_action(string name) {
+    internal GLib.SimpleAction get_action(string name) {
         return (GLib.SimpleAction) this.actions.lookup_action(name);
     }
 
-    private void on_account_added(Geary.AccountInformation account,
-                                  AccountManager.Status status) {
-        add_account(account, status);
-    }
-
-    private void on_account_status_changed(Geary.AccountInformation account,
-                                           AccountManager.Status status) {
-        AccountListRow? row = get_account_row(account);
-        if (row != null) {
-            row.update(status);
-        }
-    }
-
-    private void on_account_removed(Geary.AccountInformation account) {
-        AccountListRow? row = get_account_row(account);
-        if (row != null) {
-            this.accounts_list.remove(row);
-        }
-
-        if (this.selected_account == account) {
-            while (this.editor_panes.get_visible_child() != this.list_pane) {
-                pop();
-            }
-        }
+    private inline EditorPane? get_current_pane() {
+        return this.editor_panes.get_visible_child() as EditorPane;
     }
 
     private void on_undo() {
-        this.commands.undo.begin(null);
+        get_current_pane().undo();
     }
 
     private void on_redo() {
-        this.commands.redo.begin(null);
+        get_current_pane().redo();
     }
 
-    private void on_command() {
-        get_action(GearyController.ACTION_UNDO).set_enabled(
-            this.commands.can_undo
-        );
-        get_action(GearyController.ACTION_REDO).set_enabled(
-            this.commands.can_redo
-        );
-
-        Application.Command next_undo = this.commands.peek_undo();
-        this.undo_button.set_tooltip_text(
-            (next_undo != null && next_undo.undo_label != null)
-            ? next_undo.undo_label : ""
-        );
-    }
-
-    [GtkCallback]
-    private void on_accounts_list_row_activated(Gtk.ListBoxRow activated) {
-        AccountListRow? row = activated as AccountListRow;
-        if (row != null) {
-            show_account(row.account);
-        }
-    }
-
-    [GtkCallback]
-    private void on_back_button_clicked() {
-        pop();
+    private void on_pane_changed() {
+        EditorPane? visible = get_current_pane();
+        set_titlebar(visible != null ? visible.get_header() : null);
     }
 
 }
 
-private class Accounts.AccountListRow : EditorRow {
 
+// XXX I'd really like to make EditorPane an abstract class,
+// AccountPane an abstract class extending that, and the four concrete
+// panes extend those, but the GTK+ Builder XML template system
+// requires a template class to designate its immediate parent
+// class. I.e. if accounts-editor-list-pane.ui specifies GtkGrid as
+// the parent of EditorListPane, then it much exactly be that and not
+// an instance of EditorPane, even if that extends GtkGrid. As a
+// result, both EditorPane and AccountPane must both be interfaces so
+// that the concrete pane classes can derive from GtkGrid directly,
+// and everything becomes horrible. See GTK+ Issue #1151:
+// https://gitlab.gnome.org/GNOME/gtk/issues/1151
 
-    internal Geary.AccountInformation account;
+/**
+ * Base interface for panes that can be shown by the accounts editor.
+ */
+internal interface Accounts.EditorPane : Gtk.Grid {
 
-    private Gtk.Image unavailable_icon = new Gtk.Image.from_icon_name(
-        "dialog-warning-symbolic", Gtk.IconSize.BUTTON
-    );
-    private Gtk.Label account_name = new Gtk.Label("");
-    private Gtk.Label account_details = new Gtk.Label("");
 
+    /** The editor displaying this pane. */
+    protected abstract weak Accounts.Editor editor { get; set; }
 
-    public AccountListRow(Geary.AccountInformation account,
-                          AccountManager.Status status) {
-        this.account = account;
 
-        this.account_name.show();
-        this.account_name.set_hexpand(true);
-        this.account_name.halign = Gtk.Align.START;
+    /** The GTK header bar to display for this pane. */
+    internal abstract Gtk.HeaderBar get_header();
 
-        this.account_details.show();
+    /** Notifies the pane that it has been displayed in the dialog. */
+    internal virtual void pane_shown() {
+        // no-op by default
+    }
 
-        this.layout.add(this.unavailable_icon);
-        this.layout.add(this.account_name);
-        this.layout.add(this.account_details);
+    /** Un-does the last user action, if enabled and supported. */
+    internal virtual void undo() {
+        // no-op by default
+    }
 
-        update(status);
+    /** Re-does the last user action, if enabled and supported. */
+    internal virtual void redo() {
+        // no-op by default
     }
 
-    public void update(AccountManager.Status status) {
-        if (status != AccountManager.Status.UNAVAILABLE) {
-            this.unavailable_icon.hide();
-            this.set_tooltip_text("");
-        } else {
-            this.unavailable_icon.show();
-            this.set_tooltip_text(
-                _("This account has encountered a problem and is unavailable")
-            );
-        }
+}
 
-        string name = this.account.nickname;
-        if (Geary.String.is_empty(name)) {
-            name = account.primary_mailbox.to_address_display("", "");
-        }
-        this.account_name.set_text(name);
 
-        string? details = this.account.service_label;
-        switch (account.service_provider) {
-        case Geary.ServiceProvider.GMAIL:
-            details = _("GMail");
-            break;
+/**
+ * Base class for editor panes that display a specific account
+ */
+internal interface Accounts.AccountPane : Gtk.Grid, EditorPane {
+
 
-        case Geary.ServiceProvider.OUTLOOK:
-            details = _("Outlook.com");
-            break;
+    /** Account being displayed by this pane. */
+    internal abstract Geary.AccountInformation account { get; protected set; }
 
-        case Geary.ServiceProvider.YAHOO:
-            details = _("Yahoo");
-            break;
-        }
-        this.account_details.set_text(details);
-
-        if (status == AccountManager.Status.ENABLED) {
-            this.account_name.get_style_context().remove_class(
-                Gtk.STYLE_CLASS_DIM_LABEL
-            );
-            this.account_details.get_style_context().remove_class(
-                Gtk.STYLE_CLASS_DIM_LABEL
-            );
-        } else {
-            this.account_name.get_style_context().add_class(
-                Gtk.STYLE_CLASS_DIM_LABEL
-            );
-            this.account_details.get_style_context().add_class(
-                Gtk.STYLE_CLASS_DIM_LABEL
-            );
-        }
+
+    protected void update_header() {
+        get_header().subtitle = this.account.nickname;
     }
 
 }
diff --git a/src/client/meson.build b/src/client/meson.build
index 62caec66..90c95751 100644
--- a/src/client/meson.build
+++ b/src/client/meson.build
@@ -22,6 +22,7 @@ geary_client_vala_sources = files(
   'accounts/account-spinner-page.vala',
   'accounts/accounts-editor.vala',
   'accounts/accounts-editor-edit-pane.vala',
+  'accounts/accounts-editor-list-pane.vala',
   'accounts/accounts-editor-remove-pane.vala',
   'accounts/accounts-editor-row.vala',
   'accounts/accounts-editor-servers-pane.vala',
diff --git a/ui/accounts_editor_edit_pane.ui b/ui/accounts_editor_edit_pane.ui
index e38c0fa0..83a616eb 100644
--- a/ui/accounts_editor_edit_pane.ui
+++ b/ui/accounts_editor_edit_pane.ui
@@ -223,4 +223,68 @@
       </packing>
     </child>
   </template>
+  <object class="GtkHeaderBar" id="header">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="title">Edit Account</property>
+    <property name="subtitle">Account Name</property>
+    <property name="has_subtitle">False</property>
+    <property name="show_close_button">True</property>
+    <child>
+      <object class="GtkGrid">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkButton" id="back_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <signal name="clicked" handler="on_back_button_clicked" swapped="no"/>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="no_show_all">True</property>
+                <property name="icon_name">go-previous-symbolic</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkGrid">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkButton" id="undo_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">win.undo</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="no_show_all">True</property>
+                <property name="icon_name">edit-undo-symbolic</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="pack_type">end</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </object>
 </interface>
diff --git a/ui/accounts_editor_list_pane.ui b/ui/accounts_editor_list_pane.ui
new file mode 100644
index 00000000..b34def99
--- /dev/null
+++ b/ui/accounts_editor_list_pane.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.0 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <template class="AccountsEditorListPane" parent="GtkGrid">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="hexpand">True</property>
+        <property name="vexpand">True</property>
+        <property name="hscrollbar_policy">never</property>
+        <property name="min_content_height">400</property>
+        <child>
+          <object class="GtkViewport">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkGrid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkFrame">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkListBox" id="accounts_list">
+                        <property name="width_request">0</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="selection_mode">none</property>
+                        <signal name="row-activated" handler="on_accounts_list_row_activated" swapped="no"/>
+                      </object>
+                    </child>
+                    <child type="label_item">
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <style>
+                  <class name="geary-account-view"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">0</property>
+      </packing>
+    </child>
+  </template>
+  <object class="GtkHeaderBar" id="header">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="title">Accounts</property>
+    <property name="has_subtitle">False</property>
+    <property name="show_close_button">True</property>
+  </object>
+</interface>
diff --git a/ui/accounts_editor_remove_pane.ui b/ui/accounts_editor_remove_pane.ui
index 33ddb612..ba785dd2 100644
--- a/ui/accounts_editor_remove_pane.ui
+++ b/ui/accounts_editor_remove_pane.ui
@@ -166,4 +166,37 @@
       </packing>
     </child>
   </template>
+  <object class="GtkHeaderBar" id="header">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="title">Remove account</property>
+    <property name="subtitle">Account name</property>
+    <property name="show_close_button">True</property>
+    <child>
+      <object class="GtkGrid">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkButton" id="back_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <signal name="clicked" handler="on_back_button_clicked" swapped="no"/>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="no_show_all">True</property>
+                <property name="icon_name">go-previous-symbolic</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>
diff --git a/ui/accounts_editor_servers_pane.ui b/ui/accounts_editor_servers_pane.ui
index 9b56ad9f..8dfa598a 100644
--- a/ui/accounts_editor_servers_pane.ui
+++ b/ui/accounts_editor_servers_pane.ui
@@ -135,4 +135,56 @@
       </packing>
     </child>
   </template>
+  <object class="GtkHeaderBar" id="header">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="title">Server Settings</property>
+    <property name="subtitle">Account Name</property>
+    <property name="show_close_button">True</property>
+    <child>
+      <object class="GtkGrid">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkButton" id="cancel_button">
+            <property name="label" translatable="yes">Cancel</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <signal name="clicked" handler="on_cancel_button_clicked" swapped="no"/>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkGrid">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkButton" id="apply_button">
+            <property name="label" translatable="yes">Apply</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <signal name="clicked" handler="on_apply_button_clicked" swapped="no"/>
+            <style>
+              <class name="suggested-action"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="pack_type">end</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </object>
 </interface>
diff --git a/ui/org.gnome.Geary.gresource.xml b/ui/org.gnome.Geary.gresource.xml
index 011ec159..ba31d74f 100644
--- a/ui/org.gnome.Geary.gresource.xml
+++ b/ui/org.gnome.Geary.gresource.xml
@@ -4,8 +4,8 @@
     <file compressed="true" preprocess="xml-stripblanks">account_cannot_remove.glade</file>
     <file compressed="true" preprocess="xml-stripblanks">account_list.glade</file>
     <file compressed="true" preprocess="xml-stripblanks">account_spinner.glade</file>
-    <file compressed="true" preprocess="xml-stripblanks">accounts_editor.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">accounts_editor_edit_pane.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks">accounts_editor_list_pane.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">accounts_editor_remove_pane.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">accounts_editor_servers_pane.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">certificate_warning_dialog.glade</file>


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