From c9bfab4b059a48c69bf2798de9d18cfbcbb02d4a Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Tue, 8 Jan 2019 23:48:49 +1100 Subject: [PATCH 1/7] Typo fix --- src/client/accounts/accounts-editor-list-pane.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/accounts/accounts-editor-list-pane.vala b/src/client/accounts/accounts-editor-list-pane.vala index 7d181d15..41e737a4 100644 --- a/src/client/accounts/accounts-editor-list-pane.vala +++ b/src/client/accounts/accounts-editor-list-pane.vala @@ -379,7 +379,7 @@ private class Accounts.AccountListRow : AccountRow { case DISABLED: this.set_tooltip_text( // Translators: Tooltip for accounts that have been - // loaded by disabled by the user. + // loaded but disabled by the user. _("This account has been disabled") ); break; From 7ed289899a1b9a53d82ab11e095fc5d8836c1770 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Wed, 9 Jan 2019 10:13:11 +1100 Subject: [PATCH 2/7] Tidy up account editor's undo/redo action state management Move update_command_actions() from CommandPane to Editor, call that when updating state internally and make the implementation details of it private. Make more use of the generic command management code in the list pane. --- .../accounts/accounts-editor-edit-pane.vala | 2 +- .../accounts/accounts-editor-list-pane.vala | 16 +----- .../accounts-editor-servers-pane.vala | 2 +- src/client/accounts/accounts-editor.vala | 52 ++++++++----------- 4 files changed, 27 insertions(+), 45 deletions(-) diff --git a/src/client/accounts/accounts-editor-edit-pane.vala b/src/client/accounts/accounts-editor-edit-pane.vala index 66439e62..a86ff117 100644 --- a/src/client/accounts/accounts-editor-edit-pane.vala +++ b/src/client/accounts/accounts-editor-edit-pane.vala @@ -154,7 +154,7 @@ internal class Accounts.EditorEditPane : /** {@inheritDoc} */ protected void command_executed() { - update_command_actions(); + this.editor.update_command_actions(); Application.Command next_undo = this.commands.peek_undo(); this.undo_button.set_tooltip_text( diff --git a/src/client/accounts/accounts-editor-list-pane.vala b/src/client/accounts/accounts-editor-list-pane.vala index 41e737a4..db8092d8 100644 --- a/src/client/accounts/accounts-editor-list-pane.vala +++ b/src/client/accounts/accounts-editor-list-pane.vala @@ -110,7 +110,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { this.commands.executed.connect(on_execute); this.commands.undone.connect(on_undo); this.commands.redone.connect(on_execute); - + connect_command_signals(); update_welcome_panel(); } @@ -118,6 +118,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { this.commands.executed.disconnect(on_execute); this.commands.undone.disconnect(on_undo); this.commands.redone.disconnect(on_execute); + disconnect_command_signals(); this.accounts.account_added.disconnect(on_account_added); this.accounts.account_status_changed.disconnect(on_account_status_changed); @@ -169,15 +170,6 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { notification.show(); } - 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 - ); - } - private void update_welcome_panel() { if (this.show_welcome) { // No accounts are available, so show only the welcome @@ -251,8 +243,6 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { ian.set_button(_("Undo"), "win." + GearyController.ACTION_UNDO); add_notification(ian); } - - update_actions(); } private void on_undo(Application.Command command) { @@ -261,8 +251,6 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { ian.set_button(_("Redo"), "win." + GearyController.ACTION_REDO); add_notification(ian); } - - update_actions(); } [GtkCallback] diff --git a/src/client/accounts/accounts-editor-servers-pane.vala b/src/client/accounts/accounts-editor-servers-pane.vala index 2502196a..6c4e410a 100644 --- a/src/client/accounts/accounts-editor-servers-pane.vala +++ b/src/client/accounts/accounts-editor-servers-pane.vala @@ -179,7 +179,7 @@ internal class Accounts.EditorServersPane : /** {@inheritDoc} */ protected void command_executed() { - update_command_actions(); + this.editor.update_command_actions(); this.apply_button.set_sensitive(this.commands.can_undo); } diff --git a/src/client/accounts/accounts-editor.vala b/src/client/accounts/accounts-editor.vala index 4080164d..30eee914 100644 --- a/src/client/accounts/accounts-editor.vala +++ b/src/client/accounts/accounts-editor.vala @@ -61,11 +61,10 @@ public class Accounts.Editor : Gtk.Dialog { 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); - this.editor_list_pane = new EditorListPane(this); push(this.editor_list_pane); + + update_command_actions(); } public override bool key_press_event(Gdk.EventKey event) { @@ -111,9 +110,6 @@ public class Accounts.Editor : Gtk.Dialog { 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(pane); this.editor_panes.add(pane); @@ -131,19 +127,33 @@ public class Accounts.Editor : Gtk.Dialog { this.editor_panes.set_visible_child(prev); } - internal GLib.SimpleAction get_action(string name) { - return (GLib.SimpleAction) this.actions.lookup_action(name); - } - internal void remove_account(Geary.AccountInformation account) { this.editor_panes.set_visible_child(this.editor_list_pane); this.editor_list_pane.remove_account(account); } + /** Updates the state of the editor's undo and redo actions. */ + internal void update_command_actions() { + bool can_undo = false; + bool can_redo = false; + CommandPane? pane = get_current_pane() as CommandPane; + if (pane != null) { + can_undo = pane.commands.can_undo; + can_redo = pane.commands.can_redo; + } + + get_action(GearyController.ACTION_UNDO).set_enabled(can_undo); + get_action(GearyController.ACTION_REDO).set_enabled(can_redo); + } + private inline EditorPane? get_current_pane() { return this.editor_panes.get_visible_child() as EditorPane; } + private inline GLib.SimpleAction get_action(string name) { + return (GLib.SimpleAction) this.actions.lookup_action(name); + } + private void on_undo() { CommandPane? pane = get_current_pane() as CommandPane; if (pane != null) { @@ -171,11 +181,7 @@ public class Accounts.Editor : Gtk.Dialog { header = visible.get_header(); } set_titlebar(header); - - CommandPane? commands = visible as CommandPane; - if (commands != null) { - commands.update_command_actions(); - } + update_command_actions(); } } @@ -279,18 +285,6 @@ internal interface Accounts.CommandPane : EditorPane { this.commands.redo.begin(null); } - /** - * Updates the state of the editor's undo and redo actions. - */ - internal virtual void update_command_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 - ); - } - /** * Connects to command stack signals. * @@ -316,10 +310,10 @@ internal interface Accounts.CommandPane : EditorPane { /** * Called when a command is executed, undone or redone. * - * By default, calls {@link update_command_actions}. + * By default, calls {@link Accounts.Editor.update_command_actions}. */ protected virtual void command_executed() { - update_command_actions(); + this.editor.update_command_actions(); } private void on_command() { From fdb9243ae536a49ba6bd76839da2a669814fef18 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Wed, 9 Jan 2019 10:20:51 +1100 Subject: [PATCH 3/7] Move common account editor pane in-app notification impl to the editor This moves the individual in-app notification implementation on each of the editor panes to the editor itself, reducing redundancy and allowing the editor to issue notifications if needed (spoiler: it will). --- po/POTFILES.in | 1 + .../accounts/accounts-editor-add-pane.vala | 10 +- .../accounts/accounts-editor-list-pane.vala | 12 +- .../accounts-editor-servers-pane.vala | 10 +- src/client/accounts/accounts-editor.vala | 40 ++-- ui/accounts_editor.ui | 64 +++++ ui/accounts_editor_add_pane.ui | 193 +++++++-------- ui/accounts_editor_list_pane.ui | 225 +++++++++--------- ui/accounts_editor_servers_pane.ui | 219 ++++++++--------- ui/org.gnome.Geary.gresource.xml | 1 + 10 files changed, 393 insertions(+), 382 deletions(-) create mode 100644 ui/accounts_editor.ui diff --git a/po/POTFILES.in b/po/POTFILES.in index 50387e02..f93c1690 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -401,6 +401,7 @@ src/engine/util/util-time.vala src/engine/util/util-timeout-manager.vala src/engine/util/util-trillian.vala src/mailer/main.vala +ui/accounts_editor.ui ui/accounts_editor_add_pane.ui ui/accounts_editor_edit_pane.ui ui/accounts_editor_list_pane.ui diff --git a/src/client/accounts/accounts-editor-add-pane.vala b/src/client/accounts/accounts-editor-add-pane.vala index cee7d989..3d7a5610 100644 --- a/src/client/accounts/accounts-editor-add-pane.vala +++ b/src/client/accounts/accounts-editor-add-pane.vala @@ -27,9 +27,6 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { [GtkChild] private Gtk.HeaderBar header; - [GtkChild] - private Gtk.Overlay osd_overlay; - [GtkChild] private Gtk.Grid pane_content; @@ -148,11 +145,6 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { return this.header; } - private void add_notification(InAppNotification notification) { - this.osd_overlay.add_overlay(notification); - notification.show(); - } - private async void validate_account(GLib.Cancellable? cancellable) { this.create_spinner.show(); this.create_spinner.start(); @@ -271,7 +263,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { if (to_focus != null) { to_focus.grab_focus(); } - add_notification( + this.editor.add_notification( new InAppNotification( // Translators: In-app notification label, the // string substitution is a more detailed reason. diff --git a/src/client/accounts/accounts-editor-list-pane.vala b/src/client/accounts/accounts-editor-list-pane.vala index db8092d8..5aa0975b 100644 --- a/src/client/accounts/accounts-editor-list-pane.vala +++ b/src/client/accounts/accounts-editor-list-pane.vala @@ -54,9 +54,6 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { [GtkChild] private Gtk.HeaderBar header; - [GtkChild] - private Gtk.Overlay osd_overlay; - [GtkChild] private Gtk.Grid pane_content; @@ -165,11 +162,6 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { this.accounts_list.add(row); } - private void add_notification(InAppNotification notification) { - this.osd_overlay.add_overlay(notification); - notification.show(); - } - private void update_welcome_panel() { if (this.show_welcome) { // No accounts are available, so show only the welcome @@ -241,7 +233,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { if (command.executed_label != null) { InAppNotification ian = new InAppNotification(command.executed_label); ian.set_button(_("Undo"), "win." + GearyController.ACTION_UNDO); - add_notification(ian); + this.editor.add_notification(ian); } } @@ -249,7 +241,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { if (command.undone_label != null) { InAppNotification ian = new InAppNotification(command.undone_label); ian.set_button(_("Redo"), "win." + GearyController.ACTION_REDO); - add_notification(ian); + this.editor.add_notification(ian); } } diff --git a/src/client/accounts/accounts-editor-servers-pane.vala b/src/client/accounts/accounts-editor-servers-pane.vala index 6c4e410a..1718ea03 100644 --- a/src/client/accounts/accounts-editor-servers-pane.vala +++ b/src/client/accounts/accounts-editor-servers-pane.vala @@ -44,9 +44,6 @@ internal class Accounts.EditorServersPane : [GtkChild] private Gtk.HeaderBar header; - [GtkChild] - private Gtk.Overlay osd_overlay; - [GtkChild] private Gtk.Grid pane_content; @@ -283,7 +280,7 @@ internal class Accounts.EditorServersPane : debug("Validation complete, is valid: %s", is_valid.to_string()); if (!is_valid) { - add_notification( + this.editor.add_notification( new InAppNotification( // Translators: In-app notification label, the // string substitution is a more detailed reason. @@ -329,11 +326,6 @@ internal class Accounts.EditorServersPane : return has_changed; } - private void add_notification(InAppNotification notification) { - this.osd_overlay.add_overlay(notification); - notification.show(); - } - private void add_row(Gtk.ListBox list, EditorRow row) { list.add(row); ValidatingRow? validating = row as ValidatingRow; diff --git a/src/client/accounts/accounts-editor.vala b/src/client/accounts/accounts-editor.vala index 30eee914..a27c4db9 100644 --- a/src/client/accounts/accounts-editor.vala +++ b/src/client/accounts/accounts-editor.vala @@ -8,6 +8,7 @@ /** * The main account editor window. */ +[GtkTemplate (ui = "/org/gnome/Geary/accounts_editor.ui")] public class Accounts.Editor : Gtk.Dialog { @@ -32,7 +33,12 @@ public class Accounts.Editor : Gtk.Dialog { private SimpleActionGroup actions = new SimpleActionGroup(); - private Gtk.Stack editor_panes = new Gtk.Stack(); + [GtkChild] + private Gtk.Overlay notifications_pane; + + [GtkChild] + private Gtk.Stack editor_panes; + private EditorListPane editor_list_pane; private Gee.LinkedList editor_pane_stack = @@ -41,23 +47,13 @@ public class Accounts.Editor : Gtk.Dialog { public Editor(GearyApplication application, Gtk.Window parent) { this.application = application; + this.transient_for = parent; + + // Can't set this in Glade 3.22.1 :( + this.get_content_area().border_width = 0; + 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_after(on_pane_changed); - this.editor_panes.show(); - this.actions.add_action_entries(ACTION_ENTRIES, this); insert_action_group("win", this.actions); @@ -94,11 +90,6 @@ public class Accounts.Editor : Gtk.Dialog { return ret; } - public override void destroy() { - this.editor_panes.notify["visible-child"].disconnect(on_pane_changed); - base.destroy(); - } - 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 @@ -127,6 +118,12 @@ public class Accounts.Editor : Gtk.Dialog { this.editor_panes.set_visible_child(prev); } + /** Displays an in-app notification in the dialog. */ + internal void add_notification(InAppNotification notification) { + this.notifications_pane.add_overlay(notification); + notification.show(); + } + internal void remove_account(Geary.AccountInformation account) { this.editor_panes.set_visible_child(this.editor_list_pane); this.editor_list_pane.remove_account(account); @@ -168,6 +165,7 @@ public class Accounts.Editor : Gtk.Dialog { } } + [GtkCallback] private void on_pane_changed() { EditorPane? visible = get_current_pane(); Gtk.Widget? header = null; diff --git a/ui/accounts_editor.ui b/ui/accounts_editor.ui new file mode 100644 index 00000000..22d41852 --- /dev/null +++ b/ui/accounts_editor.ui @@ -0,0 +1,64 @@ + + + + + + diff --git a/ui/accounts_editor_add_pane.ui b/ui/accounts_editor_add_pane.ui index 4e4921f3..8d942c33 100644 --- a/ui/accounts_editor_add_pane.ui +++ b/ui/accounts_editor_add_pane.ui @@ -81,27 +81,65 @@ True False - + True - False + True + True + True + pane_adjustment + never + in - + True - True - True - True - pane_adjustment - never - in + False + none - + True False - none - + True False + 0 + in + + + True + False + none + + + + + + 0 + 0 + + + + + True + False + + + True + False + start + Receiving + + + + + + + 0 + 0 + + True @@ -109,7 +147,7 @@ 0 in - + True False none @@ -117,52 +155,52 @@ + + 0 + 1 + + + + + 0 + 1 + + + + + True + False + + + True + False + start + Sending + + + + + 0 0 - + True False + 0 + in - + True False - start - Receiving - - - - + none + - - 0 - 0 - - - - - True - False - 0 - in - - - True - False - none - - - - - - 0 - 1 - @@ -170,65 +208,18 @@ 1 - - - True - False - - - True - False - start - Sending - - - - - - - 0 - 0 - - - - - True - False - 0 - in - - - True - False - none - - - - - - 0 - 1 - - - - - 0 - 2 - - - + + 0 + 2 + + - - -1 - diff --git a/ui/accounts_editor_list_pane.ui b/ui/accounts_editor_list_pane.ui index 36a4d3d3..85e7944a 100644 --- a/ui/accounts_editor_list_pane.ui +++ b/ui/accounts_editor_list_pane.ui @@ -18,171 +18,160 @@ True False - + True - False + True True True + pane_adjustment + never + 400 - + True - True - True - True - pane_adjustment - never - 400 + False - + True False - + True False + center + 12 - + True False - center - 12 - - - True - False - 64 - org.gnome.Geary - True - - - 0 - 0 - 2 - - - - - True - False - start - start - To get started, select an email provider below. - - - 1 - 1 - - - - - True - False - start - end - Welcome to Geary - - - - - - 1 - 0 - - - + 64 + org.gnome.Geary + True 0 0 + 2 - + True False + start start - True - True - 0 - in - - - True - False - none - - - - - - - + To get started, select an email provider below. - 0 + 1 1 - + True False start - Add an account + end + Welcome to Geary - - 0 - 2 - - - - - True - False - start - True - True - 0 - in - - - 0 - True - False - start - none - - - - - - - - - - 0 - 3 + 1 + 0 + + 0 + 0 + + + + True + False + start + True + True + 0 + in + + + True + False + none + + + + + + + + + + 0 + 1 + + + + + True + False + start + Add an account + + + + + + + 0 + 2 + + + + + True + False + start + True + True + 0 + in + + + 0 + True + False + start + none + + + + + + + + + + 0 + 3 + + + - - -1 - diff --git a/ui/accounts_editor_servers_pane.ui b/ui/accounts_editor_servers_pane.ui index d49704be..1a1234a3 100644 --- a/ui/accounts_editor_servers_pane.ui +++ b/ui/accounts_editor_servers_pane.ui @@ -76,142 +76,133 @@ True False - + True - False + True + True + True + pane_adjustment + never + 400 - + True - True - True - True - pane_adjustment - never - 400 + False - + True False - + True False + True + 0 + in - + True False - True - 0 - in - - - True - False - none - - - - - - - + none + + - - 0 - 2 - - - - True - False - start - Receiving - - - - 0 - 1 - - - - - True - False - True - 0 - in - - - True - False - none - - - - - - - - - - 0 - 4 - - - - - True - False - start - Sending - - - - 0 - 3 - - - - - True - False - True - 0 - in - - - True - False - none - - - - - - - - - - 0 - 0 - + + + + + 0 + 2 + + + + + True + False + start + Receiving + + 0 + 1 + + + + True + False + True + 0 + in + + + True + False + none + + + + + + + + + + 0 + 4 + + + + + True + False + start + Sending + + + + 0 + 3 + + + + + True + False + True + 0 + in + + + True + False + none + + + + + + + + + + 0 + 0 + + + - - -1 - diff --git a/ui/org.gnome.Geary.gresource.xml b/ui/org.gnome.Geary.gresource.xml index 38fce5d8..9aa268bb 100644 --- a/ui/org.gnome.Geary.gresource.xml +++ b/ui/org.gnome.Geary.gresource.xml @@ -1,6 +1,7 @@ + accounts_editor.ui accounts_editor_add_pane.ui accounts_editor_edit_pane.ui accounts_editor_list_pane.ui From aa27c964decac20e1414b12263bbcf40cf09f272 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Wed, 9 Jan 2019 10:28:06 +1100 Subject: [PATCH 4/7] Improve account editor's doc comments --- src/client/accounts/accounts-editor.vala | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/client/accounts/accounts-editor.vala b/src/client/accounts/accounts-editor.vala index a27c4db9..ca8753fc 100644 --- a/src/client/accounts/accounts-editor.vala +++ b/src/client/accounts/accounts-editor.vala @@ -1,5 +1,5 @@ /* - * Copyright 2018 Michael Gratton + * Copyright 2018-2019 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. @@ -7,6 +7,12 @@ /** * The main account editor window. + * + * The editor is a dialog window that manages a stack of {@link + * EditorPane} instances. Each pane handles a specific task (listing + * accounts, adding a new account, editing an existing one, etc.). The + * editor displaying panes as needed, and provides some common command + * management, account management and other common code for the panes. */ [GtkTemplate (ui = "/org/gnome/Geary/accounts_editor.ui")] public class Accounts.Editor : Gtk.Dialog { @@ -90,6 +96,9 @@ public class Accounts.Editor : Gtk.Dialog { return ret; } + /** + * Adds and shows a new pane in the editor. + */ 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 @@ -107,6 +116,9 @@ public class Accounts.Editor : Gtk.Dialog { this.editor_panes.set_visible_child(pane); } + /** + * Removes the current pane from the editor, showing the last one. + */ internal void pop() { // One can't simply remove old panes fro the GTK stack since // there won't be any transition between them - the old one @@ -124,6 +136,7 @@ public class Accounts.Editor : Gtk.Dialog { notification.show(); } + /** Removes an account from the editor. */ internal void remove_account(Geary.AccountInformation account) { this.editor_panes.set_visible_child(this.editor_list_pane); this.editor_list_pane.remove_account(account); From cc07e542acfac540b1e7be5ac8a69b552e9ec045 Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Wed, 9 Jan 2019 12:23:12 +1100 Subject: [PATCH 5/7] Allow adding/modifying an account to be cancelled when in progress This adds a cancellable property to each account pane and uses it for all I/O operations, and allows it to be cancelled and pane state to be updated when either Esc is pressed, or Cancel is clicked on the servers pane. --- .../accounts/accounts-editor-add-pane.vala | 42 +++-- .../accounts/accounts-editor-edit-pane.vala | 41 +++-- .../accounts/accounts-editor-list-pane.vala | 26 ++- .../accounts/accounts-editor-remove-pane.vala | 10 +- .../accounts-editor-servers-pane.vala | 154 ++++++++++++++---- src/client/accounts/accounts-editor.vala | 81 ++++++++- 6 files changed, 278 insertions(+), 76 deletions(-) diff --git a/src/client/accounts/accounts-editor-add-pane.vala b/src/client/accounts/accounts-editor-add-pane.vala index 3d7a5610..3c5468df 100644 --- a/src/client/accounts/accounts-editor-add-pane.vala +++ b/src/client/accounts/accounts-editor-add-pane.vala @@ -1,5 +1,5 @@ /* - * Copyright 2018 Michael Gratton + * Copyright 2018-2019 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. @@ -16,6 +16,17 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { get { return this.real_name.value; } } + /** {@inheritDoc} */ + internal bool is_operation_running { + get { return !this.sensitive; } + protected set { update_operation_ui(value); } + } + + /** {@inheritDoc} */ + internal GLib.Cancellable? op_cancellable { + get; protected set; default = new GLib.Cancellable(); + } + protected weak Accounts.Editor editor { get; set; } private Geary.ServiceProvider provider; @@ -51,6 +62,9 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { [GtkChild] private Gtk.Button create_button; + [GtkChild] + private Gtk.Button back_button; + [GtkChild] private Gtk.Spinner create_spinner; @@ -146,10 +160,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { } private async void validate_account(GLib.Cancellable? cancellable) { - this.create_spinner.show(); - this.create_spinner.start(); - this.create_button.set_sensitive(false); - this.set_sensitive(false); + this.is_operation_running = true; bool is_valid = false; string message = ""; @@ -182,6 +193,9 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { to_focus = this.imap_login.value; // Translators: In-app notification label message = _("Check your receiving login and password"); + } catch (GLib.IOError.CANCELLED err) { + // Nothing to do here, someone just cancelled + debug("IMAP validation was cancelled: %s", err.message); } catch (GLib.Error err) { debug("Error validating IMAP service: %s", err.message); this.imap_tls.show(); @@ -210,6 +224,9 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { to_focus = this.smtp_login.value; // Translators: In-app notification label message = _("Check your sending login and password"); + } catch (GLib.IOError.CANCELLED err) { + // Nothing to do here, someone just cancelled + debug("SMTP validation was cancelled: %s", err.message); } catch (GLib.Error err) { debug("Error validating SMTP service: %s", err.message); this.smtp_tls.show(); @@ -252,10 +269,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { } } - this.create_spinner.stop(); - this.create_spinner.hide(); - this.create_button.set_sensitive(true); - this.set_sensitive(true); + this.is_operation_running = false; // Focus and pop up the notification after re-sensitising // so it actually succeeds. @@ -357,6 +371,14 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { this.controls_valid = controls_valid; } + private void update_operation_ui(bool is_running) { + this.create_spinner.visible = is_running; + this.create_spinner.active = is_running; + this.create_button.sensitive = !is_running; + this.back_button.sensitive = !is_running; + this.sensitive = !is_running; + } + private void on_validated(Components.Validator.Trigger reason) { check_validation(); if (this.controls_valid && reason == Components.Validator.Trigger.ACTIVATED) { @@ -399,7 +421,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { [GtkCallback] private void on_create_button_clicked() { - this.validate_account.begin(null); + this.validate_account.begin(this.op_cancellable); } [GtkCallback] diff --git a/src/client/accounts/accounts-editor-edit-pane.vala b/src/client/accounts/accounts-editor-edit-pane.vala index a86ff117..d84727f5 100644 --- a/src/client/accounts/accounts-editor-edit-pane.vala +++ b/src/client/accounts/accounts-editor-edit-pane.vala @@ -1,5 +1,5 @@ /* - * Copyright 2018 Michael Gratton + * Copyright 2018-2019 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. @@ -26,6 +26,14 @@ internal class Accounts.EditorEditPane : get; protected set; default = new Application.CommandStack(); } + /** {@inheritDoc} */ + internal bool is_operation_running { get; protected set; default = false; } + + /** {@inheritDoc} */ + internal GLib.Cancellable? op_cancellable { + get; protected set; default = null; + } + /** {@inheritDoc} */ protected weak Accounts.Editor editor { get; set; } @@ -67,7 +75,9 @@ internal class Accounts.EditorEditPane : this.pane_content.set_focus_vadjustment(this.pane_adjustment); this.details_list.set_header_func(Editor.seperator_headers); - this.details_list.add(new DisplayNameRow(account, this.commands)); + this.details_list.add( + new DisplayNameRow(account, this.commands, this.op_cancellable) + ); this.senders_list.set_header_func(Editor.seperator_headers); foreach (Geary.RFC822.MailboxAddress sender in @@ -85,7 +95,9 @@ internal class Accounts.EditorEditPane : this.signature_preview.content_loaded.connect(() => { // Only enable editability after the content has fully // loaded to avoid the WebProcess crashing. - this.signature_preview.set_editable.begin(true, null); + this.signature_preview.set_editable.begin( + true, this.op_cancellable + ); }); this.signature_preview.document_modified.connect(() => { this.signature_changed = true; @@ -101,7 +113,7 @@ internal class Accounts.EditorEditPane : new SignatureChangedCommand( this.signature_preview, account ), - null + this.op_cancellable ); } return Gdk.EVENT_PROPAGATE; @@ -175,7 +187,7 @@ internal class Accounts.EditorEditPane : this.account, this.senders_list ), - null + this.op_cancellable ); } @@ -187,7 +199,7 @@ internal class Accounts.EditorEditPane : this.account, this.senders_list ), - null + this.op_cancellable ); } @@ -253,9 +265,12 @@ private class Accounts.DisplayNameRow : AccountRow { private Application.CommandStack commands; + private GLib.Cancellable? cancellable; + public DisplayNameRow(Geary.AccountInformation account, - Application.CommandStack commands) { + Application.CommandStack commands, + GLib.Cancellable? cancellable) { base( account, // Translators: Label in the account editor for the user's @@ -265,6 +280,7 @@ private class Accounts.DisplayNameRow : AccountRow { ); this.activatable = false; this.commands = commands; + this.cancellable = cancellable; update(); @@ -295,7 +311,7 @@ private class Accounts.DisplayNameRow : AccountRow { // account. _("Change account name back to ā€œ%sā€") ), - null + this.cancellable ); } @@ -335,7 +351,7 @@ private class Accounts.AddMailboxRow : AddRow { ) ) ), - null + pane.op_cancellable ); popover.popdown(); }); @@ -376,13 +392,14 @@ private class Accounts.MailboxRow : AccountRow { popover.address ) ), - null + pane.op_cancellable ); popover.popdown(); }); popover.remove_clicked.connect(() => { pane.commands.execute.begin( - new RemoveMailboxCommand(this), null + new RemoveMailboxCommand(this), + pane.op_cancellable ); popover.popdown(); }); @@ -788,7 +805,7 @@ private class Accounts.EmailPrefetchRow : get_label(this.account.prefetch_period_days) ) ), - null + pane.op_cancellable ); }); } diff --git a/src/client/accounts/accounts-editor-list-pane.vala b/src/client/accounts/accounts-editor-list-pane.vala index 5aa0975b..3cc59888 100644 --- a/src/client/accounts/accounts-editor-list-pane.vala +++ b/src/client/accounts/accounts-editor-list-pane.vala @@ -1,5 +1,5 @@ /* - * Copyright 2018 Michael Gratton + * Copyright 2018-2019 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. @@ -28,21 +28,29 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { } - /** {@iinheritDoc} */ + /** {@inheritDoc} */ internal Gtk.Widget initial_widget { get { return this.show_welcome ? this.service_list : this.accounts_list; } } - /** {@iinheritDoc} */ + /** {@inheritDoc} */ internal Application.CommandStack commands { get; protected set; default = new Application.CommandStack(); } + /** {@inheritDoc} */ + internal bool is_operation_running { get; protected set; default = false; } + + /** {@inheritDoc} */ + internal GLib.Cancellable? op_cancellable { + get; protected set; default = null; + } + internal Manager accounts { get; private set; } - /** {@iinheritDoc} */ + /** {@inheritDoc} */ protected weak Accounts.Editor editor { get; set; } private bool show_welcome { @@ -144,7 +152,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { if (row != null) { this.commands.execute.begin( new RemoveAccountCommand(account, this.accounts), - null + this.op_cancellable ); } } @@ -208,7 +216,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { new ReorderAccountCommand( (AccountListRow) source, new_position, this.accounts ), - null + this.op_cancellable ); } @@ -217,7 +225,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane { new ReorderAccountCommand( (AccountListRow) source, target.get_index(), this.accounts ), - null + this.op_cancellable ); } @@ -306,7 +314,7 @@ private class Accounts.AccountListRow : AccountRow { // GOA account but it's disabled, so just take people // directly to the GOA panel manager.show_goa_account.begin( - account, null, + account, pane.op_cancellable, (obj, res) => { try { manager.show_goa_account.end(res); @@ -446,7 +454,7 @@ private class Accounts.AddServiceProviderRow : EditorRow { public override void activated(EditorListPane pane) { pane.accounts.add_goa_account.begin( - this.provider, null, + this.provider, pane.op_cancellable, (obj, res) => { bool add_local = false; try { diff --git a/src/client/accounts/accounts-editor-remove-pane.vala b/src/client/accounts/accounts-editor-remove-pane.vala index 7698f14c..2a6844cd 100644 --- a/src/client/accounts/accounts-editor-remove-pane.vala +++ b/src/client/accounts/accounts-editor-remove-pane.vala @@ -1,5 +1,5 @@ /* - * Copyright 2018 Michael Gratton + * Copyright 2018-2019 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. @@ -23,6 +23,14 @@ internal class Accounts.EditorRemovePane : Gtk.Grid, EditorPane, AccountPane { get { return this.remove_button; } } + /** {@inheritDoc} */ + internal bool is_operation_running { get; protected set; default = false; } + + /** {@inheritDoc} */ + internal GLib.Cancellable? op_cancellable { + get; protected set; default = null; + } + [GtkChild] private Gtk.HeaderBar header; diff --git a/src/client/accounts/accounts-editor-servers-pane.vala b/src/client/accounts/accounts-editor-servers-pane.vala index 1718ea03..49004cc2 100644 --- a/src/client/accounts/accounts-editor-servers-pane.vala +++ b/src/client/accounts/accounts-editor-servers-pane.vala @@ -1,6 +1,6 @@ /* * Copyright 2016 Software Freedom Conservancy Inc. - * Copyright 2018 Michael Gratton + * Copyright 2018-2019 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. @@ -30,6 +30,17 @@ internal class Accounts.EditorServersPane : get { return this.details_list; } } + /** {@inheritDoc} */ + internal bool is_operation_running { + get { return !this.sensitive; } + protected set { update_operation_ui(value); } + } + + /** {@inheritDoc} */ + internal GLib.Cancellable? op_cancellable { + get; protected set; default = new GLib.Cancellable(); + } + private Geary.Engine engine; // These are copies of the originals that can be updated before @@ -102,7 +113,9 @@ internal class Accounts.EditorServersPane : service_provider.activatable = false; add_row(this.details_list, service_provider); - this.save_drafts = new SaveDraftsRow(this.account, this.commands); + this.save_drafts = new SaveDraftsRow( + this.account, this.commands, this.op_cancellable + ); add_row(this.details_list, this.save_drafts); // Receiving @@ -110,19 +123,36 @@ internal class Accounts.EditorServersPane : this.receiving_list.set_header_func(Editor.seperator_headers); add_row( this.receiving_list, - new ServiceHostRow(account, this.incoming_mutable, this.commands) + new ServiceHostRow( + account, + this.incoming_mutable, + this.commands, + this.op_cancellable + ) ); add_row( this.receiving_list, - new ServiceSecurityRow(account, this.incoming_mutable, this.commands) + new ServiceSecurityRow( + account, + this.incoming_mutable, + this.commands, + this.op_cancellable + ) ); this.incoming_password = new ServicePasswordRow( - account, this.incoming_mutable, this.commands + account, + this.incoming_mutable, + this.commands, + this.op_cancellable ); this.incoming_login = new ServiceLoginRow( - account, this.incoming_mutable, this.commands, this.incoming_password + account, + this.incoming_mutable, + this.commands, + this.op_cancellable, + this.incoming_password ); add_row(this.receiving_list, this.incoming_login); @@ -133,24 +163,45 @@ internal class Accounts.EditorServersPane : this.sending_list.set_header_func(Editor.seperator_headers); add_row( this.sending_list, - new ServiceHostRow(account, this.outgoing_mutable, this.commands) + new ServiceHostRow( + account, + this.outgoing_mutable, + this.commands, + this.op_cancellable + ) ); add_row( this.sending_list, - new ServiceSecurityRow(account, this.outgoing_mutable, this.commands) + new ServiceSecurityRow( + account, + this.outgoing_mutable, + this.commands, + this.op_cancellable + ) ); this.outgoing_auth = new ServiceOutgoingAuthRow( - account, this.outgoing_mutable, this.incoming_mutable, this.commands + account, + this.outgoing_mutable, + this.incoming_mutable, + this.commands, + this.op_cancellable ); this.outgoing_auth.value.changed.connect(on_outgoing_auth_changed); add_row(this.sending_list, this.outgoing_auth); this.outgoing_password = new ServicePasswordRow( - account, this.outgoing_mutable, this.commands + account, + this.outgoing_mutable, + this.commands, + this.op_cancellable ); this.outgoing_login = new ServiceLoginRow( - account, this.outgoing_mutable, this.commands, this.outgoing_password + account, + this.outgoing_mutable, + this.commands, + this.op_cancellable, + this.outgoing_password ); add_row(this.sending_list, this.outgoing_login); @@ -185,10 +236,7 @@ internal class Accounts.EditorServersPane : } private async void save(GLib.Cancellable? cancellable) { - this.apply_button.set_sensitive(false); - this.apply_spinner.show(); - this.apply_spinner.start(); - this.set_sensitive(false); + this.is_operation_running = true; // Only need to validate if a generic account bool is_valid = true; @@ -206,6 +254,8 @@ internal class Accounts.EditorServersPane : } } + this.is_operation_running = false; + if (is_valid) { if (this.save_drafts.value_changed) { has_changed = true; @@ -226,14 +276,10 @@ internal class Accounts.EditorServersPane : // updated already by the command this.account.save_drafts = this.save_drafts.initial_value; } - - this.apply_spinner.stop(); - this.apply_spinner.hide(); - this.set_sensitive(true); } private async bool validate(GLib.Cancellable? cancellable) { - string message = ""; + string? message = null; bool imap_valid = false; try { yield this.engine.validate_imap( @@ -244,6 +290,9 @@ internal class Accounts.EditorServersPane : debug("Error authenticating IMAP service: %s", err.message); // Translators: In-app notification label message = _("Check your receiving login and password"); + } catch (GLib.IOError.CANCELLED err) { + // Nothing to do here, someone just cancelled + debug("IMAP validation was cancelled: %s", err.message); } catch (GLib.Error err) { debug("Error validating IMAP service: %s", err.message); // Translators: In-app notification label @@ -269,6 +318,9 @@ internal class Accounts.EditorServersPane : this.outgoing_auth.value.source = Geary.Credentials.Requirement.CUSTOM; // Translators: In-app notification label message = _("Check your sending login and password"); + } catch (GLib.IOError.CANCELLED err) { + // Nothing to do here, someone just cancelled + debug("SMTP validation was cancelled: %s", err.message); } catch (GLib.Error err) { debug("Error validating SMTP service: %s", err.message); // Translators: In-app notification label @@ -279,7 +331,7 @@ internal class Accounts.EditorServersPane : bool is_valid = imap_valid && smtp_valid; debug("Validation complete, is valid: %s", is_valid.to_string()); - if (!is_valid) { + if (!is_valid && message != null) { this.editor.add_notification( new InAppNotification( // Translators: In-app notification label, the @@ -342,6 +394,13 @@ internal class Accounts.EditorServersPane : ); } + private void update_operation_ui(bool is_running) { + this.apply_spinner.visible = is_running; + this.apply_spinner.active = is_running; + this.apply_button.sensitive = !is_running; + this.sensitive = !is_running; + } + private void on_validator_changed() { this.apply_button.set_sensitive(is_valid()); } @@ -354,12 +413,16 @@ internal class Accounts.EditorServersPane : [GtkCallback] private void on_cancel_button_clicked() { - this.editor.pop(); + if (this.is_operation_running) { + cancel_operation(); + } else { + this.editor.pop(); + } } [GtkCallback] private void on_apply_button_clicked() { - this.save.begin(null); + this.save.begin(this.op_cancellable); } [GtkCallback] @@ -447,7 +510,7 @@ private class Accounts.AccountProviderRow : public override void activated(EditorServersPane pane) { if (this.accounts.is_goa_account(this.account)) { this.accounts.show_goa_account.begin( - account, null, + account, pane.op_cancellable, (obj, res) => { try { this.accounts.show_goa_account.end(res); @@ -476,9 +539,12 @@ private class Accounts.SaveDraftsRow : public bool initial_value { get; private set; } private Application.CommandStack commands; + private GLib.Cancellable? cancellable; + public SaveDraftsRow(Geary.AccountInformation account, - Application.CommandStack commands) { + Application.CommandStack commands, + GLib.Cancellable? cancellable) { Gtk.Switch value = new Gtk.Switch(); base( account, @@ -489,6 +555,7 @@ private class Accounts.SaveDraftsRow : ); update(); this.commands = commands; + this.cancellable = cancellable; this.activatable = false; this.initial_value = this.account.save_drafts; this.account.notify["save-drafts"].connect(on_account_changed); @@ -505,7 +572,7 @@ private class Accounts.SaveDraftsRow : new Application.PropertyCommand( this.account, "save_drafts", this.value.state ), - null + this.cancellable ); } } @@ -532,11 +599,13 @@ private class Accounts.ServiceHostRow : } private Application.CommandStack commands; + private GLib.Cancellable? cancellable; public ServiceHostRow(Geary.AccountInformation account, Geary.ServiceInformation service, - Application.CommandStack commands) { + Application.CommandStack commands, + GLib.Cancellable? cancellable) { string label = ""; switch (service.protocol) { case Geary.Protocol.IMAP: @@ -554,6 +623,7 @@ private class Accounts.ServiceHostRow : base(account, service, label, new Gtk.Entry()); this.commands = commands; + this.cancellable = cancellable; this.activatable = false; this.validator = new Components.NetworkAddressValidator(this.value); @@ -588,8 +658,8 @@ private class Accounts.ServiceHostRow : this.service, "port", port ) }), - null - ); + this.cancellable + ); } } @@ -613,16 +683,19 @@ private class Accounts.ServiceSecurityRow : private Application.CommandStack commands; + private GLib.Cancellable? cancellable; public ServiceSecurityRow(Geary.AccountInformation account, Geary.ServiceInformation service, - Application.CommandStack commands) { + Application.CommandStack commands, + GLib.Cancellable? cancellable) { TlsComboBox value = new TlsComboBox(); base(account, service, value.label, value); update(); this.commands = commands; + this.cancellable = cancellable; this.activatable = false; value.changed.connect(on_value_changed); } @@ -655,7 +728,7 @@ private class Accounts.ServiceSecurityRow : ) }); } - this.commands.execute.begin(cmd, null); + this.commands.execute.begin(cmd, this.cancellable); } } @@ -677,12 +750,14 @@ private class Accounts.ServiceLoginRow : } private Application.CommandStack commands; + private GLib.Cancellable? cancellable; private ServicePasswordRow? password_row; public ServiceLoginRow(Geary.AccountInformation account, Geary.ServiceInformation service, Application.CommandStack commands, + GLib.Cancellable? cancellable, ServicePasswordRow? password_row = null) { base( account, @@ -694,6 +769,7 @@ private class Accounts.ServiceLoginRow : ); this.commands = commands; + this.cancellable = cancellable; this.activatable = false; this.validator = new Components.Validator(this.value); this.password_row = password_row; @@ -737,7 +813,7 @@ private class Accounts.ServiceLoginRow : }); } - this.commands.execute.begin(cmd, null); + this.commands.execute.begin(cmd, this.cancellable); } } @@ -795,11 +871,13 @@ private class Accounts.ServicePasswordRow : } private Application.CommandStack commands; + private GLib.Cancellable? cancellable; public ServicePasswordRow(Geary.AccountInformation account, Geary.ServiceInformation service, - Application.CommandStack commands) { + Application.CommandStack commands, + GLib.Cancellable? cancellable) { base( account, service, @@ -810,6 +888,7 @@ private class Accounts.ServicePasswordRow : ); this.commands = commands; + this.cancellable = cancellable; this.activatable = false; this.value.visibility = false; this.value.input_purpose = Gtk.InputPurpose.PASSWORD; @@ -833,7 +912,7 @@ private class Accounts.ServicePasswordRow : "credentials", this.service.credentials.copy_with_token(this.value.text) ), - null + this.cancellable ); } } @@ -852,18 +931,21 @@ private class Accounts.ServiceOutgoingAuthRow : private Application.CommandStack commands; + private GLib.Cancellable? cancellable; private Geary.ServiceInformation imap_service; public ServiceOutgoingAuthRow(Geary.AccountInformation account, Geary.ServiceInformation smtp_service, Geary.ServiceInformation imap_service, - Application.CommandStack commands) { + Application.CommandStack commands, + GLib.Cancellable? cancellable) { OutgoingAuthComboBox value = new OutgoingAuthComboBox(); base(account, smtp_service, value.label, value); update(); this.commands = commands; + this.cancellable = cancellable; this.imap_service = imap_service; this.activatable = false; value.changed.connect(on_value_changed); @@ -911,7 +993,7 @@ private class Accounts.ServiceOutgoingAuthRow : ); } - this.commands.execute.begin(seq, null); + this.commands.execute.begin(seq, this.cancellable); } } diff --git a/src/client/accounts/accounts-editor.vala b/src/client/accounts/accounts-editor.vala index ca8753fc..7b6f0a04 100644 --- a/src/client/accounts/accounts-editor.vala +++ b/src/client/accounts/accounts-editor.vala @@ -73,20 +73,53 @@ public class Accounts.Editor : Gtk.Dialog { bool ret = Gdk.EVENT_PROPAGATE; // Allow the user to use Esc, Back and Alt+arrow keys to - // navigate between panes. - if (get_current_pane() != this.editor_list_pane) { + // navigate between panes. If a pane is executing a long + // running operation, only allow Esc and use it to cancel the + // operation instead. + EditorPane? current_pane = get_current_pane(); + if (current_pane != null && + current_pane != this.editor_list_pane) { Gdk.ModifierType state = ( event.state & Gtk.accelerator_get_default_mod_mask() ); bool is_ltr = (get_direction() == Gtk.TextDirection.LTR); - if (event.keyval == Gdk.Key.Escape || - event.keyval == Gdk.Key.Back || - (state == Gdk.ModifierType.MOD1_MASK && - (is_ltr && event.keyval == Gdk.Key.Left) || - (!is_ltr && event.keyval == Gdk.Key.Right))) { - pop(); + + switch (event.keyval) { + case Gdk.Key.Escape: + if (current_pane.is_operation_running) { + current_pane.cancel_operation(); + } else { + pop(); + } ret = Gdk.EVENT_STOP; + break; + + case Gdk.Key.Back: + if (!current_pane.is_operation_running) { + pop(); + ret = Gdk.EVENT_STOP; + } + break; + + case Gdk.Key.Left: + if (!current_pane.is_operation_running && + state == Gdk.ModifierType.MOD1_MASK && + is_ltr) { + pop(); + ret = Gdk.EVENT_STOP; + } + break; + + case Gdk.Key.Right: + if (!current_pane.is_operation_running && + state == Gdk.ModifierType.MOD1_MASK && + !is_ltr) { + pop(); + ret = Gdk.EVENT_STOP; + } + break; } + } if (ret != Gdk.EVENT_STOP) { @@ -222,9 +255,41 @@ internal interface Accounts.EditorPane : Gtk.Grid { /** The editor displaying this pane. */ internal abstract Gtk.Widget initial_widget { get; } + /** + * Determines if a long running operation is being executed. + * + * @see cancel_operation + */ + internal abstract bool is_operation_running { get; protected set; } + + /** + * Long running operation cancellable. + * + * This cancellable must be passed to any long-running operations + * involving I/O. If not null and operation is cancelled, the + * value should be cancelled and replaced with a new instance. + * + * @see cancel_operation + */ + internal abstract GLib.Cancellable? op_cancellable { get; protected set; } + /** The GTK header bar to display for this pane. */ internal abstract Gtk.HeaderBar get_header(); + /** + * Cancels this pane's current operation, any. + * + * Sets {@link is_operation_running} to false and if {@link + * op_cancellable} is not null, it is cancelled and replaced with + * a new instance. + */ + internal void cancel_operation() { + this.is_operation_running = false; + if (this.op_cancellable != null) { + this.op_cancellable.cancel(); + this.op_cancellable = new GLib.Cancellable(); + } + } } From db85a3449d26ad645f6fee3d161f465b77f44bbb Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Wed, 9 Jan 2019 12:26:13 +1100 Subject: [PATCH 6/7] Don't validate managed accounts when validating account server details GOA supports generic IMAP accounts, we shouldn't be attempting to validate the server settings for those. --- src/client/accounts/accounts-editor-servers-pane.vala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/client/accounts/accounts-editor-servers-pane.vala b/src/client/accounts/accounts-editor-servers-pane.vala index 49004cc2..38a4b3c0 100644 --- a/src/client/accounts/accounts-editor-servers-pane.vala +++ b/src/client/accounts/accounts-editor-servers-pane.vala @@ -238,12 +238,14 @@ internal class Accounts.EditorServersPane : private async void save(GLib.Cancellable? cancellable) { this.is_operation_running = true; - // Only need to validate if a generic account + // Only need to validate if a generic, local account since + // other account types have read-only incoming/outgoing + // settings bool is_valid = true; bool has_changed = false; - if (this.account.service_provider == Geary.ServiceProvider.OTHER) { + if (this.account.service_provider == Geary.ServiceProvider.OTHER && + !this.editor.accounts.is_goa_account(this.account)) { is_valid = yield validate(cancellable); - if (is_valid) { has_changed |= yield update_service( this.account.incoming, this.incoming_mutable, cancellable From 33ae35f613ea76cd07f9b81474a0086fa74716fc Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Wed, 9 Jan 2019 12:27:52 +1100 Subject: [PATCH 7/7] Fix critical when IMAP login command is cancelled The command's status will be null if the op was cancelled. --- .../imap/transport/imap-client-session.vala | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/engine/imap/transport/imap-client-session.vala b/src/engine/imap/transport/imap-client-session.vala index ef3d44c7..4613a0d6 100644 --- a/src/engine/imap/transport/imap-client-session.vala +++ b/src/engine/imap/transport/imap-client-session.vala @@ -881,14 +881,16 @@ public class Geary.Imap.ClientSession : BaseObject { } if (login_err != null) { - // Throw an error indicating auth failed here, unless the - // response code indicated that the server is merely - // unavailable, then don't since the creds might actually - // be fine. - ResponseCode? code = cmd.status.response_code; + // Throw an error indicating auth failed here, unless + // there is a status response and it indicates that the + // server is merely reporting login as being unavailable, + // then don't since the creds might actually be fine. ResponseCodeType? code_type = null; - if (code != null) { - code_type = code.get_response_code_type(); + if (cmd.status != null) { + ResponseCode? code = cmd.status.response_code; + if (code != null) { + code_type = code.get_response_code_type(); + } } if (code_type == null ||