[geary/wip/713190-resort-2] Clean up of ConversationListStore, improved lookup performance



commit 1e5dba3565a9427faca63270132ad5118d46d8e2
Author: Jim Nelson <jim yorba org>
Date:   Wed Jan 21 16:09:14 2015 -0800

    Clean up of ConversationListStore, improved lookup performance

 src/client/application/geary-controller.vala       |    5 -
 .../conversation-list/conversation-list-store.vala |  109 ++++++++++++++------
 2 files changed, 75 insertions(+), 39 deletions(-)
---
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index d451af3..0892e13 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -1117,8 +1117,6 @@ public class GearyController : Geary.BaseObject {
         account.sending_monitor.start.disconnect(on_sending_started);
         account.sending_monitor.finish.disconnect(on_sending_finished);
         
-        if (main_window.conversation_list_store.account_owner_email == account.information.email)
-            main_window.conversation_list_store.account_owner_email = null;
         main_window.folder_list.remove_account(account);
         
         if (inboxes.has_key(account)) {
@@ -1310,9 +1308,6 @@ public class GearyController : Geary.BaseObject {
         if (!(current_folder is Geary.SearchFolder))
             previous_non_search_folder = current_folder;
         
-        main_window.conversation_list_store.set_current_folder(current_folder, conversation_cancellable);
-        main_window.conversation_list_store.account_owner_email = current_account.information.email;
-        
         main_window.main_toolbar.copy_folder_menu.clear();
         main_window.main_toolbar.move_folder_menu.clear();
         foreach(Geary.Folder f in current_folder.account.list_folders()) {
diff --git a/src/client/conversation-list/conversation-list-store.vala 
b/src/client/conversation-list/conversation-list-store.vala
index b35c0dd..31b5050 100644
--- a/src/client/conversation-list/conversation-list-store.vala
+++ b/src/client/conversation-list/conversation-list-store.vala
@@ -22,12 +22,14 @@ public class ConversationListStore : Gtk.ListStore {
     
     public enum Column {
         CONVERSATION_DATA,
-        CONVERSATION_OBJECT;
+        CONVERSATION_OBJECT,
+        ROW_WRAPPER;
         
         public static Type[] get_types() {
             return {
                 typeof (FormattedConversationData), // CONVERSATION_DATA
-                typeof (Geary.App.Conversation)     // CONVERSATION_OBJECT
+                typeof (Geary.App.Conversation),    // CONVERSATION_OBJECT
+                typeof (RowWrapper)                 // ROW_WRAPPER
             };
         }
         
@@ -35,23 +37,49 @@ public class ConversationListStore : Gtk.ListStore {
             switch (this) {
                 case CONVERSATION_DATA:
                     return "data";
+                
                 case CONVERSATION_OBJECT:
                     return "envelope";
                 
+                case ROW_WRAPPER:
+                    return "wrapper";
+                
                 default:
                     assert_not_reached();
             }
         }
     }
     
-    public string? account_owner_email { get; set; default = null; }
+    private class RowWrapper : Geary.BaseObject {
+        public Geary.App.Conversation conversation;
+        public Gtk.TreeRowReference row;
+        
+        public RowWrapper(Gtk.TreeModel model, Geary.App.Conversation conversation, Gtk.TreePath path) {
+            this.conversation = conversation;
+            this.row = new Gtk.TreeRowReference(model, path);
+        }
+        
+        public Gtk.TreePath get_path() {
+            return row.get_path();
+        }
+        
+        public Gtk.TreeIter get_iter() {
+            Gtk.TreeIter iter;
+            bool valid = row.get_model().get_iter(out iter, get_path());
+            assert(valid);
+            
+            return iter;
+        }
+    }
+    
     public Geary.ProgressMonitor preview_monitor { get; private set; default = 
         new Geary.SimpleProgressMonitor(Geary.ProgressType.ACTIVITY); }
     
     private Geary.App.ConversationMonitor conversation_monitor;
-    private Geary.Folder? current_folder = null;
+    private Gee.HashMap<Geary.App.Conversation, RowWrapper> row_map = new Gee.HashMap<
+        Geary.App.Conversation, RowWrapper>();
     private Geary.App.EmailStore? email_store = null;
-    private Cancellable? cancellable_folder = null;
+    private Cancellable cancellable = new Cancellable();
     private bool loading_local_only = true;
     private Geary.Nonblocking.Mutex refresh_mutex = new Geary.Nonblocking.Mutex();
     private uint update_id = 0;
@@ -76,6 +104,9 @@ public class ConversationListStore : Gtk.ListStore {
     ~ConversationListStore() {
         if (update_id != 0)
             Source.remove(update_id);
+        
+        GearyApplication.instance.controller.notify[GearyController.PROP_CURRENT_CONVERSATION].
+            disconnect(on_conversation_monitor_changed);
     }
     
     private void on_conversation_monitor_changed() {
@@ -88,10 +119,17 @@ public class ConversationListStore : Gtk.ListStore {
             conversation_monitor.email_flags_changed.disconnect(on_email_flags_changed);
         }
         
+        cancellable.cancel();
+        cancellable = new Cancellable();
+        
         clear();
+        row_map.clear();
         conversation_monitor = GearyApplication.instance.controller.current_conversations;
+        email_store = null;
         
         if (conversation_monitor != null) {
+            email_store = new Geary.App.EmailStore(conversation_monitor.folder.account);
+            
             // add all existing conversations
             on_conversations_added(conversation_monitor.get_conversations());
             
@@ -104,12 +142,6 @@ public class ConversationListStore : Gtk.ListStore {
         }
     }
     
-    public void set_current_folder(Geary.Folder? current_folder, Cancellable? cancellable_folder) {
-        this.current_folder = current_folder;
-        this.cancellable_folder = cancellable_folder;
-        email_store = (current_folder == null ? null : new Geary.App.EmailStore(current_folder.account));
-    }
-    
     public Geary.App.Conversation? get_conversation_at_path(Gtk.TreePath path) {
         Gtk.TreeIter iter;
         if (!get_iter(out iter, path))
@@ -118,14 +150,6 @@ public class ConversationListStore : Gtk.ListStore {
         return get_conversation_at_iter(iter);
     }
     
-    public Gtk.TreePath? get_path_for_conversation(Geary.App.Conversation conversation) {
-        Gtk.TreeIter iter;
-        if (!get_iter_for_conversation(conversation, out iter))
-            return null;
-        
-        return get_path(iter);
-    }
-    
     private async void refresh_previews_async(Geary.App.ConversationMonitor conversation_monitor) {
         // Use a mutex because it's possible for the conversation monitor to fire multiple
         // "scan-started" signals as messages come in fast and furious, but only want to process
@@ -155,7 +179,7 @@ public class ConversationListStore : Gtk.ListStore {
     
     // should only be called by refresh_previews_async()
     private async void do_refresh_previews_async(Geary.App.ConversationMonitor conversation_monitor) {
-        if (current_folder == null || !GearyApplication.instance.config.display_preview)
+        if (conversation_monitor == null || !GearyApplication.instance.config.display_preview)
             return;
         
         Gee.Set<Geary.EmailIdentifier> needing_previews = get_emails_needing_previews();
@@ -166,7 +190,7 @@ public class ConversationListStore : Gtk.ListStore {
         if (emails.size < 1)
             return;
         
-        debug("Displaying %d previews for %s...", emails.size, current_folder.to_string());
+        debug("Displaying %d previews for %s...", emails.size, conversation_monitor.folder.to_string());
         foreach (Geary.Email email in emails) {
             Geary.App.Conversation? conversation = conversation_monitor.get_conversation_for_email(email.id);
             if (conversation != null)
@@ -174,7 +198,7 @@ public class ConversationListStore : Gtk.ListStore {
             else
                 debug("Couldn't find conversation for %s", email.id.to_string());
         }
-        debug("Displayed %d previews for %s", emails.size, current_folder.to_string());
+        debug("Displayed %d previews for %s", emails.size, conversation_monitor.folder.to_string());
     }
     
     private async Gee.Collection<Geary.Email> do_get_previews_async(
@@ -185,7 +209,7 @@ public class ConversationListStore : Gtk.ListStore {
         try {
             debug("Loading %d previews...", emails_needing_previews.size);
             emails = yield email_store.list_email_by_sparse_id_async(emails_needing_previews,
-                ConversationListStore.WITH_PREVIEW_FIELDS, flags, cancellable_folder);
+                ConversationListStore.WITH_PREVIEW_FIELDS, flags, cancellable);
             debug("Loaded %d previews...", emails_needing_previews.size);
         } catch (Error err) {
             // Ignore NOT_FOUND, as that's entirely possible when waiting for the remote to open
@@ -260,10 +284,19 @@ public class ConversationListStore : Gtk.ListStore {
     
     private void set_row(Gtk.TreeIter iter, Geary.App.Conversation conversation, Geary.Email preview) {
         FormattedConversationData conversation_data = new FormattedConversationData(conversation,
-            preview, current_folder, account_owner_email);
+            preview, conversation_monitor.folder, conversation_monitor.folder.account.information.email);
+        
+        Gtk.TreePath? path = get_path(iter);
+        assert(path != null);
+        RowWrapper wrapper = new RowWrapper(this, conversation, path);
+        
         set(iter,
             Column.CONVERSATION_DATA, conversation_data,
-            Column.CONVERSATION_OBJECT, conversation);
+            Column.CONVERSATION_OBJECT, conversation,
+            Column.ROW_WRAPPER, wrapper
+        );
+        
+        row_map.set(conversation, wrapper);
     }
     
     private void refresh_conversation(Geary.App.Conversation conversation) {
@@ -318,20 +351,26 @@ public class ConversationListStore : Gtk.ListStore {
             row_changed(path, iter);
     }
     
-    private bool get_iter_for_conversation(Geary.App.Conversation conversation, out Gtk.TreeIter iter) {
-        if (!get_iter_first(out iter))
-            return false;
+    public Gtk.TreePath? get_path_for_conversation(Geary.App.Conversation conversation) {
+        RowWrapper? wrapper = row_map.get(conversation);
         
-        do {
-            if (get_conversation_at_iter(iter) == conversation)
-                return true;
-        } while (iter_next(ref iter));
+        return (wrapper != null) ? wrapper.get_path() : null;
+    }
+    
+    private bool get_iter_for_conversation(Geary.App.Conversation conversation, out Gtk.TreeIter iter) {
+        // use get_iter_first() because boxing Gtk.TreeIter with a nullable is problematic with
+        // current bindings
+        RowWrapper? wrapper = row_map.get(conversation);
+        if (wrapper != null)
+            iter = wrapper.get_iter();
+        else
+            get_iter_first(out iter);
         
-        return false;
+        return wrapper != null;
     }
     
     private bool has_conversation(Geary.App.Conversation conversation) {
-        return get_iter_for_conversation(conversation, null);
+        return row_map.has_key(conversation);
     }
     
     private Geary.App.Conversation? get_conversation_at_iter(Gtk.TreeIter iter) {
@@ -352,6 +391,8 @@ public class ConversationListStore : Gtk.ListStore {
         Gtk.TreeIter iter;
         if (get_iter_for_conversation(conversation, out iter))
             remove(iter);
+        
+        row_map.remove(conversation);
     }
     
     private bool add_conversation(Geary.App.Conversation conversation) {


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