[geary/mjog/account-command-stacks: 20/27] Make Application.Controller commands per-account



commit 83bcacd032126bd253c578db0486d182e04d21ee
Author: Michael Gratton <mike vee net>
Date:   Wed Oct 23 01:17:17 2019 +1100

    Make Application.Controller commands per-account
    
    Give each account its own command stack, and use that when executing
    commands for an account, since people are unlikely to select a different
    account and expect commands for the previous account to work.

 src/client/application/application-controller.vala | 59 ++++-----------
 src/client/components/main-window.vala             | 83 +++++++++++++++++-----
 2 files changed, 79 insertions(+), 63 deletions(-)
---
diff --git a/src/client/application/application-controller.vala 
b/src/client/application/application-controller.vala
index a904acda..0fc93e1a 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -47,6 +47,11 @@ public class Application.Controller : Geary.BaseObject {
         /** The account's contact store */
         public Application.ContactStore contacts { get; private set; }
 
+        /** The account's application command stack. */
+        public Application.CommandStack commands {
+            get; protected set; default = new CommandStack();
+        }
+
         /** A cancellable tied to the life-cycle of the account. */
         public Cancellable cancellable {
             get; private set; default = new Cancellable();
@@ -154,8 +159,6 @@ public class Application.Controller : Geary.BaseObject {
     // Null if none selected
     private Geary.Folder? current_folder = null;
 
-    private Application.CommandStack commands { get; protected set; }
-
     private Cancellable cancellable_folder = new Cancellable();
     private Cancellable cancellable_search = new Cancellable();
     private Cancellable cancellable_open_account = new Cancellable();
@@ -233,10 +236,8 @@ public class Application.Controller : Geary.BaseObject {
         );
         this.plugin_manager.load();
 
-        this.commands = new CommandStack();
-
         // Create the main window (must be done after creating actions.)
-        main_window = new MainWindow(this.application, this.commands);
+        main_window = new MainWindow(this.application);
         main_window.retry_service_problem.connect(on_retry_service_problem);
         main_window.notify["has-toplevel-focus"].connect(on_has_toplevel_focus);
 
@@ -319,36 +320,6 @@ public class Application.Controller : Geary.BaseObject {
         return this.accounts.get(account);
     }
 
-    /** Un-does the last executed application command, if any. */
-    public async void undo() {
-        this.commands.undo.begin(
-            this.open_cancellable,
-            (obj, res) => {
-                try {
-                    this.commands.undo.end(res);
-                } catch (GLib.Error err) {
-                    // XXX extract account info somehow
-                    report_problem(new Geary.ProblemReport(err));
-                }
-            }
-        );
-    }
-
-    /** Re-does the last undone application command, if any. */
-    public async void redo() {
-        this.commands.redo.begin(
-            this.open_cancellable,
-            (obj, res) => {
-                try {
-                    this.commands.redo.end(res);
-                } catch (GLib.Error err) {
-                    // XXX extract account info somehow
-                    report_problem(new Geary.ProblemReport(err));
-                }
-            }
-        );
-    }
-
     /** Closes all accounts and windows, releasing held resources. */
     public async void close_async() {
         // Cancel internal processes early so they don't block
@@ -580,7 +551,7 @@ public class Application.Controller : Geary.BaseObject {
 
         AccountContext? context = this.accounts.get(target.information);
         if (context != null) {
-            yield this.commands.execute(
+            yield context.commands.execute(
                 new MarkEmailCommand(
                     context.emails,
                     messages,
@@ -620,7 +591,7 @@ public class Application.Controller : Geary.BaseObject {
         throws GLib.Error {
         AccountContext? context = this.accounts.get(target.information);
         if (context != null) {
-            yield this.commands.execute(
+            yield context.commands.execute(
                 new MarkEmailCommand(
                     context.emails,
                     messages,
@@ -650,7 +621,7 @@ public class Application.Controller : Geary.BaseObject {
         throws GLib.Error {
         AccountContext? context = this.accounts.get(source.account.information);
         if (context != null) {
-            yield this.commands.execute(
+            yield context.commands.execute(
                 new MoveEmailCommand(
                     source,
                     destination,
@@ -690,7 +661,7 @@ public class Application.Controller : Geary.BaseObject {
                 );
             }
 
-            yield this.commands.execute(
+            yield context.commands.execute(
                 new MoveEmailCommand(
                     source,
                     dest,
@@ -730,7 +701,7 @@ public class Application.Controller : Geary.BaseObject {
                 );
             }
 
-            yield this.commands.execute(
+            yield context.commands.execute(
                 new MoveEmailCommand(
                     source,
                     dest,
@@ -763,7 +734,7 @@ public class Application.Controller : Geary.BaseObject {
         throws GLib.Error {
         AccountContext? context = this.accounts.get(source.account.information);
         if (context != null) {
-            yield this.commands.execute(
+            yield context.commands.execute(
                 new CopyEmailCommand(
                     source,
                     destination,
@@ -795,7 +766,7 @@ public class Application.Controller : Geary.BaseObject {
         throws GLib.Error {
         AccountContext? context = this.accounts.get(target.account.information);
         if (context != null) {
-            yield this.commands.execute(
+            yield context.commands.execute(
                 new DeleteEmailCommand(
                     target,
                     to_in_folder_email_ids(conversations)
@@ -810,7 +781,7 @@ public class Application.Controller : Geary.BaseObject {
         throws GLib.Error {
         AccountContext? context = this.accounts.get(target.account.information);
         if (context != null) {
-            yield this.commands.execute(
+            yield context.commands.execute(
                 new DeleteEmailCommand(target, messages),
                 context.cancellable
             );
@@ -832,7 +803,7 @@ public class Application.Controller : Geary.BaseObject {
                 );
             }
 
-            yield this.commands.execute(
+            yield context.commands.execute(
                 new EmptyFolderCommand(emptyable),
                 context.cancellable
             );
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index 19b583d2..cdeb3887 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -195,6 +195,8 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
     public StatusBar status_bar { get; private set; default = new StatusBar(); }
     private MonitoredSpinner spinner = new MonitoredSpinner();
 
+    private Application.Controller.AccountContext? context = null;
+
     private Geary.AggregateProgressMonitor progress_monitor = new Geary.AggregateProgressMonitor();
 
     private GLib.Cancellable action_update_cancellable = new GLib.Cancellable();
@@ -203,8 +205,6 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
     private Geary.TimeoutManager update_ui_timeout;
     private int64 update_ui_last = 0;
 
-    private Application.CommandStack commands { get; protected set; }
-
 
     [GtkChild]
     private Gtk.Box main_layout;
@@ -250,8 +250,7 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
     public signal void on_shift_key(bool pressed);
 
 
-    public MainWindow(GearyApplication application,
-                      Application.CommandStack commands) {
+    public MainWindow(GearyApplication application) {
         Object(
             application: application,
             show_menubar: false
@@ -266,12 +265,7 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         setup_layout(application.config);
         on_change_orientation();
 
-        this.commands = commands;
-        this.commands.executed.connect(on_command_execute);
-        this.commands.undone.connect(on_command_undo);
-        this.commands.redone.connect(on_command_redo);
         update_command_actions();
-
         update_conversation_actions(NONE);
 
         this.application.engine.account_available.connect(on_account_available);
@@ -377,8 +371,9 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
 
     /** Deselected the currently selected account, if any. */
     public void deselect_account() {
+        // XXX do other things like select the first/next most highest
+        // account's inbox?
         this.search_bar.set_search_text(""); // Reset search.
-        // XXX do other things
     }
 
     /** Displays a composer addressed to a specific email address. */
@@ -705,12 +700,47 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         update_headerbar();
     }
 
+    /** Un-does the last executed application command, if any. */
+    private async void undo() {
+        Application.Controller.AccountContext? selected = this.context;
+        if (selected != null) {
+            selected.commands.undo.begin(
+                selected.cancellable,
+                (obj, res) => {
+                    try {
+                        selected.commands.undo.end(res);
+                    } catch (GLib.Error err) {
+                        handle_error(selected.account.information, err);
+                    }
+                }
+            );
+        }
+    }
+
+    /** Re-does the last undone application command, if any. */
+    private async void redo() {
+        Application.Controller.AccountContext? selected = this.context;
+        if (selected != null) {
+            selected.commands.redo.begin(
+                selected.cancellable,
+                (obj, res) => {
+                    try {
+                        selected.commands.redo.end(res);
+                    } catch (GLib.Error err) {
+                        handle_error(selected.account.information, err);
+                    }
+                }
+            );
+        }
+    }
+
     private void update_command_actions() {
+        Application.Controller.AccountContext? selected = this.context;
         get_action(GearyApplication.ACTION_UNDO).set_enabled(
-            this.commands.can_undo
+            selected != null && selected.commands.can_undo
         );
         get_action(GearyApplication.ACTION_REDO).set_enabled(
-            this.commands.can_redo
+            selected != null && selected.commands.can_redo
         );
     }
 
@@ -791,17 +821,36 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
             if (this.selected_account != null) {
                 this.main_toolbar.copy_folder_menu.clear();
                 this.main_toolbar.move_folder_menu.clear();
+
+                Application.Controller.AccountContext? context = this.context;
+                if (context != null) {
+                    context.commands.executed.disconnect(on_command_execute);
+                    context.commands.undone.disconnect(on_command_undo);
+                    context.commands.redone.disconnect(on_command_redo);
+                }
+                this.context = null;
             }
 
             this.selected_account = account;
             this.search_bar.set_account(account);
 
             if (account != null) {
+                this.context = this.application.controller.get_context_for_account(
+                    account.information
+                );
+                if (this.context != null) {
+                    this.context.commands.executed.connect(on_command_execute);
+                    this.context.commands.undone.connect(on_command_undo);
+                    this.context.commands.redone.connect(on_command_redo);
+                }
+
                 foreach (Geary.Folder folder in account.list_folders()) {
                     this.main_toolbar.copy_folder_menu.add_folder(folder);
                     this.main_toolbar.move_folder_menu.add_folder(folder);
                 }
             }
+
+            update_command_actions();
         }
     }
 
@@ -939,16 +988,12 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
                     selected
                 );
 
-                Application.Controller.AccountContext? context =
-                    this.application.controller.get_context_for_account(
-                        convo.base_folder.account.information
-                    );
-
                 // It's possible for a conversation with zero email to
                 // be selected, when it has just evaporated after its
                 // last email was removed but the conversation monitor
                 // hasn't signalled its removal yet. In this case,
                 // just don't load it since it will soon disappear.
+                Application.Controller.AccountContext? context = this.context;
                 if (context != null && convo.get_count() > 0) {
                     this.conversation_viewer.load_conversation.begin(
                         convo,
@@ -1381,11 +1426,11 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
     // Window-level action callbacks
 
     private void on_undo() {
-        this.application.controller.undo.begin();
+        this.undo.begin();
     }
 
     private void on_redo() {
-        this.application.controller.redo.begin();
+        this.redo.begin();
     }
 
     private void on_close() {


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