Add listener notifications with usable progress info for the indexing progress.
authorAndrew Sutherland <asutherland@asutherland.org>
Sun, 06 Jul 2008 17:53:55 -0700
changeset 831 2295e74c106d495adf2690be450ec139eec28f16
parent 830 216c9e2add57653bf0dda002b3755737e2cd9e28
child 832 45029a6efd9cef41f2f15a84ac28f85db69ac974
push idunknown
push userunknown
push dateunknown
Add listener notifications with usable progress info for the indexing progress.
content/overlay.js
modules/datastore.js
modules/indexer.js
--- a/content/overlay.js
+++ b/content/overlay.js
@@ -47,14 +47,14 @@ Components.utils.import("resource://glod
 
 var gloda = {
   onLoad: function() {
     // initialization code
     this.initialized = true;
     this.strings = document.getElementById("gloda-strings");
   },
   onMenuItemCommand: function(e) {
-    GlodaIndexer.init(window);
+    GlodaIndexer.init(window, msgWindow);
     GlodaIndexer.indexEverything();
   },
 
 };
 window.addEventListener("load", function(e) { gloda.onLoad(e); }, false);
--- a/modules/datastore.js
+++ b/modules/datastore.js
@@ -779,18 +779,18 @@ let GlodaDatastore = {
     smas.params.messageID = aMessage.id;
     while (smas.step()) {
       let attributeID = smas.row["attributeID"];
       if (!(attributeID in this._attributeIDToDef)) {
         this._log.error("Attribute ID " + attributeID + " not in our map!");
       } 
       let attribAndParam = this._attributeIDToDef[attributeID];
       let val = smas.row["value"];
-      this._log.debug("Loading attribute: " + attribAndParam[0].id + " param: "+
-                      attribAndParam[1] + " val: " + val);
+      //this._log.debug("Loading attribute: " + attribAndParam[0].id + " param: "+
+      //                attribAndParam[1] + " val: " + val);
       attribParamVals.push([attribAndParam[0], attribAndParam[1], val]);
     }
     smas.reset();
     
     return attribParamVals;
   },
   
   queryMessagesAPV: function gloda_ds_queryMessagesAPV(aAPVs) {
--- a/modules/indexer.js
+++ b/modules/indexer.js
@@ -117,25 +117,32 @@ function fixIterator(aEnum, aIface) {
 
 let GlodaIndexer = {
   _datastore: GlodaDatastore,
   _log: Log4Moz.Service.getLogger("gloda.indexer"),
   _msgwindow: null,
   _domWindow: null,
 
   _inited: false,
-  init: function gloda_index_init(aDOMWindow) {
+  init: function gloda_index_init(aDOMWindow, aMsgWindow) {
     if (this._inited)
       return;
     
+    this._inited = true;
+    
     this._domWindow = aDOMWindow;
     
+    // topmostMsgWindow explodes for un-clear reasons if we have multiple
+    //  windows open.  very sad.
+    /*
     let mailSession = Cc["@mozilla.org/messenger/services/session;1"].
                         getService(Ci.nsIMsgMailSession);
     this._msgWindow = mailSession.topmostMsgWindow;
+    */
+    this._msgWindow = aMsgWindow;
   },
 
   /** Track whether indexing is active (we have timers in-flight). */
   _indexingActive: false,
   get indexing() { return this._indexingActive; },
   /** You can turn on indexing, but you can't turn it off! */
   set indexing(aShouldIndex) {
     if (!this._indexingActive && aShouldIndex) {
@@ -143,17 +150,20 @@ let GlodaIndexer = {
       this._domWindow.setTimeout(this._wrapIncrementalIndex, this._indexInterval, this);
     }  
   },
   
   /** The nsIMsgFolder we are indexing, or null if we aren't. */
   _indexingFolder: null,
   /** The iterator we are using to traverse _indexingFolder. */
   _indexingIterator: null,
-  _indexingCount: 0,
+  _indexingFolderCount: 0,
+  _indexingFolderGoal: 0,
+  _indexingMessageCount: 0,
+  _indexingMessageGoal: 0,
   
   /**
    * A list of things yet to index.  Contents will be lists matching one of the
    *  following patterns:
    * - ['account', account object]
    * - ['folder', folder URI]
    * - ['message', folder URI, message key, message ID]
    */
@@ -166,35 +176,81 @@ let GlodaIndexer = {
   _indexInterval: 100,
   /**
    * Number of indexing 'tokens' we are allowed to consume before yielding for
    *  each incremental pass.  Consider a single token equal to indexing a single
    *  medium-sized message.  This may be altered by user session (in)activity.
    */
   _indexTokens: 10,
   
+  _indexListeners: [],
+  /**
+   * Add an indexing progress listener.  The listener will be notified of at
+   *  least all major status changes (idle -> indexing, indexing -> idle), plus
+   *  arbitrary progress updates during the indexing process.
+   * If indexing is not active when the listener is added, a synthetic idle
+   *  notification will be generated.
+   *
+   * @param aListener A listener function, taking arguments: status (string),
+   *     folder name being indexed (string or null), current zero-based folder
+   *     number being indexed (int), total number of folders to index (int),
+   *     current message number being indexed in this folder (int), total number
+   *     of messages in this folder to be indexed (int).
+   */
+  addListener: function gloda_index_addListener(aListener) {
+    // should we weakify?
+    if (this._indexListeners.indexOf(aListener) == -1)
+      this._indexListeners.push(aListener);
+    // if we aren't indexing, give them an idle indicator, otherwise they can
+    //  just be happy when we hit the next actual status point.
+    if (!this.indexing)
+      aListener("Idle", null, 0, 1, 0, 1);
+    return aListener;
+  },
+  removeListener: function gloda_index_removeListener(aListener) {
+    let index = this._indexListeners.indexOf(aListener);
+    if (index != -1)
+      this._indexListeners(index, 1);
+  },
+  _notifyListeners: function gloda_index_notifyListeners(aStatus, aFolderName,
+      aFolderIndex, aFoldersTotal, aMessageIndex, aMessagesTotal) {
+    for (let iListener=this._indexListeners.length-1; iListener >= 0; 
+         iListener--) {
+      let listener = this._indexListeners[iListener];
+      listener(aStatus, aFolderName, aFolderIndex, aFoldersTotal, aMessageIndex,
+               aMessagesTotal);
+    } 
+  },
+  
   _wrapIncrementalIndex: function gloda_index_wrapIncrementalIndex(aThis) {
     aThis.incrementalIndex();
   },
   
   incrementalIndex: function gloda_index_incrementalIndex() {
     this._log.debug("index wake-up!");
   
     GlodaDatastore._beginTransaction();
     try {
     
       for (let tokensLeft=this._indexTokens; tokensLeft > 0; tokensLeft--) {
         if (this._indexingFolder != null) {
           try {
             this._indexMessage(this._indexingIterator.next());
-            this._indexingCount++;
+            this._indexingMessageCount++;
             
-            if (this._indexingCount % 50 == 1) {
-              this._log.debug("indexed " + this._indexingCount + " in " +
-                              this._indexingFolder.prettiestName);
+            if (this._indexingMessageCount % 50 == 1) {
+              this._notifyListeners("Indexing: " +
+                                    this._indexingFolder.prettiestName,
+                                    this._indexingFolder.prettiestName,
+                                    this._indexingFolderCount,
+                                    this._indexingFolderGoal,
+                                    this._indexingMessageCount,
+                                    this._indexingMessageGoal);
+              //this._log.debug("indexed " + this._indexingCount + " in " +
+              //                this._indexingFolder.prettiestName);
             }
           }
           catch (ex) {
             this._log.debug("Done with indexing folder because: " + ex);
             this._indexingFolder = null;
             this._indexingIterator = null;
           }
         }
@@ -228,30 +284,38 @@ let GlodaIndexer = {
               try {
                 //this._indexingFolder.updateFolder(this._msgWindow);
               
                 let msgDatabase = folder.getMsgDatabase(this._msgWindow);
                 this._indexingIterator = Iterator(fixIterator(
                                            //folder.getMessages(this._msgWindow),
                                            msgDatabase.EnumerateMessages(),
                                            Ci.nsIMsgDBHdr));
+                this._indexingFolderCount++;
+                this._indexingMessageCount = 0;
+                this._indexingMessageGoal = folder.getTotalMessages(false); 
               }
               catch (ex) {
                 this._log.error("Problem indexing folder: " +
                                 folder.prettiestName + ", skipping.");
                 this._log.error("Error was: " + ex);
                 this._indexingFolder = null;
                 this._indexingIterator = null;
               }
             }
           }
         }
         else {
           this._log.info("Done indexing, disabling timer renewal.");
           this._indexingActive = false;
+          this._indexingFolderCount = 0;
+          this._indexingFolderGoal = 0;
+          this._indexingMessageCount = 0;
+          this._indexingMessageGoal = 0;
+          this._notifyListeners("Idle", null, 0, 1, 0, 1);
           break;
         }
       }
     
     }
     finally {
       GlodaDatastore._commitTransaction();
     
@@ -261,31 +325,31 @@ let GlodaIndexer = {
     }
   },
 
   indexEverything: function glodaIndexEverything() {
     this._log.info("Queueing all accounts for indexing.");
     let msgAccountManager = Cc["@mozilla.org/messenger/account-manager;1"].
                             getService(Ci.nsIMsgAccountManager);
     
-    this._indexQueue = this._indexQueue.concat(
-                [["account", account] for each
-                (account in fixIterator(msgAccountManager.accounts,
-                                        Ci.nsIMsgAccount))]);
-    this.indexing = true;
+    let sideEffects = [this.indexAccount(account) for each
+                       (account in fixIterator(msgAccountManager.accounts,
+                                               Ci.nsIMsgAccount))];
   },
 
   indexAccount: function glodaIndexAccount(aAccount) {
     let rootFolder = aAccount.incomingServer.rootFolder;
     if (rootFolder instanceof Ci.nsIMsgFolder) {
       this._log.info("Queueing account folders for indexing: " + aAccount.key);
 
-      this._indexQueue = this._indexQueue.concat(
+      let folders =
               [["folder", folder.URI] for each
-              (folder in fixIterator(rootFolder.subFolders, Ci.nsIMsgFolder))]);
+              (folder in fixIterator(rootFolder.subFolders, Ci.nsIMsgFolder))];
+      this._indexingFolderGoal += folders.length;
+      this._indexQueue = this._indexQueue.concat(folders);
       this.indexing = true;
     }
     else {
       this._log.info("Skipping Account, root folder not nsIMsgFolder");
     }
   },
 
   indexFolder: function glodaIndexFolder(aFolder) {
@@ -330,17 +394,17 @@ let GlodaIndexer = {
     // pull our current message lookup results off
     references.pop();
     let curMsg = ancestors.pop();
     
     if (curMsg != null) {
       // we already know about the guy, which means he was either previously
       // a ghost or he is a duplicate...
       if (curMsg.messageKey != null) {
-        this._log.warn("Attempting to re-index message: " + aMsgHdr.messageId
+        this._log.info("Attempting to re-index message: " + aMsgHdr.messageId
                         + " (" + aMsgHdr.subject + ")");
         return;
       } 
     }
     
     let conversationID = null;
     
     // (walk from closest to furthest ancestor)