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? setting = row as EditorRow; 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 { +private class Accounts.NicknameRow : AccountRow { public NicknameRow(Geary.AccountInformation account) { @@ -146,7 +213,7 @@ private class Accounts.NicknameRow : AccountRow { 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 { entry.set_placeholder_text(value ?? ""); entry.set_width_chars(20); entry.activate.connect(() => { - editor.commands.execute.begin( + pane.commands.execute.begin( new PropertyCommand( 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 { } -private class Accounts.AddMailboxRow : AddRow { +private class Accounts.AddMailboxRow : AddRow { - 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 { +private class Accounts.MailboxRow : AccountRow { internal Geary.RFC822.MailboxAddress mailbox; @@ -247,14 +309,14 @@ private class Accounts.MailboxRow : AccountRow { 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 { 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 { +private class Accounts.EmailPrefetchRow : + AccountRow { private static bool row_separator(Gtk.TreeModel model, Gtk.TreeIter iter) { @@ -613,10 +676,9 @@ private class Accounts.EmailPrefetchRow : AccountRow { } - 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 { update(); this.value.changed.connect(() => { - editor.commands.execute.begin( + pane.commands.execute.begin( new PropertyCommand( 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 + * + * 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 edit_pane_cache = + new Gee.HashMap(); + + + 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()); + + 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 { + + + 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 : 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 : EditorRow { +internal class Accounts.LabelledEditorRow : EditorRow { protected Gtk.Label label { get; private set; default = new Gtk.Label(""); } @@ -64,7 +64,7 @@ internal class Accounts.LabelledEditorRow : EditorRow { } -internal class Accounts.AddRow : EditorRow { +internal class Accounts.AddRow : EditorRow { public AddRow() { @@ -81,7 +81,8 @@ internal class Accounts.AddRow : EditorRow { } -internal abstract class Accounts.AccountRow : LabelledEditorRow { +internal abstract class Accounts.AccountRow : + LabelledEditorRow { internal Geary.AccountInformation account { get; private set; } @@ -108,7 +109,7 @@ internal abstract class Accounts.AccountRow : LabelledEditorRow { } -private abstract class Accounts.ServiceRow : AccountRow { +private abstract class Accounts.ServiceRow : AccountRow { 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 { +private class Accounts.ServiceProviderRow : + AccountRow { public ServiceProviderRow(Geary.AccountInformation account) { @@ -96,7 +125,8 @@ private class Accounts.ServiceProviderRow : AccountRow { } -private class Accounts.AccountProviderRow : AccountRow { +private class Accounts.AccountProviderRow : + AccountRow { public AccountProviderRow(Geary.AccountInformation account) { @@ -129,7 +159,8 @@ private class Accounts.AccountProviderRow : AccountRow { } -private class Accounts.SaveDraftsRow : AccountRow { +private class Accounts.SaveDraftsRow : + AccountRow { public SaveDraftsRow(Geary.AccountInformation account) { @@ -151,7 +182,8 @@ private class Accounts.SaveDraftsRow : AccountRow { } -private class Accounts.ServiceHostRow : ServiceRow { +private class Accounts.ServiceHostRow : + ServiceRow { public ServiceHostRow(Geary.AccountInformation account, Geary.ServiceInformation service) { @@ -191,7 +223,8 @@ private class Accounts.ServiceHostRow : ServiceRow { } -private class Accounts.ServiceSecurityRow : ServiceRow { +private class Accounts.ServiceSecurityRow : + ServiceRow { 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 { } -private class Accounts.ServiceAuthRow : ServiceRow { +private class Accounts.ServiceAuthRow : + ServiceRow { 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 editor_pane_stack = - new Gee.LinkedList(); + private Gee.LinkedList editor_pane_stack = + new Gee.LinkedList(); public Editor(GearyApplication application, Gtk.Window parent) { this.application = application; - this.accounts = application.controller.account_manager; + + set_default_size(700, 450); + set_icon_name(GearyApplication.APP_ID); + set_modal(true); + set_title(_("Accounts")); + set_transient_for(parent); + + get_content_area().border_width = 0; + get_content_area().add(this.editor_panes); + + 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.actions.add_action_entries(ACTION_ENTRIES, this); insert_action_group("win", this.actions); - set_titlebar(this.default_header); - 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); - - 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.commands.executed.connect(on_command); - this.commands.undone.connect(on_command); - this.commands.redone.connect(on_command); - get_action(GearyController.ACTION_UNDO).set_enabled(false); get_action(GearyController.ACTION_REDO).set_enabled(false); + + push(new EditorListPane(this)); } - ~Editor() { - this.commands.executed.disconnect(on_command); - this.commands.undone.disconnect(on_command); - this.commands.redone.disconnect(on_command); - - 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; + 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); - this.editor_panes.set_visible_child(this.editor_pane_stack.get(next)); - - // 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 + +/** + * Base interface for panes that can be shown by the accounts editor. + */ +internal interface Accounts.EditorPane : Gtk.Grid { - 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(""); + /** The editor displaying this pane. */ + protected abstract weak Accounts.Editor editor { get; set; } - public AccountListRow(Geary.AccountInformation account, - AccountManager.Status status) { - this.account = account; + /** The GTK header bar to display for this pane. */ + internal abstract Gtk.HeaderBar get_header(); - 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); + /** Notifies the pane that it has been displayed in the dialog. */ + internal virtual void pane_shown() { + // 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") - ); - } + /** Un-does the last user action, if enabled and supported. */ + internal virtual void undo() { + // no-op by default + } - 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 - ); - } + /** Re-does the last user action, if enabled and supported. */ + internal virtual void redo() { + // no-op by default + } + +} + + +/** + * Base class for editor panes that display a specific account + */ +internal interface Accounts.AccountPane : Gtk.Grid, EditorPane { + + + /** Account being displayed by this pane. */ + internal abstract Geary.AccountInformation account { get; protected set; } + + + 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.ui b/ui/accounts_editor.ui deleted file mode 100644 index f957839e..00000000 --- a/ui/accounts_editor.ui +++ /dev/null @@ -1,179 +0,0 @@ - - - - - - - True - False - Accounts - False - True - - - True - False - - - True - True - - - - True - False - True - go-previous-symbolic - - - - - 0 - 0 - - - - - - - True - False - - - True - True - win.undo - - - True - False - True - edit-undo-symbolic - - - - - 0 - 0 - - - - - end - 1 - - - - 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 @@ + + True + False + Edit Account + Account Name + False + True + + + True + False + + + True + True + True + + + + True + False + True + go-previous-symbolic + + + + + 0 + 0 + + + + + + + True + False + + + True + True + True + win.undo + + + True + False + True + edit-undo-symbolic + + + + + 0 + 0 + + + + + end + 1 + + + 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 @@ + + + + + + + True + False + Accounts + False + True + + 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 @@ + + True + False + Remove account + Account name + True + + + True + False + + + True + True + True + + + + True + False + True + go-previous-symbolic + + + + + 0 + 0 + + + + + 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 @@ + + True + False + Server Settings + Account Name + True + + + True + False + + + Cancel + True + True + True + + + + 0 + 0 + + + + + + + True + False + + + Apply + True + True + True + + + + + 0 + 0 + + + + + end + 1 + + + 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 @@ account_cannot_remove.glade account_list.glade account_spinner.glade - accounts_editor.ui accounts_editor_edit_pane.ui + accounts_editor_list_pane.ui accounts_editor_remove_pane.ui accounts_editor_servers_pane.ui certificate_warning_dialog.glade