[geary/wip/controller-cleanup: 1/3] Fix some circular references on application startup



commit 4b811d67ba8436ae4cc162316fb76a534200807f
Author: Michael Gratton <mike vee net>
Date:   Sun Apr 21 13:48:36 2019 +1000

    Fix some circular references on application startup
    
    Since Applicaton.Controller constructs a MainWindow instance when it
    is constructed, but the MainWindow and some other related classes
    accesses the controller via the deprecated GearyApplication singleton
    when they are cunstructed, the controller instance is still null and
    a critical is reported instead.
    
    Most of the code in question was related to classes needing to
    know when the controller's account/folder/conversation monitor has
    changed, so invert the call chain and have the controller let these
    classes know.
    
    The main culprit was the current conversation monitor, but since that
    is very MainWindow-specific, just move it to the main window from the
    controller, so the controller only needs to let the main window know
    that the selected folder has changed.

 src/client/application/application-controller.vala | 189 ++++---------------
 src/client/components/main-window.vala             | 205 +++++++++++++++++----
 src/client/components/search-bar.vala              |  36 ++--
 .../conversation-list/conversation-list-view.vala  |  49 +++--
 4 files changed, 237 insertions(+), 242 deletions(-)
---
diff --git a/src/client/application/application-controller.vala 
b/src/client/application/application-controller.vala
index c0d99449..c73c6b9e 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -40,11 +40,8 @@ public class Application.Controller : Geary.BaseObject {
     public const string ACTION_TOGGLE_FIND = "toggle-find";
 
     // Properties
-    public const string PROP_CURRENT_CONVERSATION ="current-conversations";
     public const string PROP_SELECTED_CONVERSATIONS ="selected-conversations";
 
-    public const int MIN_CONVERSATION_COUNT = 50;
-
     private const int SELECT_FOLDER_TIMEOUT_USEC = 100 * 1000;
 
     private const string PROP_ATTEMPT_OPEN_ACCOUNT = "attempt-open-account";
@@ -144,11 +141,6 @@ public class Application.Controller : Geary.BaseObject {
     /** Default main window */
     public MainWindow main_window { get; private set; }
 
-    /** Conversations for the current folder, null if none selected */
-    public Geary.App.ConversationMonitor? current_conversations {
-        get; private set; default = null;
-    }
-
     // Primary collection of the application's open accounts
     private Gee.Map<Geary.AccountInformation,AccountContext> accounts =
         new Gee.HashMap<Geary.AccountInformation,AccountContext>();
@@ -218,21 +210,6 @@ public class Application.Controller : Geary.BaseObject {
         {ACTION_ZOOM,  on_zoom,  "s"  },
     };
 
-    /**
-     * Fired when the currently selected account has changed.
-     */
-    public signal void account_selected(Geary.Account? account);
-
-    /**
-     * Fired when the currently selected folder has changed.
-     */
-    public signal void folder_selected(Geary.Folder? folder);
-
-    /**
-     * Fired when the number of conversations changes.
-     */
-    public signal void conversation_count_changed(int count);
-
     /**
      * Fired when the search text is changed according to the controller.  This accounts
      * for a brief typmatic delay.
@@ -311,7 +288,6 @@ public class Application.Controller : Geary.BaseObject {
         // Connect to various UI signals.
         main_window.conversation_list_view.conversations_selected.connect(on_conversations_selected);
         main_window.conversation_list_view.conversation_activated.connect(on_conversation_activated);
-        main_window.conversation_list_view.load_more.connect(on_load_more);
         main_window.conversation_list_view.mark_conversations.connect(on_mark_conversations);
         
main_window.conversation_list_view.visible_conversations_changed.connect(on_visible_conversations_changed);
         main_window.folder_list.folder_selected.connect(on_folder_selected);
@@ -328,7 +304,9 @@ public class Application.Controller : Geary.BaseObject {
             this.get_contact_store_for_account,
             this.should_notify_new_messages
         );
-        main_window.folder_list.set_new_messages_monitor(this.new_messages_monitor);
+        this.main_window.folder_list.set_new_messages_monitor(
+            this.new_messages_monitor
+        );
 
         // New messages indicator (Ubuntuism)
         this.new_messages_indicator = NewMessagesIndicator.create(
@@ -404,7 +382,7 @@ public class Application.Controller : Geary.BaseObject {
             );
             yield this.account_manager.load_accounts(cancellable);
             if (engine.get_accounts().size == 0) {
-                this.application.show_accounts();
+                yield this.application.show_accounts();
                 if (engine.get_accounts().size == 0) {
                     // User cancelled without creating an account, so
                     // nothing else to do but exit.
@@ -435,7 +413,6 @@ public class Application.Controller : Geary.BaseObject {
         // Disconnect from various UI signals.
         this.main_window.conversation_list_view.conversations_selected.disconnect(on_conversations_selected);
         this.main_window.conversation_list_view.conversation_activated.disconnect(on_conversation_activated);
-        this.main_window.conversation_list_view.load_more.disconnect(on_load_more);
         this.main_window.conversation_list_view.mark_conversations.disconnect(on_mark_conversations);
         
this.main_window.conversation_list_view.visible_conversations_changed.disconnect(on_visible_conversations_changed);
         this.main_window.folder_list.folder_selected.disconnect(on_folder_selected);
@@ -459,23 +436,6 @@ public class Application.Controller : Geary.BaseObject {
 
         this.cancellable_open_account.cancel();
 
-        // 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
-                );
-            }
-
-            this.current_conversations = null;
-        }
-
         // Create an array of known accounts so the loops below do not
         // explode if accounts are removed while iterating.
         AccountContext[] accounts = this.accounts.values.to_array();
@@ -583,6 +543,31 @@ public class Application.Controller : Geary.BaseObject {
         this.composer_widgets.add(widget);
     }
 
+    /** Displays a problem report when an error has been encountered. */
+    public void report_problem(Geary.ProblemReport report) {
+        debug("Problem reported: %s", report.to_string());
+
+        if (report.error == null ||
+            !(report.error.thrown is IOError.CANCELLED)) {
+            MainWindowInfoBar info_bar = new MainWindowInfoBar.for_problem(report);
+            info_bar.retry.connect(on_retry_problem);
+            this.main_window.show_infobar(info_bar);
+        }
+
+        Geary.ServiceProblemReport? service_report =
+            report as Geary.ServiceProblemReport;
+        if (service_report != null && service_report.service.protocol == SMTP) {
+            this.notifications.set_error_notification(
+                /// Notification title.
+                _("A problem occurred sending email for %s").printf(
+                    service_report.account.display_name
+                ),
+                /// Notification body
+                _("Email will not be sent until re-connected")
+            );
+        }
+    }
+
     /** Expunges removed accounts while the controller remains open. */
     internal async void expunge_accounts() {
         try {
@@ -671,30 +656,6 @@ public class Application.Controller : Geary.BaseObject {
         }
     }
 
-    private void report_problem(Geary.ProblemReport report) {
-        debug("Problem reported: %s", report.to_string());
-
-        if (report.error == null ||
-            !(report.error.thrown is IOError.CANCELLED)) {
-            MainWindowInfoBar info_bar = new MainWindowInfoBar.for_problem(report);
-            info_bar.retry.connect(on_retry_problem);
-            this.main_window.show_infobar(info_bar);
-        }
-
-        Geary.ServiceProblemReport? service_report =
-            report as Geary.ServiceProblemReport;
-        if (service_report != null && service_report.service.protocol == SMTP) {
-            this.notifications.set_error_notification(
-                /// Notification title.
-                _("A problem occurred sending email for %s").printf(
-                    service_report.account.display_name
-                ),
-                /// Notification body
-                _("Email will not be sent until re-connected")
-            );
-        }
-    }
-
     private void update_account_status() {
         // Start off assuming all accounts are online and error free
         // (i.e. no status issues to indicate) and proceed until
@@ -1097,7 +1058,7 @@ public class Application.Controller : Geary.BaseObject {
             this.current_folder = null;
             main_window.conversation_list_view.set_model(null);
             main_window.main_toolbar.folder = null;
-            folder_selected(null);
+            this.main_window.folder_selected(null, null);
         } else if (folder != this.current_folder) {
             this.main_window.conversation_viewer.show_loading();
             get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(false);
@@ -1149,12 +1110,6 @@ public class Application.Controller : Geary.BaseObject {
         // 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);
@@ -1165,7 +1120,7 @@ public class Application.Controller : Geary.BaseObject {
 
         if (this.current_account != folder.account) {
             this.current_account = folder.account;
-            account_selected(this.current_account);
+            this.main_window.search_bar.set_account(this.current_account);
 
             // If we were waiting for an account to be selected before issuing mailtos, do that now.
             if (pending_mailtos.size > 0) {
@@ -1183,8 +1138,6 @@ public class Application.Controller : Geary.BaseObject {
             }
         }
 
-        folder_selected(current_folder);
-
         if (!(current_folder is Geary.SearchFolder))
             previous_non_search_folder = current_folder;
 
@@ -1194,66 +1147,15 @@ public class Application.Controller : Geary.BaseObject {
 
         update_ui();
 
-        current_conversations = new Geary.App.ConversationMonitor(
-            current_folder,
-            Geary.Folder.OpenFlags.NO_DELAY,
-            // Include fields for the conversation viewer as well so
-            // conversations can be displayed without having to go
-            // back to the db
-            ConversationListStore.REQUIRED_FIELDS |
-            ConversationListBox.REQUIRED_FIELDS |
-            ConversationEmail.REQUIRED_FOR_CONSTRUCT,
-            MIN_CONVERSATION_COUNT
-        );
-
-        current_conversations.scan_completed.connect(on_scan_completed);
-        current_conversations.scan_error.connect(on_scan_error);
-
-        current_conversations.scan_completed.connect(on_conversation_count_changed);
-        current_conversations.conversations_added.connect(on_conversation_count_changed);
-        current_conversations.conversations_removed.connect(on_conversation_count_changed);
+        this.main_window.folder_selected(folder, this.cancellable_folder);
 
         clear_new_messages("do_select_folder", null);
 
-        yield this.current_conversations.start_monitoring_async(
-            this.cancellable_folder
-        );
-
         select_folder_mutex.release(ref mutex_token);
 
         debug("Switched to %s", folder.to_string());
     }
 
-    private void on_conversation_count_changed() {
-        if (this.current_conversations != null) {
-            ConversationListView list = this.main_window.conversation_list_view;
-            ConversationViewer viewer = this.main_window.conversation_viewer;
-            int count = this.current_conversations.size;
-            if (count == 0) {
-                // Let the user know if there's no available conversations
-                if (this.current_folder is Geary.SearchFolder) {
-                    viewer.show_empty_search();
-                } else {
-                    viewer.show_empty_folder();
-                }
-                enable_message_buttons(false);
-            } else {
-                // When not doing autoselect, we never get
-                // conversations_selected firing from the convo list,
-                // so we need to stop the loading spinner here. Only
-                // do so if there isn't already a selection or a
-                // composer to avoid interrupting those.
-                if (!this.application.config.autoselect &&
-                    list.get_selection().count_selected_rows() == 0 &&
-                    !viewer.is_composer_visible) {
-                    viewer.show_none_selected();
-                    enable_message_buttons(false);
-                }
-            }
-            conversation_count_changed(count);
-        }
-    }
-
     private void on_indicator_activated_application(uint32 timestamp) {
         this.main_window.present();
     }
@@ -1268,11 +1170,6 @@ public class Application.Controller : Geary.BaseObject {
         main_window.folder_list.select_folder(folder);
     }
 
-    private void on_load_more() {
-        debug("on_load_more");
-        current_conversations.min_window_count += MIN_CONVERSATION_COUNT;
-    }
-
     private void on_select_folder_completed(Object? source, AsyncResult result) {
         try {
             do_select_folder.end(result);
@@ -3006,28 +2903,6 @@ public class Application.Controller : Geary.BaseObject {
         }
     }
 
-    private void on_scan_completed(Geary.App.ConversationMonitor monitor) {
-        // Done scanning.  Check if we have enough messages to fill
-        // the conversation list; if not, trigger a load_more();
-        if (this.main_window != null &&
-            this.main_window.is_visible() &&
-            !this.main_window.conversation_list_has_scrollbar() &&
-            monitor == this.current_conversations &&
-            monitor.can_load_more) {
-            debug("Not enough messages, loading more for folder %s",
-                  current_folder.to_string());
-            on_load_more();
-        }
-    }
-
-    private void on_scan_error(Geary.App.ConversationMonitor monitor, Error err) {
-        Geary.AccountInformation account =
-            monitor.base_folder.account.information;
-        report_problem(
-            new Geary.ServiceProblemReport(account, account.incoming, err)
-        );
-    }
-
     private void on_email_load_error(ConversationEmail view, GLib.Error err) {
         report_problem(
             new Geary.ServiceProblemReport(
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index 3369c075..3ba877e5 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -1,6 +1,6 @@
 /*
  * Copyright 2016 Software Freedom Conservancy Inc.
- * Copyright 2016 Michael Gratton <mike vee net>
+ * Copyright 2016, 2019 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later). See the COPYING file in this distribution.
@@ -12,6 +12,7 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
 
     private const int STATUS_BAR_HEIGHT = 18;
     private const int UPDATE_UI_INTERVAL = 60;
+    private const int MIN_CONVERSATION_COUNT = 50;
 
 
     public new GearyApplication application {
@@ -19,10 +20,15 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         set { base.set_application(value); }
     }
 
+    /** Currently selected folder, null if none selected */
     public Geary.Folder? current_folder { get; private set; default = null; }
 
+    /** Conversations for the current folder, null if none selected */
+    public Geary.App.ConversationMonitor? conversations {
+        get; private set; default = null;
+    }
+
     private Geary.AggregateProgressMonitor progress_monitor = new Geary.AggregateProgressMonitor();
-    private Geary.ProgressMonitor? folder_progress = null;
 
     // Used to save/load the window state between sessions.
     public int window_width { get; set; }
@@ -102,9 +108,6 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         load_config(application.config);
         restore_saved_window_state();
 
-        application.controller.notify[Application.Controller.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);
 
@@ -189,10 +192,8 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         // currently used, that is when a user clicks on a
         // notification for new mail in the current folder.
         show_folder(folder);
-        Geary.App.ConversationMonitor? conversations =
-            this.application.controller.current_conversations;
         Geary.App.Conversation? conversation =
-            conversations.get_by_email_identifier(id);
+            this.conversations.get_by_email_identifier(id);
         if (conversation != null) {
             this.conversation_list_view.select_conversation(conversation);
         }
@@ -337,6 +338,7 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
 
     private void setup_layout(Configuration config) {
         this.conversation_list_view = new ConversationListView(this);
+        this.conversation_list_view.load_more.connect(on_load_more);
 
         this.conversation_viewer = new ConversationViewer(
             this.application.config
@@ -459,6 +461,25 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         return base.key_release_event(event);
     }
 
+    public void folder_selected(Geary.Folder? folder,
+                                GLib.Cancellable? cancellable) {
+        if (this.current_folder != null) {
+            this.progress_monitor.remove(this.current_folder.opening_monitor);
+            this.current_folder.properties.notify.disconnect(update_headerbar);
+            close_conversation_monitor();
+        }
+
+        this.current_folder = folder;
+
+        if (folder != null) {
+            this.progress_monitor.add(folder.opening_monitor);
+            folder.properties.notify.connect(update_headerbar);
+            open_conversation_monitor.begin(cancellable);
+        }
+
+        update_headerbar();
+    }
+
     private void update_ui() {
         // Only update if we haven't done so within the last while
         int64 now = GLib.get_monotonic_time() / (1000 * 1000);
@@ -477,48 +498,128 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         }
     }
 
-    private void on_conversation_monitor_changed() {
-        ConversationListStore? old_model = this.conversation_list_view.get_model();
+    private async void open_conversation_monitor(GLib.Cancellable cancellable) {
+        this.conversations = new Geary.App.ConversationMonitor(
+            this.current_folder,
+            Geary.Folder.OpenFlags.NO_DELAY,
+            // Include fields for the conversation viewer as well so
+            // conversations can be displayed without having to go
+            // back to the db
+            ConversationListStore.REQUIRED_FIELDS |
+            ConversationListBox.REQUIRED_FIELDS |
+            ConversationEmail.REQUIRED_FOR_CONSTRUCT,
+            MIN_CONVERSATION_COUNT
+        );
+
+        this.conversations.scan_completed.connect(on_scan_completed);
+        this.conversations.scan_error.connect(on_scan_error);
+
+        this.conversations.scan_completed.connect(
+            on_conversation_count_changed
+        );
+        this.conversations.conversations_added.connect(
+            on_conversation_count_changed
+        );
+        this.conversations.conversations_removed.connect(
+            on_conversation_count_changed
+        );
+
+        ConversationListStore new_model = new ConversationListStore(
+            this.conversations
+        );
+        this.progress_monitor.add(new_model.preview_monitor);
+        this.progress_monitor.add(conversations.progress_monitor);
+        this.conversation_list_view.set_model(new_model);
+
+        // Work on a local copy since the main window's copy may
+        // change if a folder is selected while closing.
+        Geary.App.ConversationMonitor conversations = this.conversations;
+        conversations.start_monitoring_async.begin(
+            cancellable,
+            (obj, res) => {
+                try {
+                    conversations.start_monitoring_async.end(res);
+                } catch (Error err) {
+                    Geary.AccountInformation account =
+                        conversations.base_folder.account.information;
+                    this.application.controller.report_problem(
+                        new Geary.ServiceProblemReport(account, account.incoming, err)
+                    );
+                }
+            }
+        );
+    }
+
+    private void close_conversation_monitor() {
+        ConversationListStore? old_model =
+            this.conversation_list_view.get_model();
         if (old_model != null) {
             this.progress_monitor.remove(old_model.preview_monitor);
             this.progress_monitor.remove(old_model.conversations.progress_monitor);
         }
 
-        Geary.App.ConversationMonitor? conversations =
-            this.application.controller.current_conversations;
+        this.conversations.scan_completed.disconnect(on_scan_completed);
+        this.conversations.scan_error.disconnect(on_scan_error);
 
-        if (conversations != null) {
-            ConversationListStore new_model =
-                new ConversationListStore(conversations);
-            this.progress_monitor.add(new_model.preview_monitor);
-            this.progress_monitor.add(conversations.progress_monitor);
-            this.conversation_list_view.set_model(new_model);
-        }
+        this.conversations.scan_completed.disconnect(
+            on_conversation_count_changed
+        );
+        this.conversations.conversations_added.disconnect(
+            on_conversation_count_changed
+        );
+        this.conversations.conversations_removed.disconnect(
+            on_conversation_count_changed
+        );
+
+        // Work on a local copy since the main window's copy may
+        // change if a folder is selected while closing.
+        Geary.App.ConversationMonitor conversations = this.conversations;
+        conversations.stop_monitoring_async.begin(
+            null,
+            (obj, res) => {
+                try {
+                    conversations.stop_monitoring_async.end(res);
+                } catch (Error err) {
+                    warning(
+                        "Error closing conversation monitor %s: %s",
+                        this.conversations.base_folder.to_string(),
+                        err.message
+                    );
+                }
+            }
+        );
+
+        this.conversations = null;
     }
 
-    private void on_folder_selected(Geary.Folder? folder) {
-        if (this.folder_progress != null) {
-            this.progress_monitor.remove(this.folder_progress);
-            this.folder_progress = null;
+    private void load_more() {
+        if (this.conversations != null) {
+            this.conversations.min_window_count += MIN_CONVERSATION_COUNT;
         }
+    }
 
-        if (folder != null) {
-            this.folder_progress = folder.opening_monitor;
-            this.progress_monitor.add(this.folder_progress);
+    private void on_conversation_count_changed() {
+        if (this.conversations.size == 0) {
+            // Let the user know if there's no available conversations
+            if (this.current_folder is Geary.SearchFolder) {
+                this.conversation_viewer.show_empty_search();
+            } else {
+                this.conversation_viewer.show_empty_folder();
+            }
+            this.application.controller.enable_message_buttons(false);
+        } else {
+            // When not doing autoselect, we never get
+            // conversations_selected firing from the convo list, so
+            // we need to stop the loading spinner here. Only do so if
+            // there isn't already a selection or a composer to avoid
+            // interrupting those.
+            if (!this.application.config.autoselect &&
+                this.conversation_list_view.get_selection().count_selected_rows() == 0 &&
+                !this.conversation_viewer.is_composer_visible) {
+                this.conversation_viewer.show_none_selected();
+                this.application.controller.enable_message_buttons(false);
+            }
         }
-
-        // 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);
-
-        // swap it in
-        this.current_folder = folder;
-
-        update_headerbar();
     }
 
     private void on_account_available(Geary.AccountInformation account) {
@@ -642,6 +743,32 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
         return (SimpleAction) lookup_action(name);
     }
 
+    private void on_scan_completed(Geary.App.ConversationMonitor monitor) {
+        // Done scanning.  Check if we have enough messages to fill
+        // the conversation list; if not, trigger a load_more();
+        if (is_visible() &&
+            !conversation_list_has_scrollbar() &&
+            monitor == this.conversations &&
+            monitor.can_load_more) {
+            debug("Not enough messages, loading more for folder %s",
+                  this.current_folder.to_string());
+            load_more();
+        }
+    }
+
+    private void on_scan_error(Geary.App.ConversationMonitor monitor,
+                               GLib.Error err) {
+        Geary.AccountInformation account =
+            monitor.base_folder.account.information;
+        this.application.controller.report_problem(
+            new Geary.ServiceProblemReport(account, account.incoming, err)
+        );
+    }
+
+    private void on_load_more() {
+        load_more();
+    }
+
     [GtkCallback]
     private void on_map() {
         this.update_ui_timeout.start();
diff --git a/src/client/components/search-bar.vala b/src/client/components/search-bar.vala
index 40bb02f7..db1c7920 100644
--- a/src/client/components/search-bar.vala
+++ b/src/client/components/search-bar.vala
@@ -38,8 +38,6 @@ public class SearchBar : Gtk.SearchBar {
         add(search_entry);
 
         set_search_placeholder_text(DEFAULT_SEARCH_TEXT);
-
-        GearyApplication.instance.controller.account_selected.connect(on_account_changed);
     }
 
     public void set_search_text(string text) {
@@ -55,23 +53,7 @@ public class SearchBar : Gtk.SearchBar {
         search_entry.placeholder_text = placeholder;
     }
 
-    private void on_search_upgrade_start() {
-        // Set the progress bar's width to match the search entry's width.
-        int minimum_width = 0;
-        int natural_width = 0;
-        search_entry.get_preferred_width(out minimum_width, out natural_width);
-        search_upgrade_progress_bar.width_request = minimum_width;
-
-        search_entry.hide();
-        search_upgrade_progress_bar.show();
-    }
-
-    private void on_search_upgrade_finished() {
-        search_entry.show();
-        search_upgrade_progress_bar.hide();
-    }
-
-    private void on_account_changed(Geary.Account? account) {
+    public void set_account(Geary.Account? account) {
         on_search_upgrade_finished(); // Reset search box.
 
         if (search_upgrade_progress_monitor != null) {
@@ -106,6 +88,22 @@ public class SearchBar : Gtk.SearchBar {
         on_information_changed(); // Set new account name.
     }
 
+    private void on_search_upgrade_start() {
+        // Set the progress bar's width to match the search entry's width.
+        int minimum_width = 0;
+        int natural_width = 0;
+        search_entry.get_preferred_width(out minimum_width, out natural_width);
+        search_upgrade_progress_bar.width_request = minimum_width;
+
+        search_entry.hide();
+        search_upgrade_progress_bar.show();
+    }
+
+    private void on_search_upgrade_finished() {
+        search_entry.show();
+        search_upgrade_progress_bar.hide();
+    }
+
     private void on_information_changed() {
         set_search_placeholder_text(current_account == null ||
             GearyApplication.instance.controller.get_num_accounts() == 1 ? DEFAULT_SEARCH_TEXT :
diff --git a/src/client/conversation-list/conversation-list-view.vala 
b/src/client/conversation-list/conversation-list-view.vala
index 57390503..a4400248 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -13,7 +13,6 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
     private bool enable_load_more = true;
 
     private bool reset_adjustment = false;
-    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>();
@@ -59,8 +58,6 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
 
         GearyApplication.instance.config.settings.changed[Configuration.DISPLAY_PREVIEW_KEY].connect(
             on_display_preview_changed);
-        GearyApplication.instance.controller.notify[Application.Controller.PROP_CURRENT_CONVERSATION].
-            connect(on_conversation_monitor_changed);
 
         // Watch for mouse events.
         motion_notify_event.connect(on_motion_notify_event);
@@ -92,6 +89,9 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
     public new void set_model(ConversationListStore? new_store) {
         ConversationListStore? old_store = get_model();
         if (old_store != null) {
+            old_store.conversations.scan_started.disconnect(on_scan_started);
+            old_store.conversations.scan_completed.disconnect(on_scan_completed);
+
             old_store.conversations_added.disconnect(on_conversations_added);
             old_store.conversations_removed.disconnect(on_conversations_removed);
             old_store.row_inserted.disconnect(on_rows_changed);
@@ -102,6 +102,9 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
         }
 
         if (new_store != null) {
+            new_store.conversations.scan_started.connect(on_scan_started);
+            new_store.conversations.scan_completed.connect(on_scan_completed);
+
             new_store.row_inserted.connect(on_rows_changed);
             new_store.rows_reordered.connect(on_rows_changed);
             new_store.row_changed.connect(on_rows_changed);
@@ -136,31 +139,23 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
     }
 
     private void check_load_more() {
-        // Check if we're at the very bottom of the list. If we are,
-        // it's time to issue a load_more signal.
-        Gtk.Adjustment adjustment = ((Gtk.Scrollable) this).get_vadjustment();
-        double upper = adjustment.get_upper();
-        double threshold = upper - adjustment.page_size - LOAD_MORE_HEIGHT;
-        if (this.is_visible() &&
-            this.conversation_monitor.can_load_more &&
-            adjustment.get_value() >= threshold) {
-            load_more();
-        }
-
-        schedule_visible_conversations_changed();
-    }
-
-    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 = GearyApplication.instance.controller.current_conversations;
+        ConversationListStore? model = get_model();
+        Geary.App.ConversationMonitor? conversations = (model != null)
+            ? model.conversations
+            : null;
+        if (conversations != null) {
+            // Check if we're at the very bottom of the list. If we
+            // are, it's time to issue a load_more signal.
+            Gtk.Adjustment adjustment = ((Gtk.Scrollable) this).get_vadjustment();
+            double upper = adjustment.get_upper();
+            double threshold = upper - adjustment.page_size - LOAD_MORE_HEIGHT;
+            if (this.is_visible() &&
+                conversations.can_load_more &&
+                adjustment.get_value() >= threshold) {
+                load_more();
+            }
 
-        if (conversation_monitor != null) {
-            conversation_monitor.scan_started.connect(on_scan_started);
-            conversation_monitor.scan_completed.connect(on_scan_completed);
+            schedule_visible_conversations_changed();
         }
     }
 


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