[geary/wip/730682-refine-convo-list] Move Controller's ConversationMonitor to MainWindow.



commit fa62f4e1475ed01ff7d11389c40ff024ceb70325
Author: Michael James Gratton <mike vee net>
Date:   Thu Dec 28 11:44:53 2017 +1100

    Move Controller's ConversationMonitor to MainWindow.
    
    It's per-window specific, and managing it there means we can make the
    folder/monitor loading process more obvious.
    
    * src/client/components/main-window.vala (MainWindow): Add a
      ConversationMonitor, merge folder and monitor management code so we are
      now updating the folder and closing the monitor as needed. When
      closing the window, ensure the conversation monitor is stopped.
    
    * src/client/application/geary-controller.vala (GearyController): Remove
      ConversationMonitor instance and related code. When quitting, close the
      main window if not already closing.

 src/client/application/geary-controller.vala       |   68 ++-------
 src/client/components/main-window.vala             |  154 +++++++++++---------
 .../conversation-list/conversation-list-view.vala  |   80 +++++-----
 3 files changed, 140 insertions(+), 162 deletions(-)
---
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 4317efa..578eb7d 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -47,11 +47,6 @@ public class GearyController : Geary.BaseObject {
     public const string ACTION_TOGGLE_SEARCH = "toggle-search";
     public const string ACTION_TOGGLE_FIND = "toggle-find";
 
-    // Properties
-    public const string PROP_CURRENT_CONVERSATION ="current-conversations";
-
-    internal const int CONVERSATION_PAGE_SIZE = 50;
-
     private const int SELECT_FOLDER_TIMEOUT_USEC = 100 * 1000;
 
     private const string PROP_ATTEMPT_OPEN_ACCOUNT = "attempt-open-account";
@@ -59,11 +54,9 @@ public class GearyController : Geary.BaseObject {
     public weak GearyApplication application { get; private set; } // circular ref
 
     public MainWindow main_window { get; private set; }
-    
-    public Geary.App.ConversationMonitor? current_conversations { get; private set; default = null; }
-    
+
     public AutostartManager? autostart_manager { get; private set; default = null; }
-    
+
     public LoginDialog? login_dialog { get; private set; default = null; }
 
     public Soup.Session? avatar_session { get; private set; default = null; }
@@ -301,31 +294,17 @@ public class GearyController : Geary.BaseObject {
             on_conversation_view_added
         );
 
-        // hide window while shutting down, as this can take a few seconds under certain conditions
-        main_window.hide();
-        
+        // Ensure the main window is closed if not already doing so
+        // that its conversation monitor stops accessing the network.
+        if (!this.main_window.is_closing) {
+            main_window.close();
+        }
+
         // drop the Revokable, which will commit it if necessary
         save_revokable(null, null);
 
         this.autostart_manager = null;
 
-        // Close the ConversationMonitor
-        if (current_conversations != null) {
-            debug("Stopping conversation monitor for %s...",
-                  this.current_conversations.base_folder.to_string());
-            try {
-                yield this.current_conversations.stop_monitoring_async(null);
-            } catch (Error err) {
-                debug(
-                    "Error closing conversation monitor %s at shutdown: %s",
-                    this.current_conversations.base_folder.to_string(),
-                    err.message
-                );
-            } finally {
-                this.current_conversations = null;
-            }
-        }
-
         // Close all inboxes. Launch these in parallel so we're not
         // waiting time waiting for each one to close. The account
         // will wait around for them to actually close.
@@ -1375,21 +1354,10 @@ public class GearyController : Geary.BaseObject {
         // mutex lock is a bandaid solution to make the function safe to
         // reenter.
         int mutex_token = yield select_folder_mutex.claim_async(cancellable_folder);
-        
-        bool current_is_inbox = inboxes.values.contains(current_folder);
-        
-        Cancellable? conversation_cancellable = (current_is_inbox ?
-            inbox_cancellables.get(folder.account) : cancellable_folder);
-        
+
         // clear Revokable, as Undo is only available while a folder is selected
         save_revokable(null, null);
         
-        // stop monitoring for conversations and close the folder
-        if (current_conversations != null) {
-            yield current_conversations.stop_monitoring_async(null);
-            current_conversations = null;
-        }
-        
         // re-enable copy/move to the last selected folder
         if (current_folder != null) {
             main_window.main_toolbar.copy_folder_menu.enable_disable_folder(current_folder, true);
@@ -1434,21 +1402,11 @@ public class GearyController : Geary.BaseObject {
             !(current_folder is Geary.FolderSupport.Remove)
         );
 
-        current_conversations = new Geary.App.ConversationMonitor(
-            current_folder,
-            Geary.Folder.OpenFlags.NO_DELAY,
-            ConversationListModel.REQUIRED_FIELDS,
-            CONVERSATION_PAGE_SIZE * 2 // load double up front when not scrolling
-        );
-
         if (inboxes.values.contains(current_folder)) {
             // Inbox selected, clear new messages if visible
             clear_new_messages("do_select_folder (inbox)", null);
         }
 
-        if (!current_conversations.is_monitoring)
-            yield current_conversations.start_monitoring_async(conversation_cancellable);
-        
         select_folder_mutex.release(ref mutex_token);
         
         debug("Switched to %s", folder.to_string());
@@ -1459,11 +1417,11 @@ public class GearyController : Geary.BaseObject {
         
         if (folder == null || email == null || !can_switch_conversation_view())
             return;
-        
-        main_window.folder_list.select_folder(folder);
-        Geary.App.Conversation? conversation = current_conversations.get_conversation_for_email(email.id);
+
+        this.main_window.folder_list.select_folder(folder);
+        Geary.App.Conversation? conversation = 
this.main_window.current_conversations.get_conversation_for_email(email.id);
         if (conversation != null)
-            main_window.conversation_list.select_conversation(conversation);
+            this.main_window.conversation_list.select_conversation(conversation);
     }
 
     private void on_indicator_activated_application(uint32 timestamp) {
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index 97f0e09..afc9d7a 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -23,6 +23,8 @@ public class MainWindow : Gtk.ApplicationWindow {
     public const string ACTION_SELECTION_MODE_DISABLE = "selection-mode-disable";
     public const string ACTION_SELECTION_MODE_ENABLE = "selection-mode-enable";
 
+    internal const int CONVERSATION_PAGE_SIZE = 50;
+
     private const int STATUS_BAR_HEIGHT = 18;
 
     private const ActionEntry[] action_entries = {
@@ -51,6 +53,9 @@ public class MainWindow : Gtk.ApplicationWindow {
     public int window_height { get; set; }
     public bool window_maximized { get; set; }
 
+    /** Determines if the window is shutting down. */
+    public bool is_closing { get; private set; default = false; }
+
     // Widget descendants
     public FolderList.Tree folder_list { get; private set; default = new FolderList.Tree(); }
     public MainToolbar main_toolbar { get; private set; }
@@ -60,12 +65,13 @@ public class MainWindow : Gtk.ApplicationWindow {
     public StatusBar status_bar { get; private set; default = new StatusBar(); }
 
     public Geary.Folder? current_folder { get; private set; default = null; }
+    public Geary.App.ConversationMonitor? current_conversations { get; private set; default = null; }
+    private Cancellable load_cancellable = new Cancellable();
 
     private ConversationActionBar conversation_list_actions =
         new ConversationActionBar();
     private Geary.AggregateProgressMonitor progress_monitor =
         new Geary.AggregateProgressMonitor();
-    private Geary.ProgressMonitor? folder_progress = null;
 
     private MonitoredSpinner spinner = new MonitoredSpinner();
 
@@ -117,8 +123,6 @@ public class MainWindow : Gtk.ApplicationWindow {
         load_config(application.config);
         restore_saved_window_state();
 
-        application.controller.notify[GearyController.PROP_CURRENT_CONVERSATION]
-            .connect(on_conversation_monitor_changed);
         application.controller.folder_selected.connect(on_folder_selected);
         this.application.engine.account_available.connect(on_account_available);
         this.application.engine.account_unavailable.connect(on_account_unavailable);
@@ -424,8 +428,74 @@ public class MainWindow : Gtk.ApplicationWindow {
         }
     }
 
-    private inline SimpleAction get_action(string name) {
-        return (SimpleAction) lookup_action(name);
+    private void update_folder(Geary.Folder folder) {
+        debug("Loading new folder: %s...", folder.to_string());
+        this.conversation_list.freeze_selection();
+
+        if (this.current_folder != null) {
+            this.progress_monitor.remove(this.current_folder.opening_monitor);
+            this.progress_monitor.remove(this.current_conversations.progress_monitor);
+            this.progress_monitor.remove(this.conversation_list.model.previews.progress);
+
+            this.current_folder.properties.notify.disconnect(update_headerbar);
+            this.stop_conversation_monitor.begin();
+        }
+
+        folder.properties.notify.connect(update_headerbar);
+        this.current_folder = folder;
+
+        // Set up a new conversation monitor for the folder
+        Geary.App.ConversationMonitor monitor = new Geary.App.ConversationMonitor(
+            folder,
+            Geary.Folder.OpenFlags.NO_DELAY,
+            ConversationListModel.REQUIRED_FIELDS,
+            CONVERSATION_PAGE_SIZE * 2 // load double up front when not scrolling
+        );
+        monitor.scan_error.connect(on_scan_error);
+        monitor.seed_completed.connect(on_seed_completed);
+        monitor.seed_completed.connect(on_initial_conversation_load);
+        monitor.seed_completed.connect(on_conversation_count_changed);
+        monitor.scan_completed.connect(on_conversation_count_changed);
+        monitor.conversations_added.connect(on_conversation_count_changed);
+        monitor.conversations_removed.connect(on_conversation_count_changed);
+        monitor.email_flags_changed.connect(on_conversation_flags_changed);
+
+        this.current_conversations = monitor;
+        this.conversation_list.bind_model(monitor);
+
+        // Update the UI
+        this.conversation_list_actions.set_account(folder.account);
+        this.conversation_list_actions.update_location(this.current_folder);
+        update_headerbar();
+        set_selection_mode_enabled(false);
+
+        this.progress_monitor.add(folder.opening_monitor);
+        this.progress_monitor.add(monitor.progress_monitor);
+        this.progress_monitor.add(this.conversation_list.model.previews.progress);
+
+        // Finally, start the folder loading
+        this.load_cancellable = new Cancellable();
+        monitor.start_monitoring_async.begin(this.load_cancellable);
+        this.conversation_list.thaw_selection();
+    }
+
+    private async void stop_conversation_monitor() {
+        Geary.App.ConversationMonitor old_monitor = this.current_conversations;
+        if (old_monitor != null) {
+            this.current_conversations = null;
+
+            // Cancel any pending operations, then shut it down
+            this.load_cancellable.cancel();
+            try {
+                yield old_monitor.stop_monitoring_async(null);
+            } catch (Error err) {
+                debug(
+                    "Error closing conversation monitor %s on close: %s",
+                    old_monitor.base_folder.to_string(),
+                    err.message
+                );
+            }
+        }
     }
 
     private void show_conversation(Geary.App.Conversation? target) {
@@ -478,72 +548,14 @@ public class MainWindow : Gtk.ApplicationWindow {
               err.message);
     }
 
-    private void on_conversation_monitor_changed() {
-        this.conversation_list.freeze_selection();
-        ConversationListModel? old_model = this.conversation_list.model;
-        if (old_model != null) {
-            this.progress_monitor.remove(old_model.monitor.progress_monitor);
-            this.progress_monitor.remove(old_model.previews.progress);
-
-            Geary.App.ConversationMonitor? old_monitor = old_model.monitor;
-            old_monitor.scan_error.disconnect(on_scan_error);
-            old_monitor.seed_completed.disconnect(on_seed_completed);
-            old_monitor.seed_completed.disconnect(on_conversation_count_changed);
-            old_monitor.seed_completed.disconnect(on_initial_conversation_load);
-            old_monitor.scan_completed.disconnect(on_conversation_count_changed);
-            old_monitor.conversations_added.disconnect(on_conversation_count_changed);
-            old_monitor.conversations_removed.disconnect(on_conversation_count_changed);
-            old_monitor.email_flags_changed.disconnect(on_conversation_flags_changed);
-        }
-
-        Geary.App.ConversationMonitor? new_monitor =
-            this.application.controller.current_conversations;
-        if (new_monitor != null) {
-            this.conversation_list.bind_model(new_monitor);
-            ConversationListModel new_model = this.conversation_list.model;
-
-            this.progress_monitor.add(new_model.monitor.progress_monitor);
-            this.progress_monitor.add(new_model.previews.progress);
-
-            new_monitor.scan_error.connect(on_scan_error);
-            new_monitor.seed_completed.connect(on_seed_completed);
-            new_monitor.seed_completed.connect(on_conversation_count_changed);
-            new_monitor.seed_completed.connect(on_initial_conversation_load);
-            new_monitor.scan_completed.connect(on_conversation_count_changed);
-            new_monitor.conversations_added.connect(on_conversation_count_changed);
-            new_monitor.conversations_removed.connect(on_conversation_count_changed);
-            new_monitor.email_flags_changed.connect(on_conversation_flags_changed);
-        }
-        this.conversation_list.thaw_selection();
+    private inline SimpleAction get_action(string name) {
+        return (SimpleAction) lookup_action(name);
     }
 
     private void on_folder_selected(Geary.Folder? folder) {
-        if (this.folder_progress != null) {
-            this.progress_monitor.remove(this.folder_progress);
-            this.folder_progress = null;
-        }
-
-        if (folder != null) {
-            this.folder_progress = folder.opening_monitor;
-            this.progress_monitor.add(this.folder_progress);
-        }
-
-        // disconnect from old folder
-        if (this.current_folder != null)
-            this.current_folder.properties.notify.disconnect(update_headerbar);
-
-        // connect to new folder
         if (folder != null) {
-            folder.properties.notify.connect(update_headerbar);
-            this.conversation_list_actions.set_account(folder.account);
-            this.conversation_list_actions.update_location(this.current_folder);
+            update_folder(folder);
         }
-
-        // swap it in
-        this.current_folder = folder;
-
-        update_headerbar();
-        set_selection_mode_enabled(false);
     }
 
     private void on_account_available(Geary.AccountInformation account) {
@@ -639,7 +651,9 @@ public class MainWindow : Gtk.ApplicationWindow {
 
     private void on_load_more() {
         debug("on_load_more");
-        this.application.controller.current_conversations.min_window_count += 
GearyController.CONVERSATION_PAGE_SIZE;
+        if (this.current_conversations != null) {
+            this.current_conversations.min_window_count += CONVERSATION_PAGE_SIZE;
+        }
     }
 
     private void on_scan_error(Error err) {
@@ -683,6 +697,12 @@ public class MainWindow : Gtk.ApplicationWindow {
                 hide();
             }
         } else {
+            set_sensitive(false);
+            this.is_closing = true;
+            this.stop_conversation_monitor.begin((obj, res) => {
+                    this.stop_conversation_monitor.end(res);
+                    hide();
+                });
             this.application.exit();
         }
         return Gdk.EVENT_STOP;
diff --git a/src/client/conversation-list/conversation-list-view.vala 
b/src/client/conversation-list/conversation-list-view.vala
index c636be1..ec4f72f 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -16,7 +16,7 @@ public class ConversationListView : Gtk.TreeView {
     // scroll adjustment seen at the call to load_more().
     private double last_upper = -1.0;
     private bool reset_adjustment = false;
-    private Geary.App.ConversationMonitor? conversation_monitor;
+    //private Geary.App.ConversationMonitor? conversation_monitor;
     private Gee.Set<Geary.App.Conversation>? current_visible_conversations = null;
     private Geary.Scheduler.Scheduled? scheduled_update_visible_conversations = null;
     private Gee.Set<Geary.App.Conversation> selected = new Gee.HashSet<Geary.App.Conversation>();
@@ -61,8 +61,8 @@ public class ConversationListView : Gtk.TreeView {
         
         GearyApplication.instance.config.settings.changed[Configuration.DISPLAY_PREVIEW_KEY].connect(
             on_display_preview_changed);
-        GearyApplication.instance.controller.notify[GearyController.PROP_CURRENT_CONVERSATION].
-            connect(on_conversation_monitor_changed);
+        // GearyApplication.instance.controller.notify[GearyController.PROP_CURRENT_CONVERSATION].
+        //     connect(on_conversation_monitor_changed);
         
         // Watch for mouse events.
         motion_notify_event.connect(on_motion_notify_event);
@@ -134,47 +134,47 @@ public class ConversationListView : Gtk.TreeView {
         }
     }
 
-    private void on_conversation_monitor_changed() {
-        if (conversation_monitor != null) {
-            conversation_monitor.scan_started.disconnect(on_scan_started);
-            conversation_monitor.scan_completed.disconnect(on_scan_completed);
-            conversation_monitor.seed_completed.disconnect(on_seed_completed);
-        }
+    // private void on_conversation_monitor_changed() {
+    //     if (conversation_monitor != null) {
+    //         conversation_monitor.scan_started.disconnect(on_scan_started);
+    //         conversation_monitor.scan_completed.disconnect(on_scan_completed);
+    //         conversation_monitor.seed_completed.disconnect(on_seed_completed);
+    //     }
         
-        conversation_monitor = GearyApplication.instance.controller.current_conversations;
+    //     conversation_monitor = GearyApplication.instance.controller.current_conversations;
         
-        if (conversation_monitor != null) {
-            conversation_monitor.scan_started.connect(on_scan_started);
-            conversation_monitor.scan_completed.connect(on_scan_completed);
-            conversation_monitor.seed_completed.connect(on_seed_completed);
-        }
-    }
+    //     if (conversation_monitor != null) {
+    //         conversation_monitor.scan_started.connect(on_scan_started);
+    //         conversation_monitor.scan_completed.connect(on_scan_completed);
+    //         conversation_monitor.seed_completed.connect(on_seed_completed);
+    //     }
+    // }
     
-    private void on_scan_started() {
-        enable_load_more = false;
-    }
+    // private void on_scan_started() {
+    //     enable_load_more = false;
+    // }
     
-    private void on_scan_completed() {
-        enable_load_more = true;
-
-        // Select the first conversation, if autoselect is enabled,
-        // nothing has been selected yet and we're not composing. Do
-        // this here instead of in on_seed_completed since we want to
-        // to select the first row on folder change as soon as
-        // possible.
-        if (GearyApplication.instance.config.autoselect &&
-            get_selection().count_selected_rows() == 0 &&
-            !GearyApplication.instance.controller.any_inline_composers()) {
-            set_cursor(new Gtk.TreePath.from_indices(0, -1), null, false);
-        }
-    }
-
-    private void on_seed_completed() {
-        if (!GearyApplication.instance.config.autoselect) {
-            // Notify that no conversations will be selected
-            conversations_selected(this.selected.read_only_view);
-        }
-    }
+    // private void on_scan_completed() {
+    //     enable_load_more = true;
+
+    //     // Select the first conversation, if autoselect is enabled,
+    //     // nothing has been selected yet and we're not composing. Do
+    //     // this here instead of in on_seed_completed since we want to
+    //     // to select the first row on folder change as soon as
+    //     // possible.
+    //     if (GearyApplication.instance.config.autoselect &&
+    //         get_selection().count_selected_rows() == 0 &&
+    //         !GearyApplication.instance.controller.any_inline_composers()) {
+    //         set_cursor(new Gtk.TreePath.from_indices(0, -1), null, false);
+    //     }
+    // }
+
+    // private void on_seed_completed() {
+    //     if (!GearyApplication.instance.config.autoselect) {
+    //         // Notify that no conversations will be selected
+    //         conversations_selected(this.selected.read_only_view);
+    //     }
+    // }
 
     private void on_conversations_added(bool start) {
         Gtk.Adjustment? adjustment = get_adjustment();


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