[geary/wip/714802-chron] Cleanup, candidate fix



commit b1f211fe86d3e1b6e79b66a83f6baaaf0ea9bde3
Author: Jim Nelson <jim yorba org>
Date:   Mon Jan 26 16:24:36 2015 -0800

    Cleanup, candidate fix

 src/client/application/geary-controller.vala       |   12 +-
 .../conversation-list/conversation-list-store.vala |    2 +-
 .../conversation-list/conversation-list-view.vala  |   14 ++--
 src/engine/app/app-conversation-monitor.vala       |  112 ++++++++++++--------
 .../app-conversation-operation-queue.vala          |    9 ++
 src/engine/imap-db/imap-db-folder.vala             |    8 ++-
 6 files changed, 98 insertions(+), 59 deletions(-)
---
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 654857d..3375f7e 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -55,6 +55,7 @@ public class GearyController : Geary.BaseObject {
     public const string PROP_CURRENT_CONVERSATION ="current-conversations";
     
     public const int MIN_CONVERSATION_COUNT = 50;
+    public const int LOAD_MORE_CONVERSATION_COUNT = 50;
     
     private const string DELETE_MESSAGE_TOOLTIP_SINGLE = _("Delete conversation (Shift+Delete)");
     private const string DELETE_MESSAGE_TOOLTIP_MULTIPLE = _("Delete conversations (Shift+Delete)");
@@ -1394,12 +1395,11 @@ public class GearyController : Geary.BaseObject {
     }
     
     private void on_load_more() {
-        if (!current_conversations.all_messages_loaded)
-            current_conversations.min_window_count += MIN_CONVERSATION_COUNT;
-        
-        debug("on_load_more: all_messages_loaded=%s min_window_count=%d",
-            current_conversations.all_messages_loaded.to_string(),
-            current_conversations.min_window_count);
+        if (current_conversations.increase_window(LOAD_MORE_CONVERSATION_COUNT)) {
+            debug("Loading more messages for conversations: all_messages_loaded=%s window_count=%d",
+                current_conversations.all_messages_loaded.to_string(),
+                current_conversations.window_count);
+        }
     }
     
     private void on_select_folder_completed(Object? source, AsyncResult result) {
diff --git a/src/client/conversation-list/conversation-list-store.vala 
b/src/client/conversation-list/conversation-list-store.vala
index 179937b..f4d21e0 100644
--- a/src/client/conversation-list/conversation-list-store.vala
+++ b/src/client/conversation-list/conversation-list-store.vala
@@ -372,7 +372,7 @@ public class ConversationListStore : Gtk.ListStore {
         return true;
     }
     
-    private void on_scan_completed(Geary.App.ConversationMonitor sender) {
+    private void on_scan_completed(Geary.App.ConversationMonitor sender, bool local_only) {
         refresh_previews_async.begin(sender);
         loading_local_only = false;
     }
diff --git a/src/client/conversation-list/conversation-list-view.vala 
b/src/client/conversation-list/conversation-list-view.vala
index d7c5b64..8fa3e80 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -98,26 +98,26 @@ public class ConversationListView : Gtk.TreeView {
         }
     }
     
-    private void on_scan_started() {
+    private void on_scan_started(bool local_only) {
         enable_load_more = false;
     }
     
-    private void on_scan_completed() {
-        debug("ON SCAN COMPLETED");
-        
+    private void on_scan_completed(bool local_only) {
         enable_load_more = true;
         
         // Select first conversation.
         if (GearyApplication.instance.config.autoselect)
             select_first_conversation();
         
+        // don't load more if a local-only scan, a full scan will follow
+        if (local_only)
+            return;
+        
         // if no scrollbars, load more ... need to do this until scrollbars show in order to allow
         // the user a way to manually load more when they're ready
         Gtk.Adjustment vadj = ((Gtk.Scrollable) this).get_vadjustment();
-        if (vadj.upper <= vadj.page_size) {
-            debug("NO SCROLLBAR, LOADING MORE");
+        if (vadj.upper <= vadj.page_size)
             load_more();
-        }
     }
     
     private void on_conversation_removed(Geary.App.Conversation conversation) {
diff --git a/src/engine/app/app-conversation-monitor.vala b/src/engine/app/app-conversation-monitor.vala
index d4f8eed..17d51ca 100644
--- a/src/engine/app/app-conversation-monitor.vala
+++ b/src/engine/app/app-conversation-monitor.vala
@@ -22,9 +22,11 @@ public class Geary.App.ConversationMonitor : BaseObject {
             = new Gee.HashMap<Geary.EmailIdentifier, Geary.Email>();
         
         public bool inside_scan;
+        public Folder.ListFlags flags;
         
-        public ProcessJobContext(bool inside_scan) {
+        public ProcessJobContext(bool inside_scan, Folder.ListFlags flags) {
             this.inside_scan = inside_scan;
+            this.flags = flags;
         }
     }
     
@@ -32,15 +34,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
     public bool reestablish_connections { get; set; default = true; }
     public bool is_monitoring { get; private set; default = false; }
     
-    /**
-     * TODO: Only allow increasing window count if FillWindowOperation is not outstanding.
-     */
-    public int min_window_count { get { return _min_window_count; }
-        set {
-            _min_window_count = value;
-            operation_queue.add(new FillWindowOperation(this, false));
-        }
-    }
+    public int window_count { get; private set; }
     
     /**
      * Indicates no more messages can be loaded by increasing the min_window_count.
@@ -54,7 +48,6 @@ public class Geary.App.ConversationMonitor : BaseObject {
     private Geary.Folder.OpenFlags open_flags;
     private Cancellable? cancellable_monitor = null;
     private bool reseed_notified = false;
-    private int _min_window_count = 0;
     private ConversationOperationQueue operation_queue = new ConversationOperationQueue();
     
     /**
@@ -85,10 +78,15 @@ public class Geary.App.ConversationMonitor : BaseObject {
      * asynchronous.  "scan-started", "scan-error", and "scan-completed" will be fired (as
      * appropriate) for each individual load request; that is, there is no internal counter to ensure
      * only a single "scan-completed" is fired to indicate multiple loads have finished.
+     *
+     * local_only is true when the { link ConversationMonitor}'s scan was of the local database
+     * only in order to quickly get conversations loaded while the { link Folder} is opening.
+     * Another { link scan_started} and { link scan_completed} should execute once the Folder's
+     * remote is opened.
      */
-    public virtual signal void scan_started() {
-        Logging.debug(Logging.Flag.CONVERSATIONS, "[%s] ConversationMonitor::scan_started",
-            folder.to_string());
+    public virtual signal void scan_started(bool local_only) {
+        Logging.debug(Logging.Flag.CONVERSATIONS, "[%s] ConversationMonitor::scan_started local_only=%s",
+            folder.to_string(), local_only.to_string());
     }
     
     /**
@@ -102,10 +100,15 @@ public class Geary.App.ConversationMonitor : BaseObject {
     
     /**
      * "scan-completed" is fired when the scan of the email has finished.
+     *
+     * local_only is true when the { link ConversationMonitor}'s scan was of the local database
+     * only in order to quickly get conversations loaded while the { link Folder} is opening.
+     * Another { link scan_started} and { link scan_completed} should execute once the Folder's
+     * remote is opened.
      */
-    public virtual signal void scan_completed() {
-        Logging.debug(Logging.Flag.CONVERSATIONS, "[%s] ConversationMonitor::scan_completed",
-            folder.to_string());
+    public virtual signal void scan_completed(bool local_only) {
+        Logging.debug(Logging.Flag.CONVERSATIONS, "[%s] ConversationMonitor::scan_completed local_only=%s",
+            folder.to_string(), local_only.to_string());
     }
     
     /**
@@ -189,11 +192,11 @@ public class Geary.App.ConversationMonitor : BaseObject {
      * @param min_window_count Minimum number of conversations that will be loaded
      */
     public ConversationMonitor(Geary.Folder folder, Geary.Folder.OpenFlags open_flags,
-        Geary.Email.Field required_fields, int min_window_count) {
+        Geary.Email.Field required_fields, int initial_window_count) {
         this.folder = folder;
         this.open_flags = open_flags;
         this.required_fields = required_fields | REQUIRED_FIELDS;
-        _min_window_count = min_window_count;
+        window_count = initial_window_count;
     }
     
     ~ConversationMonitor() {
@@ -212,16 +215,16 @@ public class Geary.App.ConversationMonitor : BaseObject {
         monitoring_stopped(retrying);
     }
     
-    protected virtual void notify_scan_started() {
-        scan_started();
+    protected virtual void notify_scan_started(bool local_only) {
+        scan_started(local_only);
     }
     
     protected virtual void notify_scan_error(Error err) {
         scan_error(err);
     }
     
-    protected virtual void notify_scan_completed() {
-        scan_completed();
+    protected virtual void notify_scan_completed(bool local_only) {
+        scan_completed(local_only);
     }
     
     protected virtual void notify_seed_completed() {
@@ -324,15 +327,17 @@ public class Geary.App.ConversationMonitor : BaseObject {
     internal async void local_load_async() {
         debug("ConversationMonitor seeding with local email for %s", folder.to_string());
         
-        int count = min_window_count;
+        // Since it's possible the chronologically newest email in the Folder is not at the end of
+        // the vector, find the chronologically newest email's offset and load from it to the end
+        // of the vector; that ensures that all messages are loaded in the span of time, to avoid
+        // fragmented conversations
+        int count = window_count;
         try {
             int offset_from_top;
             bool found = yield folder.fetch_local_newest_async(null, null, out offset_from_top,
                 cancellable_monitor);
-            if (found && (offset_from_top + 1) > count) {
+            if (found && (offset_from_top + 1) > count)
                 count = offset_from_top + 1;
-                debug("SEEDING %d TO GET NEWEST EMAIL", count);
-            }
         } catch (Error err) {
             debug("Error fetching local newest email: %s", err.message);
         }
@@ -342,6 +347,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
         } catch (Error e) {
             debug("Error loading local messages: %s", e.message);
         }
+        
         debug("ConversationMonitor seeded for %s", folder.to_string());
     }
     
@@ -390,31 +396,48 @@ public class Geary.App.ConversationMonitor : BaseObject {
     }
     
     /**
+     * Instructs the { link ConversationMonitor} to increase the { link window_size} by the supplied
+     * value, in an attempt to generate more conversations by loading more messages.
+     *
+     * Returns false if the ConversationMonitor is in the process of increasing the window already
+     * or all messages in the { link Folder} have been loaded.
+     */
+    public bool increase_window(int increase) {
+        if (operation_queue.has_fill_window || all_messages_loaded)
+            return false;
+        
+        window_count += increase;
+        operation_queue.add(new FillWindowOperation(this, false));
+        
+        return true;
+    }
+    
+    /**
      * See Geary.Folder.list_email_by_id_async() for details of how these parameters operate.  Instead
      * of returning emails, this method will load the Conversations object with them sorted into
      * Conversation objects.
      */
     private async void load_by_id_async(Geary.EmailIdentifier? initial_id, int count,
         Geary.Folder.ListFlags flags, Cancellable? cancellable) throws Error {
-        notify_scan_started();
+        notify_scan_started(flags.is_local_only());
         try {
             yield process_email_async(yield folder.list_email_by_id_async(initial_id,
-                count, required_fields, flags, cancellable), new ProcessJobContext(true));
+                count, required_fields, flags, cancellable), new ProcessJobContext(true, flags));
         } catch (Error err) {
-            list_error(err);
+            list_error(flags, err);
             throw err;
         }
     }
     
     private async void load_by_sparse_id(Gee.Collection<Geary.EmailIdentifier> ids,
         Geary.Folder.ListFlags flags, Cancellable? cancellable) {
-        notify_scan_started();
+        notify_scan_started(flags.is_local_only());
         
         try {
             yield process_email_async(yield folder.list_email_by_sparse_id_async(ids,
-                required_fields, flags, cancellable), new ProcessJobContext(true));
+                required_fields, flags, cancellable), new ProcessJobContext(true, flags));
         } catch (Error err) {
-            list_error(err);
+            list_error(flags, err);
         }
     }
     
@@ -465,7 +488,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
             
             debug("Fetched %d relevant emails locally", search_emails.size);
             
-            yield process_email_async(search_emails, new ProcessJobContext(false));
+            yield process_email_async(search_emails, new ProcessJobContext(false, flags));
         } catch (Error e) {
             debug("Error loading external emails: %s", e.message);
             if (opened) {
@@ -478,10 +501,10 @@ public class Geary.App.ConversationMonitor : BaseObject {
         }
     }
     
-    private void list_error(Error err) {
+    private void list_error(Folder.ListFlags flags, Error err) {
         debug("Error while assembling conversations in %s: %s", folder.to_string(), err.message);
         notify_scan_error(err);
-        notify_scan_completed();
+        notify_scan_completed(flags.is_local_only());
     }
     
     private async void process_email_async(Gee.Collection<Geary.Email>? emails, ProcessJobContext job) {
@@ -629,7 +652,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
         }
         
         if (job.inside_scan)
-            notify_scan_completed();
+            notify_scan_completed(job.flags.is_local_only());
     }
     
     private void on_folder_email_appended(Gee.Collection<Geary.EmailIdentifier> appended_ids) {
@@ -680,7 +703,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
         Gee.HashSet<RFC822.MessageID> search_message_ids = new Gee.HashSet<RFC822.MessageID>();
         foreach (Conversation conversation in trimmed.get_keys())
             search_message_ids.add_all(conversation.get_message_ids());
-        yield expand_conversations_async(search_message_ids, new ProcessJobContext(false));
+        yield expand_conversations_async(search_message_ids,
+            new ProcessJobContext(false, Folder.ListFlags.NONE));
     }
     
     internal async void external_append_emails_async(Geary.Folder folder,
@@ -736,8 +760,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
                     cancellable_monitor);
             } else {
                 debug("ConversationMonitor (%s) reseeding latest %d emails on opened %s", why,
-                    min_window_count, folder.to_string());
-                yield load_by_id_async(null, min_window_count, Geary.Folder.ListFlags.NONE, 
cancellable_monitor);
+                    window_count, folder.to_string());
+                yield load_by_id_async(null, window_count, Geary.Folder.ListFlags.NONE, cancellable_monitor);
             }
         } catch (Error e) {
             debug("Reseed error: %s", e.message);
@@ -761,7 +785,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
      * Attempts to load enough conversations to fill min_window_count.
      */
     internal async void fill_window_async(bool is_insert) {
-        if (!is_monitoring || min_window_count <= conversations.size)
+        if (!is_monitoring || window_count <= conversations.size)
             return;
         
         int initial_message_count = conversations.get_email_count();
@@ -789,7 +813,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
         Geary.EmailIdentifier? low_id = yield get_lowest_email_id_async(null);
         if (low_id != null && !is_insert) {
             // Load at least as many messages as remianing conversations.
-            int num_to_load = min_window_count - conversations.size;
+            int num_to_load = window_count - conversations.size;
             if (num_to_load < WINDOW_FILL_MESSAGE_COUNT)
                 num_to_load = WINDOW_FILL_MESSAGE_COUNT;
             
@@ -802,13 +826,13 @@ public class Geary.App.ConversationMonitor : BaseObject {
             // No existing messages or an insert invalidated our existing list,
             // need to start from scratch.
             try {
-                yield load_by_id_async(null, min_window_count, flags, cancellable_monitor);
+                yield load_by_id_async(null, window_count, flags, cancellable_monitor);
             } catch(Error e) {
                 debug("Error filling conversation window: %s", e.message);
             }
         }
         
-        all_messages_loaded = conversations.get_email_count() == initial_message_count;
+        all_messages_loaded = conversations.get_email_count() == folder.properties.email_total;
         
         // Run again to make sure we're full unless we ran out of messages.
         if (conversations.get_email_count() != initial_message_count)
diff --git a/src/engine/app/conversation-monitor/app-conversation-operation-queue.vala 
b/src/engine/app/conversation-monitor/app-conversation-operation-queue.vala
index 7577e4f..a681f03 100644
--- a/src/engine/app/conversation-monitor/app-conversation-operation-queue.vala
+++ b/src/engine/app/conversation-monitor/app-conversation-operation-queue.vala
@@ -6,6 +6,9 @@
 
 private class Geary.App.ConversationOperationQueue : BaseObject {
     public bool is_processing { get; private set; default = false; }
+    
+    public bool has_fill_window { get; private set; default = false; }
+    
     public Geary.SimpleProgressMonitor progress_monitor { get; private set; default = 
         new Geary.SimpleProgressMonitor(Geary.ProgressType.ACTIVITY); }
     
@@ -38,6 +41,8 @@ private class Geary.App.ConversationOperationQueue : BaseObject {
                     }
                 }
             }
+            
+            has_fill_window = true;
         }
         
         mailbox.send(op);
@@ -66,9 +71,13 @@ private class Geary.App.ConversationOperationQueue : BaseObject {
                 debug("Error processing in conversation operation mailbox: %s", e.message);
                 break;
             }
+            
             if (op is TerminateOperation)
                 break;
             
+            if (op is FillWindowOperation)
+                has_fill_window = false;
+            
             if (!progress_monitor.is_in_progress)
                 progress_monitor.notify_start();
             
diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala
index f1fc3eb..d8e7f3b 100644
--- a/src/engine/imap-db/imap-db-folder.vala
+++ b/src/engine/imap-db/imap-db-folder.vala
@@ -141,6 +141,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
         int64 internaldate_time_t = -1;
         int offset = -1;
         yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
+            // get newest id and INTERNALDATE from MessageTable for this folder
             Db.Statement stmt = cx.prepare("""
                 SELECT id, internaldate_time_t
                 FROM MessageTable
@@ -161,6 +162,8 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
             message_id = result.rowid_at(0);
             internaldate_time_t = result.int64_at(1);
             
+            // Then turn around and sort MessageLocationTable by ordering (UID) and find the offset
+            // of that message
             stmt = cx.prepare("""
                 SELECT COUNT(*)
                 FROM MessageLocationTable
@@ -193,7 +196,10 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
         newest_date = (internaldate_time_t > -1) ? new DateTime.from_unix_utc(internaldate_time_t) : null;
         offset_from_top = offset;
         
-        debug("NEWEST: %s %s offset=%d", newest_id.to_string(), newest_date.to_string(), offset_from_top);
+        if (offset_from_top != 0) {
+            debug("Newest local email in %s is not at end of vector: %s %s offset=%d", to_string(),
+                newest_id.to_string(), newest_date.to_string(), offset_from_top);
+        }
         
         return true;
     }


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