Bug 507601 - Port |Bug 414038 - Replace rdf-driven folder pane with a js-driven/non-rdf treeview| to SeaMonkey - Move functions to folderPane.js gFolderTreeController. r=frg a=frg
authorIan Neal <iann_cvs@blueyonder.co.uk>
Thu, 12 Dec 2019 19:33:00 +0100
changeset 32383 4a152649b5d834a2459d62346607e178ab11fbe2
parent 32382 fddcb5bd203c2f4bb0c85225231efd7ae10ec8e5
child 32384 cf178c975a6a8d5227beb730289f7a5325e657aa
push id232
push userfrgrahl@gmx.net
push dateThu, 12 Dec 2019 18:52:58 +0000
treeherdercomm-esr60@6f606bccc252 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfrg, frg
bugs507601, 414038
Bug 507601 - Port |Bug 414038 - Replace rdf-driven folder pane with a js-driven/non-rdf treeview| to SeaMonkey - Move functions to folderPane.js gFolderTreeController. r=frg a=frg
suite/mailnews/content/folderPane.js
suite/mailnews/content/mail3PaneWindowCommands.js
suite/mailnews/content/mailCommands.js
suite/mailnews/content/mailWindowOverlay.js
suite/mailnews/content/mailWindowOverlay.xul
suite/mailnews/content/messageWindow.js
suite/mailnews/content/messenger.xul
suite/mailnews/content/msgViewPickerOverlay.js
suite/mailnews/content/widgetglue.js
suite/mailnews/jar.mn
new file mode 100644
--- /dev/null
+++ b/suite/mailnews/content/folderPane.js
@@ -0,0 +1,353 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ChromeUtils.import("resource:///modules/folderUtils.jsm");
+ChromeUtils.import("resource:///modules/iteratorUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+// Implements a tree of folders. It shows icons depending on folder type
+// and other fancy styling.
+// This is used in the main folder pane, but also some dialogs that need
+// to show a nice list of folders.
+
+/**
+ * This handles the invocation of most commands dealing with folders, based off
+ * of the current selection, or a passed in folder.
+ */
+var gFolderTreeController = {
+  /**
+   * Opens the dialog to create a new sub-folder, and creates it if the user
+   * accepts
+   *
+   * @param aParent (optional)  the parent for the new subfolder
+   */
+  newFolder(aParent) {
+    var preselectedFolder = aParent || GetFirstSelectedMsgFolder();
+    var dualUseFolders = true;
+    var server = null;
+    var folder = null;
+
+    if (preselectedFolder) {
+      try {
+        server = preselectedFolder.server;
+        if (server) {
+         folder = getDestinationFolder(preselectedFolder, server);
+
+          var imapServer = server.QueryInterface(Ci.nsIImapIncomingServer);
+          if (imapServer)
+            dualUseFolders = imapServer.dualUseFolders;
+        }
+      } catch (e) {
+        dump ("Exception: dualUseFolders = true\n");
+      }
+    }
+
+    //xxx useless param
+    function newFolderCallback(aName, aFolder) {
+      if (aName)
+        folder.createSubfolder(aName, msgWindow);
+    }
+
+    window.openDialog("chrome://messenger/content/newFolderDialog.xul",
+                      "",
+                      "chrome,modal,centerscreen",
+                      {folder: folder,
+                       dualUseFolders: dualUseFolders,
+                       okCallback: newFolderCallback});
+  },
+
+  /**
+   * Opens the dialog to edit the properties for a folder
+   *
+   * @param aTabID  (optional) the tab to show in the dialog
+   * @param aFolder (optional) the folder to edit, if not the selected one
+   */
+  editFolder(aTabID, aFolder) {
+    let folder = aFolder || GetSelectedMsgFolders()[0];
+
+    // If a server is selected, view settings for that account.
+    if (folder.isServer) {
+      MsgAccountManager(null, folder.server);
+      return;
+    }
+
+    if (folder.flags & Ci.nsMsgFolderFlags.Virtual) {
+      // virtual folders get their own property dialog that contains all of the
+      // search information related to the virtual folder.
+      this.editVirtualFolder(folder);
+      return;
+    }
+
+    let title = gMessengerBundle.getString("folderProperties");
+
+    function editFolderCallback(aNewName, aOldName, aUri) {
+      if (aNewName != aOldName)
+        folder.rename(aNewName, msgWindow);
+    }
+
+    function rebuildSummary(msgFolder) {
+      if (msgFolder.locked) {
+        msgFolder.throwAlertMsg("operationFailedFolderBusy", msgWindow);
+        return;
+      }
+      let msgDB = msgFolder.msgDatabase;
+      msgDB.summaryValid = false;
+      try {
+        msgFolder.closeAndBackupFolderDB("");
+      }
+      catch(e) {
+        // In a failure, proceed anyway since we're dealing with problems
+        msgFolder.ForceDBClosed();
+      }
+      // these two lines will cause the thread pane to get reloaded
+      // when the download/reparse is finished. Only do this
+      // if the selected folder is loaded (i.e., not thru the
+      // context menu on a non-loaded folder).
+      if (msgFolder == GetLoadedMsgFolder()) {
+        gRerootOnFolderLoad = true;
+        gCurrentFolderToReroot = msgFolder.URI;
+      }
+      msgFolder.updateFolder(msgWindow);
+    }
+
+    window.openDialog("chrome://messenger/content/folderProps.xul",
+                      "", "chrome,modal,centerscreen",
+                      {folder: folder, serverType: folder.server.type,
+                       msgWindow: msgWindow, title: title,
+                       okCallback: editFolderCallback, tabID: aTabID,
+                       name: folder.prettyName,
+                       rebuildSummaryCallback: rebuildSummary});
+  },
+
+ /**
+   * Opens the dialog to rename a particular folder, and does the renaming if
+   * the user clicks OK in that dialog
+   *
+   * @param aFolder (optional)  the folder to rename, if different than the
+   *                            currently selected one
+   */
+  renameFolder(aFolder) {
+    let folder = aFolder || GetSelectedMsgFolders()[0];
+
+    function renameCallback(aName, aUri) {
+      let folderTree = GetFolderTree();
+      if (gDBView)
+        gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage;
+
+      ClearThreadPane();
+      ClearMessagePane();
+      folderTree.view.selection.clearSelection();
+
+      try {
+        folder.rename(aName, msgWindow);
+      }
+      catch(e) {
+        SelectFolder(folder.URI);  //restore selection
+        throw(e); // so that the dialog does not automatically close
+        dump ("Exception : RenameFolder \n");
+      }
+    }
+
+    window.openDialog("chrome://messenger/content/renameFolderDialog.xul",
+                      "", "chrome,modal,centerscreen",
+                      {preselectedURI: folder.URI,
+                       okCallback: renameCallback, name: folder.prettyName});
+  },
+
+  /**
+   * Deletes a folder from its parent. Also handles unsubscribe from newsgroups
+   * if the selected folder/s happen to be nntp.
+   *
+   * @param aFolder (optional) the folder to delete, if not the selected one
+   */
+  deleteFolder(aFolder) {
+    let folders = aFolder ? [aFolder] : GetSelectedMsgFolders();
+    const NS_MSG_ERROR_COPY_FOLDER_ABORTED = 0x8055001a;
+    let prompt = Services.prompt;
+    for (let folder of folders) {
+      let specialFolder = getSpecialFolderString(folder);
+      if (specialFolder == "Inbox" || specialFolder == "Trash")
+        continue;
+
+      if (folder.flags & Ci.nsMsgFolderFlags.Virtual) {
+        let confirmation = gMessengerBundle.getString("confirmSavedSearchDeleteMessage");
+        let title = gMessengerBundle.getString("confirmSavedSearchDeleteTitle");
+        let buttonTitle = gMessengerBundle.getString("confirmSavedSearchDeleteButton");
+        let buttonFlags = prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0 +
+                          prompt.BUTTON_TITLE_CANCEL * prompt.BUTTON_POS_1;
+        if (prompt.confirmEx(window, title, confirmation, buttonFlags, buttonTitle,
+                             "", "", "", {}) != 0) /* the yes button is in position 0 */
+          continue;
+        if (gCurrentVirtualFolderUri == folder.URI)
+          gCurrentVirtualFolderUri = null;
+        let array = Cc["@mozilla.org/array;1"]
+                      .createInstance(Ci.nsIMutableArray);
+        array.appendElement(folder);
+        folder.parent.deleteSubFolders(array, msgWindow);
+        continue;
+      }
+
+      if (isNewsURI(folder.URI)) {
+        let unsubscribe = ConfirmUnsubscribe(folder);
+        if (unsubscribe)
+          UnSubscribe(folder);
+      }
+      else if (specialFolder == "Junk" ?
+               CanRenameDeleteJunkMail(folder.URI) : folder.deletable) {
+        // We can delete this folder.
+        let array = Cc["@mozilla.org/array;1"]
+                      .createInstance(Ci.nsIMutableArray);
+        array.appendElement(folder);
+        try {
+          folder.parent.deleteSubFolders(array, msgWindow);
+        }
+        // Ignore known errors from canceled warning dialogs.
+        catch (ex if (ex.result == NS_MSG_ERROR_COPY_FOLDER_ABORTED)) {}
+      }
+    }
+  },
+
+  /**
+   * Prompts the user to confirm and empties the trash for the selected folder
+   *
+   * @param aFolder (optional)  the trash folder to empty
+   * @note Calling this function on a non-trash folder will result in strange
+   *       behavior!
+   */
+  emptyTrash(aFolder) {
+    let folder = aFolder || GetSelectedMsgFolders()[0];
+    if (this._checkConfirmationPrompt("emptyTrash"))
+      folder.emptyTrash(msgWindow, null);
+  },
+
+  /**
+   * Deletes everything (folders and messages) in this folder
+   *
+   * @param aFolder (optional)  the folder to empty
+   */
+  emptyJunk(aFolder) {
+    let folder = aFolder || GetSelectedMsgFolders()[0];
+
+    if (!this._checkConfirmationPrompt("emptyJunk"))
+      return;
+
+    // Delete any sub-folders this folder might have.
+    let iter = folder.subFolders;
+    while (iter.hasMoreElements())
+      folder.propagateDelete(iter.getNext(), true, msgWindow);
+
+    let children = Cc["@mozilla.org/array;1"]
+                     .createInstance(Ci.nsIMutableArray);
+
+    // Now delete the messages.
+    iter = folder.messages;
+    while (iter.hasMoreElements()) {
+      children.appendElement(iter.getNext());
+    }
+    folder.deleteMessages(children, msgWindow, true, false, null, false);
+    children.clear();
+  },
+
+  /**
+   * Compacts either a particular folder, or all folders
+   *
+   * @param aCompactAll - whether we should compact all folders
+   * @param aFolder (optional) the folder to compact, if different than the
+   *                           currently selected one
+   */
+  compactFolder(aCompactAll, aFolder) {
+    let folder = aFolder || GetSelectedMsgFolders()[0];
+    let isImapFolder = folder.server.type == "imap";
+    if (!isImapFolder) {
+      if (folder.expungedBytes > 0) {
+        if (gDBView) {
+          gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage;
+          if (gDBView.msgFolder == folder || aCompactAll) {
+            ClearThreadPaneSelection();
+            ClearThreadPane();
+            ClearMessagePane();
+          }
+        }
+      }
+      else {
+        if (!aCompactAll) // you have one local folder with no room to compact
+          return;
+      }
+    }
+    if (aCompactAll)
+      folder.compactAll(null, msgWindow, isImapFolder || folder.server.type == "nntp");
+    else
+      folder.compact(null, msgWindow);
+  },
+
+  /**
+   * Opens the dialog to create a new virtual folder
+   *
+   * @param aName - the default name for the new folder
+   * @param aSearchTerms - the search terms associated with the folder
+   * @param aParent - the folder to run the search terms on
+   */
+  newVirtualFolder(aName, aSearchTerms, aParent) {
+    let folder = aParent || GetSelectedMsgFolders()[0];
+    let name = folder.prettyName;
+    if (aName)
+      name += "-" + aName;
+
+    window.openDialog("chrome://messenger/content/virtualFolderProperties.xul",
+                      "", "chrome,modal,centerscreen",
+                      {folder: folder, searchTerms: aSearchTems,
+                       newFolderName: name});
+  },
+
+  /**
+   * Opens the dialog to edit the properties for a virtual folder
+   *
+   * @param aFolder (optional) the folder to edit, if not the selected one
+   */
+  editVirtualFolder(aFolder) {
+    let folder = aFolder || GetSelectedMsgFolders()[0];
+
+    function editVirtualCallback(aURI) {
+      // we need to reload the folder if it is the currently loaded folder...
+      if (gMsgFolderSelected && aURI == gMsgFolderSelected.URI) {
+        // force the folder pane to reload the virtual folder
+        gMsgFolderSelected = null;
+        FolderPaneSelectionChange();
+      }
+    }
+    window.openDialog("chrome://messenger/content/virtualFolderProperties.xul",
+                      "", "chrome,modal,centerscreen",
+                      {folder: folder, editExistingFolder: true,
+                       onOKCallback: editVirtualCallback,
+                       msgWindow:msgWindow});
+  },
+
+  /**
+   * Prompts for confirmation, if the user hasn't already chosen the "don't ask
+   * again" option.
+   *
+   * @param aCommand - the command to prompt for
+   */
+  _checkConfirmationPrompt(aCommand) {
+    const kDontAskAgainPref = "mailnews." + aCommand + ".dontAskAgain";
+    // default to ask user if the pref is not set
+    if (!Services.prefs.getBoolPref(kDontAskAgainPref, false)) {
+      let checkbox = {value: false};
+      let choice = Services.prompt.confirmEx(
+                     window,
+                     gMessengerBundle.getString(aCommand + "Title"),
+                     gMessengerBundle.getString(aCommand + "Message"),
+                     Services.prompt.STD_YES_NO_BUTTONS,
+                     null, null, null,
+                     gMessengerBundle.getString(aCommand + "DontAsk"),
+                     checkbox);
+      if (checkbox.value)
+        Services.prefs.setBoolPref(kDontAskAgainPref, true);
+
+      if (choice != 0)
+        return false;
+    }
+    return true;
+  },
+}
--- a/suite/mailnews/content/mail3PaneWindowCommands.js
+++ b/suite/mailnews/content/mail3PaneWindowCommands.js
@@ -84,17 +84,17 @@ var FolderPaneController =
     if (!this.isCommandEnabled(command)) return;
 
     switch ( command )
     {
       case "cmd_delete":
       case "cmd_shiftDelete":
       case "button_delete":
       case "button_shiftDelete":
-        MsgDeleteFolder();
+        gFolderTreeController.deleteFolder();
         break;
     }
   },
 
   onEvent: function(event)
   {
   }
 };
@@ -582,17 +582,17 @@ var DefaultController =
         break;
       case "cmd_expandAllThreads":
                 gDBView.doCommand(nsMsgViewCommandType.expandAll);
         break;
       case "cmd_collapseAllThreads":
                 gDBView.doCommand(nsMsgViewCommandType.collapseAll);
         break;
       case "cmd_renameFolder":
-        MsgRenameFolder();
+        gFolderTreeController.renameFolder();
         return;
       case "cmd_sendUnsentMsgs":
         MsgSendUnsentMsgs();
         return;
       case "cmd_subscribe":
         MsgSubscribe();
         return;
       case "cmd_openMessage":
@@ -612,32 +612,32 @@ var DefaultController =
         return;
       case "cmd_saveAsTemplate":
         MsgSaveAsTemplate();
         return;
       case "cmd_viewPageSource":
         MsgViewPageSource();
         return;
       case "cmd_setFolderCharset":
-        MsgFolderProperties();
+        gFolderTreeController.editFolder();
         return;
       case "cmd_reload":
         ReloadMessage();
         return;
       case "cmd_find":
         MsgFind();
         return;
       case "cmd_findNext":
         MsgFindAgain(false);
         return;
       case "cmd_findPrev":
         MsgFindAgain(true);
         return;
       case "cmd_properties":
-        MsgFolderProperties();
+        gFolderTreeController.editFolder();
         return;
       case "button_search":
       case "cmd_search":
         MsgSearchMessages();
         return;
       case "button_mark":
       case "cmd_markAsRead":
         MsgMarkMsgAsRead(null);
@@ -686,20 +686,20 @@ var DefaultController =
         return;
       case "cmd_runJunkControls":
         filterFolderForJunk();
         return;
       case "cmd_deleteJunk":
         deleteJunkInFolder();
         return;
       case "cmd_emptyTrash":
-        MsgEmptyTrash();
+        gFolderTreeController.emptyTrash();
         return;
       case "cmd_compactFolder":
-        MsgCompactFolder(true);
+        gFolderTreeController.compactFolder(true);
         return;
       case "cmd_downloadFlagged":
         MsgDownloadFlagged();
         break;
       case "cmd_downloadSelected":
         MsgDownloadSelected();
         break;
       case "cmd_synchronizeOffline":
@@ -896,76 +896,16 @@ function IsFolderSelected()
   return folders.length == 1 && !folders[0].isServer;
 }
 
 function IsMessageDisplayedInMessagePane()
 {
   return (!IsMessagePaneCollapsed() && (GetNumSelectedMessages() > 0));
 }
 
-function MsgDeleteFolder()
-{
-  const NS_MSG_ERROR_COPY_FOLDER_ABORTED = 0x8055001a;
-  var folderTree = GetFolderTree();
-  var selectedFolders = GetSelectedMsgFolders();
-  var prompt = Services.prompt;
-  for (var i = 0; i < selectedFolders.length; i++)
-  {
-    var selectedFolder = selectedFolders[i];
-    let specialFolder = getSpecialFolderString(selectedFolder);
-    if (specialFolder != "Inbox" && specialFolder != "Trash")
-    {
-      var folder = selectedFolder.QueryInterface(Ci.nsIMsgFolder);
-      if (folder.flags & Ci.nsMsgFolderFlags.Virtual)
-      {
-        var confirmation = gMessengerBundle.getString("confirmSavedSearchDeleteMessage");
-        var title = gMessengerBundle.getString("confirmSavedSearchDeleteTitle");
-        var buttonTitle = gMessengerBundle.getString("confirmSavedSearchDeleteButton");
-        var buttonFlags = prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0 +
-                          prompt.BUTTON_TITLE_CANCEL * prompt.BUTTON_POS_1;
-        if (prompt.confirmEx(window, title, confirmation, buttonFlags, buttonTitle,
-                             "", "", "", {}) != 0) /* the yes button is in position 0 */
-          continue;
-        if (gCurrentVirtualFolderUri == selectedFolder.URI)
-          gCurrentVirtualFolderUri = null;
-        var array = Cc["@mozilla.org/array;1"]
-                      .createInstance(Ci.nsIMutableArray);
-        array.appendElement(folder);
-        folder.parent.deleteSubFolders(array, msgWindow);
-        continue;
-      }
-
-      if (isNewsURI(selectedFolder.URI))
-      {
-        var unsubscribe = ConfirmUnsubscribe(selectedFolder);
-        if (unsubscribe)
-          UnSubscribe(selectedFolder);
-      }
-      else if (specialFolder == "Junk" ?
-             CanRenameDeleteJunkMail(folder.URI) : folder.deletable)
-      {
-        // We can delete this folder.
-        var array = Cc["@mozilla.org/array;1"]
-                      .createInstance(Ci.nsIMutableArray);
-        array.appendElement(selectedFolder);
-        try
-        {
-          selectedFolder.parent.deleteSubFolders(array, msgWindow);
-        }
-        // Ignore known errors from canceled warning dialogs.
-        catch (ex) {
-          if (ex.result != NS_MSG_ERROR_COPY_FOLDER_ABORTED) {
-            throw ex;
-          }
-        }
-      }
-    }
-  }
-}
-
 function SetFocusThreadPaneIfNotOnMessagePane()
 {
   var focusedElement = gFolderDisplay.focusedPane;
 
   if((focusedElement != GetThreadTree()) &&
      (focusedElement != GetMessagePane()))
      SetFocusThreadPane();
 }
--- a/suite/mailnews/content/mailCommands.js
+++ b/suite/mailnews/content/mailCommands.js
@@ -258,24 +258,16 @@ function NewMessageToSelectedAddresses(t
       }
       composeFields.to = addressList.join(",");
       params.composeFields = composeFields;
       msgComposeService.OpenComposeWindowWithParams(null, params);
     }
   }
 }
 
-function NewFolder(name, folder)
-{
-  if (!folder || !name)
-    return;
-
-  folder.createSubfolder(name, msgWindow);
-}
-
 function UnSubscribe(folder)
 {
   // Unsubscribe the current folder from the newsserver, this assumes any confirmation has already
   // been made by the user  SPL
 
   var server = folder.server;
   var subscribableServer = server.QueryInterface(Ci.nsISubscribableServer);
   subscribableServer.unsubscribe(folder.name);
@@ -447,65 +439,8 @@ function ViewPageSource(messages)
         return false;
     }
 }
 
 function doHelpButton()
 {
     openHelp("mail-offline-items");
 }
-
-function confirmToProceed(commandName)
-{
-  const kDontAskAgainPref = "mailnews."+commandName+".dontAskAgain";
-  // default to ask user if the pref is not set
-  var dontAskAgain = false;
-  try {
-    dontAskAgain = Services.prefs.getBoolPref(kDontAskAgainPref);
-  } catch (ex) {}
-
-  if (!dontAskAgain)
-  {
-    var checkbox = {value:false};
-    var choice = Services.prompt.confirmEx(
-                   window,
-                   gMessengerBundle.getString(commandName+"Title"),
-                   gMessengerBundle.getString(commandName+"Message"),
-                   Services.prompt.STD_YES_NO_BUTTONS,
-                   null, null, null,
-                   gMessengerBundle.getString(commandName+"DontAsk"),
-                   checkbox);
-    try {
-      if (checkbox.value)
-        Services.prefs.setBoolPref(kDontAskAgainPref, true);
-    } catch (ex) {}
-
-    if (choice != 0)
-      return false;
-  }
-  return true;
-}
-
-function deleteAllInFolder(commandName)
-{
-  var folder = GetMsgFolderFromUri(GetSelectedFolderURI(), true);
-  if (!folder)
-    return;
-
-  if (!confirmToProceed(commandName))
-    return;
-
-  // Delete sub-folders.
-  var iter = folder.subFolders;
-  while (iter.hasMoreElements())
-    folder.propagateDelete(iter.getNext(), true, msgWindow);
-
-  var children = Cc["@mozilla.org/array;1"]
-                  .createInstance(Ci.nsIMutableArray);
-
-  // Delete messages.
-  iter = folder.messages;
-  while (iter.hasMoreElements()) {
-    children.appendElement(iter.getNext());
-  }
-  folder.deleteMessages(children, msgWindow, true, false, null, false);
-  children.clear();
-}
--- a/suite/mailnews/content/mailWindowOverlay.js
+++ b/suite/mailnews/content/mailWindowOverlay.js
@@ -1432,48 +1432,16 @@ function MsgCreateFilter()
   }
   if (!folder)
     folder = GetFirstSelectedMsgFolder();
 
     if (emailAddress)
      top.MsgFilters(emailAddress, folder);
 }
 
-function MsgNewFolder(callBackFunctionName)
-{
-    var preselectedFolder = GetFirstSelectedMsgFolder();
-    var dualUseFolders = true;
-    var server = null;
-    var destinationFolder = null;
-
-    if (preselectedFolder)
-    {
-        try {
-            server = preselectedFolder.server;
-            if (server)
-            {
-                destinationFolder = getDestinationFolder(preselectedFolder, server);
-
-                var imapServer =
-                    server.QueryInterface(Ci.nsIImapIncomingServer);
-                if (imapServer)
-                    dualUseFolders = imapServer.dualUseFolders;
-            }
-        } catch (e) {
-            dump ("Exception: dualUseFolders = true\n");
-        }
-    }
-    window.openDialog("chrome://messenger/content/newFolderDialog.xul",
-                      "",
-                      "chrome,modal,centerscreen",
-                      {folder: destinationFolder,
-                       dualUseFolders: dualUseFolders,
-                       okCallback:callBackFunctionName});
-}
-
 function getDestinationFolder(preselectedFolder, server)
 {
     var destinationFolder = null;
 
     var isCreateSubfolders = preselectedFolder.canCreateSubfolders;
     if (!isCreateSubfolders)
     {
         destinationFolder = server.rootMsgFolder;
@@ -1573,22 +1541,22 @@ function MsgOpenFromFile()
 }
 
 function MsgOpenNewWindowForFolder(uri, key)
 {
   var uriToOpen = uri;
   var keyToSelect = key;
 
   if (!uriToOpen)
-    // use GetSelectedFolderURI() to find out which message to open instead of
-    // GetLoadedMsgFolder().URI.
-    // This is required because on a right-click, the currentIndex value will be
-    // different from the actual row that is highlighted.  GetSelectedFolderURI()
-    // will return the message that is highlighted.
-    uriToOpen = GetSelectedFolderURI();
+    // Use GetSelectedMsgFolders() to find out which message to open instead of
+    // GetLoadedMsgFolder().URI. This is required because on a right-click, the
+    // currentIndex value will be different from the actual row that is
+    // highlighted. GetSelectedMsgFolders() will return the message that is
+    // highlighted.
+    uriToOpen = GetSelectedMsgFolders()[0].URI;
 
   if (uriToOpen) {
    // get the messenger window open service and ask it to open a new window for us
    var mailWindowService = Cc["@mozilla.org/messenger/windowservice;1"].getService(Ci.nsIMessengerWindowService);
    if (mailWindowService)
      mailWindowService.openMessengerWindowWithUri("mail:3pane", uriToOpen, keyToSelect);
   }
 }
@@ -1702,23 +1670,23 @@ function MsgOpenSearch(aSearchStr, aEven
 }
 
 function MsgOpenNewWindowForMessage(messageUri, folderUri)
 {
   if (!messageUri)
     messageUri = gFolderDisplay.selectedMessageUri;
 
     if (!folderUri)
-        // use GetSelectedFolderURI() to find out which message to open
+        // Use GetSelectedMsgFolders() to find out which message to open
         // instead of gDBView.getURIForViewIndex(currentIndex).  This is
         // required because on a right-click, the currentIndex value will be
         // different from the actual row that is highlighted.
-        // GetSelectedFolderURI() will return the message that is
+        // GetSelectedMsgFolders() will return the message that is
         // highlighted.
-        folderUri = GetSelectedFolderURI();
+        folderUri = GetSelectedMsgFolders()[0].URI;
 
     // be sure to pass in the current view....
     if (messageUri && folderUri) {
         window.openDialog( "chrome://messenger/content/messageWindow.xul", "_blank", "all,chrome,dialog=no,status,toolbar", messageUri, folderUri, gDBView );
     }
 }
 
 function CloseMailWindow()
@@ -1752,20 +1720,20 @@ function MsgMarkReadByDate()
 {
     window.openDialog( "chrome://messenger/content/markByDate.xul","",
                        "chrome,modal,titlebar,centerscreen",
                        GetLoadedMsgFolder() );
 }
 
 function MsgMarkAllRead()
 {
-    var folder = GetMsgFolderFromUri(GetSelectedFolderURI(), true);
-
-    if(folder)
-        folder.markAllMessagesRead(msgWindow);
+  var folder = GetSelectedMsgFolders()[0];
+
+  if (folder)
+    folder.markAllMessagesRead(msgWindow);
 }
 
 function MsgDownloadFlagged()
 {
   gDBView.doCommand(nsMsgViewCommandType.downloadFlaggedForOffline);
 }
 
 function MsgDownloadSelected()
@@ -2101,18 +2069,22 @@ function IsGetNextNMessagesEnabled()
     }
 
     menuItem.setAttribute("hidden","true");
     return false;
 }
 
 function IsCompactFolderEnabled()
 {
-  var server = GetServer(GetSelectedFolderURI());
+  let folder = GetSelectedMsgFolders()[0];
+  if (!folder)
+    return false;
+  let server = folder.server;
   return (server &&
+      (server.type != 'nntp') && // compact news folder is not supported
       ((server.type != 'imap') || server.canCompactFoldersOnServer) &&
       isCommandEnabled("cmd_compactFolder"));   // checks e.g. if IMAP is offline
 }
 
 function SetUpToolbarButtons(uri)
 {
   let deleteButton = document.getElementById("button-delete");
   let replyAllButton = document.getElementById("button-replyall");
--- a/suite/mailnews/content/mailWindowOverlay.xul
+++ b/suite/mailnews/content/mailWindowOverlay.xul
@@ -425,61 +425,61 @@
         accesskey="&folderContextUnsubscribe.accesskey;"
         oncommand="MsgUnsubscribe();"/>
 
     <menuseparator id="folderPaneContext-sep1"/>
 
     <menuitem id="folderPaneContext-new"
         label="&folderContextNew.label;"
         accesskey="&folderContextNew.accesskey;"
-        oncommand="MsgNewFolder(NewFolder);"/>
+        oncommand="gFolderTreeController.newFolder();"/>
     <menuitem id="folderPaneContext-remove"
               label="&folderContextRemove.label;"
               accesskey="&folderContextRemove.accesskey;"
-              oncommand="MsgDeleteFolder();"/>
+              oncommand="gFolderTreeController.deleteFolder();"/>
     <menuitem id="folderPaneContext-rename"
         label="&folderContextRename.label;"
         accesskey="&folderContextRename.accesskey;"
-        oncommand="MsgRenameFolder();"/>
+        oncommand="gFolderTreeController.renameFolder();"/>
 
     <menuitem id="folderPaneContext-compact"
         label="&folderContextCompact.label;"
         accesskey="&folderContextCompact.accesskey;"
-        oncommand="MsgCompactFolder(false);"/>
+        oncommand="gFolderTreeController.compactFolder(false);"/>
     <menuitem id="folderPaneContext-markMailFolderAllRead"
               label="&folderContextMarkMailFolderRead.label;"
               accesskey="&folderContextMarkMailFolderRead.accesskey;"
               oncommand="MsgMarkAllRead();"/>
     <menuitem id="folderPaneContext-markNewsgroupAllRead"
               label="&folderContextMarkNewsgroupRead.label;"
               accesskey="&folderContextMarkNewsgroupRead.accesskey;"
               oncommand="MsgMarkAllRead();"/>
     <menuitem id="folderPaneContext-emptyTrash"
         label="&folderContextEmptyTrash.label;"
         accesskey="&folderContextEmptyTrash.accesskey;"
-        oncommand="MsgEmptyTrash();"/>
+        oncommand="gFolderTreeController.emptyTrash();"/>
     <menuitem id="folderPaneContext-emptyJunk"
         label="&folderContextEmptyJunk.label;"
         accesskey="&folderContextEmptyJunk.accesskey;"
-        oncommand="deleteAllInFolder('emptyJunk');"/>
+        oncommand="gFolderTreeController.emptyJunk();"/>
     <menuitem id="folderPaneContext-sendUnsentMessages"
         label="&folderContextSendUnsentMessages.label;"
         accesskey="&folderContextSendUnsentMessages.accesskey;"
         oncommand="goDoCommand('cmd_sendUnsentMsgs')"/>
 
     <menuseparator id="folderPaneContext-sep-edit"/>
 
     <menuitem id="folderPaneContext-properties"
         label="&folderContextProperties.label;"
         accesskey="&folderContextProperties.accesskey;"
-        oncommand="MsgFolderProperties();"/>
+        oncommand="gFolderTreeController.editFolder();"/>
     <menuitem id="folderPaneContext-settings"
               label="&folderContextSettings.label;"
               accesskey="&folderContextSettings.accesskey;"
-              oncommand="MsgFolderProperties();"/>
+              oncommand="gFolderTreeController.editFolder();"/>
   </menupopup>
 
   <menupopup id="mailContext"
          onpopupshowing="return event.target != this ||
                                 FillMailContextMenu(this, event);"
          onpopuphiding="if (event.target == this) MailContextOnPopupHiding(this);">
     <menuitem id="context-openlinkintab"
               label="&openLinkCmdInTab.label;"
@@ -911,19 +911,19 @@
           <menuitem id="newNewMsgCmd"
                     label="&newNewMsgCmd.label;"
                     accesskey="&newNewMsgCmd.accesskey;"
                     key="key_newMessage"
                     oncommand="MsgNewMessage(null);"/>
           <menuitem id="menu_newFolder"
                     label="&newFolderCmd.label;"
                     accesskey="&newFolderCmd.accesskey;"
-                    oncommand="MsgNewFolder(NewFolder);"/>
+                    oncommand="gFolderTreeController.newFolder();"/>
           <menuitem id="menu_newVirtualFolder" label="&newVirtualFolderCmd.label;"
-                    oncommand="MsgVirtualFolderProperties(false);"
+                    oncommand="gFolderTreeController.newVirtualFolder();"
                     accesskey="&newVirtualFolderCmd.accesskey;"/>
           <menuitem id="newAccountMenuItem"
                     label="&newAccountCmd.label;"
                     accesskey="&newAccountCmd.accesskey;"
                     oncommand="MsgAccountWizard();"/>
           <menuseparator id="newPopupMenuSeparator"/>
           <menuitem id="menu_newCard"/>
           <menuitem id="menu_newTab"
--- a/suite/mailnews/content/messageWindow.js
+++ b/suite/mailnews/content/messageWindow.js
@@ -433,21 +433,16 @@ function GetSelectedIndices(dbView)
   }
 }
 
 function GetLoadedMsgFolder()
 {
   return gCurrentFolderUri ? GetMsgFolderFromUri(gCurrentFolderUri) : null;
 }
 
-function GetSelectedFolderURI()
-{
-  return gCurrentFolderUri;
-}
-
 function GetLoadedMessage()
 {
   return gCurrentMessageUri;
 }
 
 //Clear everything related to the current message. called after load start page.
 function ClearMessageSelection()
 {
--- a/suite/mailnews/content/messenger.xul
+++ b/suite/mailnews/content/messenger.xul
@@ -45,16 +45,17 @@
 <script type="application/javascript" src="chrome://messenger/content/commandglue.js"/>
 <script type="application/javascript" src="chrome://messenger/content/msgViewNavigation.js"/>
 <script type="application/javascript" src="chrome://messenger/content/mailWindow.js"/>
 <script type="application/javascript" src="chrome://messenger/content/msgMail3PaneWindow.js"/>
 <script type="application/javascript" src="chrome://messenger/content/mail3PaneWindowCommands.js"/>
 <script type="application/javascript" src="chrome://messenger/content/mailContextMenus.js"/>
 <script type="application/javascript" src="chrome://messenger/content/messengerdnd.js"/>
 <script type="application/javascript" src="chrome://messenger/content/accountUtils.js"/>
+<script type="application/javascript" src="chrome://messenger/content/folderPane.js"/>
 <script type="application/javascript" src="chrome://messenger/content/phishingDetector.js"/>
 <script type="application/javascript" src="chrome://communicator/content/contentAreaClick.js"/>
 <script type="application/javascript" src="chrome://global/content/nsDragAndDrop.js"/>
 <script type="application/javascript" src="chrome://messenger/content/searchBar.js"/>
 <script type="application/javascript" src="chrome://messenger/content/tabmail.js"/>
 
 <commandset id="mailCommands">
   <commandset id="mailFileMenuItems"/>
--- a/suite/mailnews/content/msgViewPickerOverlay.js
+++ b/suite/mailnews/content/msgViewPickerOverlay.js
@@ -34,22 +34,18 @@ function ViewChange(aValue, aLabel)
   if (aValue == kViewItemCustomize || aValue == kViewItemVirtual)
   {
     // restore to the previous view value, in case they cancel
     UpdateViewPicker(gCurrentViewValue, gCurrentViewLabel);
     if (aValue == kViewItemCustomize)
       LaunchCustomizeDialog();
     else
     {
-      // Thunderbird uses the folder pane for this.
-      if ("gFolderTreeController" in window)
-        gFolderTreeController.newVirtualFolder(gCurrentViewLabel,
-                                               gSaveDefaultSVTerms);
-      else
-        openNewVirtualFolderDialogWithArgs(gCurrentViewLabel, gSaveDefaultSVTerms);
+      gFolderTreeController.newVirtualFolder(gCurrentViewLabel,
+                                             gSaveDefaultSVTerms);
     }
     return;
   }
 
   // persist the view
   gCurrentViewValue = aValue;
   gCurrentViewLabel = aLabel;
   SetMailViewForFolder(GetFirstSelectedMsgFolder(), gCurrentViewValue)
--- a/suite/mailnews/content/widgetglue.js
+++ b/suite/mailnews/content/widgetglue.js
@@ -11,214 +11,16 @@
 
 //The eventual goal is for this file to go away and its contents to be brought into
 //mailWindowOverlay.js.  This is currently being done.
 
 ChromeUtils.import("resource:///modules/MailUtils.js");
 
 //NOTE: gMessengerBundle must be defined and set or this Overlay won't work
 
-function GetSelectedFolderURI()
-{
-  let folders = GetSelectedMsgFolders();
-  return folders.length == 1 ? folders[0].URI : null;
-}
-
-function MsgRenameFolder()
-{
-  let folder = GetSelectedMsgFolders()[0];
-  let dialog = window.openDialog(
-               "chrome://messenger/content/renameFolderDialog.xul",
-               "",
-               "chrome,modal,centerscreen",
-               {preselectedURI: folder.URI,
-                okCallback: RenameFolder, name: folder.prettyName});
-}
-
-function RenameFolder(name,uri)
-{
-  var folderTree = GetFolderTree();
-  if (folderTree)
-  {
-    if (uri && (uri != "") && name && (name != ""))
-    {
-      var selectedFolder = GetMsgFolderFromUri(uri);
-      if (gDBView)
-        gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage;
-
-      ClearThreadPane();
-      ClearMessagePane();
-      folderTree.view.selection.clearSelection();
-
-      try
-      {
-        selectedFolder.rename(name, msgWindow);
-      }
-      catch(e)
-      {
-        SelectFolder(selectedFolder.URI);  //restore selection
-        throw(e); // so that the dialog does not automatically close
-        dump ("Exception : RenameFolder \n");
-      }
-    }
-    else
-    {
-      dump("no name or nothing selected\n");
-    }
-  }
-  else
-  {
-    dump("no folder tree\n");
-  }
-}
-
-function MsgEmptyTrash()
-{
-  let folders = GetSelectedMsgFolders();
-  if (folders.length != 1 || !confirmToProceed("emptyTrash"))
-    return;
-
-  folders[0].emptyTrash(msgWindow, null);
-}
-
-function MsgCompactFolder(aCompactAccount)
-{
-  // Get the selected folders.
-  var selectedFolders = GetSelectedMsgFolders();
-  if (selectedFolders.length == 1)
-  {
-    var selectedFolder = selectedFolders[0];
-    var isImapFolder = selectedFolder.server.type == "imap";
-    if (!isImapFolder)
-    {
-      if (selectedFolder.expungedBytes > 0)
-      {
-        if (gDBView)
-        {
-          gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage;
-          if (gDBView.msgFolder == selectedFolder || aCompactAccount)
-          {
-            ClearThreadPaneSelection();
-            ClearThreadPane();
-            ClearMessagePane();
-          }
-        }
-      }
-      else
-      {
-        if (!aCompactAccount) // you have one local folder with no room to compact
-          return;
-      }
-    }
-    if (aCompactAccount)
-      selectedFolder.compactAll(null, msgWindow, isImapFolder || selectedFolder.server.type == "nntp");
-    else
-      selectedFolder.compact(null, msgWindow);
-  }
-}
-
-function openNewVirtualFolderDialogWithArgs(defaultViewName, aSearchTerms)
-{
-  var folder = GetFirstSelectedMsgFolder();
-  let name = folder.prettyName + "-" + defaultViewName;
-
-  var dialog = window.openDialog("chrome://messenger/content/virtualFolderProperties.xul", "",
-                                 "chrome,modal,centerscreen",
-                                 {folder:folder,
-                                  searchTerms:aSearchTerms,
-                                  newFolderName:name});
-}
-
-function MsgVirtualFolderProperties(aEditExistingVFolder)
-{
-  var preselectedFolder = GetFirstSelectedMsgFolder();
-
-  var dialog = window.openDialog("chrome://messenger/content/virtualFolderProperties.xul", "",
-                                 "chrome,modal,centerscreen",
-                                 {folder:preselectedFolder,
-                                  editExistingFolder: aEditExistingVFolder,
-                                  onOKCallback:onEditVirtualFolderPropertiesCallback,
-                                  msgWindow:msgWindow});
-}
-
-function onEditVirtualFolderPropertiesCallback(aVirtualFolderURI)
-{
-  // we need to reload the folder if it is the currently loaded folder...
-  if (gMsgFolderSelected && aVirtualFolderURI == gMsgFolderSelected.URI)
-  {
-    gMsgFolderSelected = null; // force the folder pane to reload the virtual folder
-    FolderPaneSelectionChange();
-  }
-}
-
-
-function MsgFolderProperties()
-{
-  let msgFolder = GetMsgFolderFromUri(GetSelectedFolderURI(), true);
-
-  // if a server is selected, view settings for that account
-  if (msgFolder.isServer) {
-    MsgAccountManager(null, msgFolder.server);
-    return;
-  }
-
-  if (msgFolder.flags & Ci.nsMsgFolderFlags.Virtual)
-  {
-    // virtual folders get their own property dialog that contains all of the
-    // search information related to the virtual folder.
-    MsgVirtualFolderProperties(true);
-    return;
-  }
-
-  var windowTitle = gMessengerBundle.getString("folderProperties");
-  var dialog = window.openDialog(
-              "chrome://messenger/content/folderProps.xul",
-              "",
-              "chrome,modal,centerscreen",
-              {folder: msgFolder, serverType: msgFolder.server.type,
-               msgWindow: msgWindow, title: windowTitle,
-               okCallback: FolderProperties, tabID: "", tabIndex: 0,
-               name: msgFolder.prettyName,
-               rebuildSummaryCallback: RebuildSummaryFile});
-}
-
-function RebuildSummaryFile(msgFolder)
-{
-  if (msgFolder.locked)
-  {
-    msgFolder.throwAlertMsg("operationFailedFolderBusy", msgWindow);
-    return;
-  }
-  var msgDB = msgFolder.msgDatabase;
-  msgDB.summaryValid = false;
-  try {
-    msgFolder.closeAndBackupFolderDB("");
-  }
-  catch(e) {
-    // In a failure, proceed anyway since we're dealing with problems
-    msgFolder.ForceDBClosed();
-  }
-  // these two lines will cause the thread pane to get reloaded
-  // when the download/reparse is finished. Only do this
-  // if the selected folder is loaded (i.e., not thru the
-  // context menu on a non-loaded folder).
-  if (msgFolder == GetLoadedMsgFolder())
-  {
-    gRerootOnFolderLoad = true;
-    gCurrentFolderToReroot = msgFolder.URI;
-  }
-  msgFolder.updateFolder(msgWindow);
-}
-
-function FolderProperties(name, oldName, uri)
-{
-  if (name != oldName)
-    RenameFolder(name, uri);
-}
-
 // Given a URI we would like to return corresponding message folder here.
 // An additonal input param which specifies whether or not to check folder
 // attributes (like if there exists a parent or is it a server) is also passed
 // to this routine. Qualifying against those checks would return an existing
 // folder. Callers who don't want to check those attributes will specify the
 // same and then this routine will simply return a msgfolder. This scenario
 // applies to a new imap account creation where special folders are created
 // on demand and hence needs to prior check of existence.
--- a/suite/mailnews/jar.mn
+++ b/suite/mailnews/jar.mn
@@ -19,18 +19,19 @@ messenger.jar:
 % overlay chrome://messenger/content/addressbook/abSelectAddressesDialog.xul   chrome://messenger/content/mailOverlay.xul
 % overlay chrome://editor/content/composerOverlay.xul                          chrome://messenger/content/mailEditorOverlay.xul
 % overlay chrome://editor/content/EdColorProps.xul                             chrome://messenger/content/messengercompose/EdColorPropsOverlay.xul
 % overlay chrome://editor/content/EdImageOverlay.xul                           chrome://messenger/content/messengercompose/mailComposeEditorOverlay.xul
 % overlay chrome://editor/content/EdLinkProps.xul                              chrome://messenger/content/messengercompose/mailComposeEditorOverlay.xul
     content/messenger/browserRequest.js                                        (content/browserRequest.js)
     content/messenger/browserRequest.xul                                       (content/browserRequest.xul)
     content/messenger/commandglue.js                                           (content/commandglue.js)
+    content/messenger/folderDisplay.js                                         (content/folderDisplay.js)
+    content/messenger/folderPane.js                                            (content/folderPane.js)
     content/messenger/folderPane.xul                                           (content/folderPane.xul)
-    content/messenger/folderDisplay.js                                         (content/folderDisplay.js)
     content/messenger/mail-offline.js                                          (content/mail-offline.js)
     content/messenger/mail3PaneWindowCommands.js                               (content/mail3PaneWindowCommands.js)
     content/messenger/mailCommands.js                                          (content/mailCommands.js)
     content/messenger/mailContextMenus.js                                      (content/mailContextMenus.js)
     content/messenger/mailEditorOverlay.xul                                    (content/mailEditorOverlay.xul)
     content/messenger/mailKeysOverlay.xul                                      (content/mailKeysOverlay.xul)
     content/messenger/mailOverlay.js                                           (content/mailOverlay.js)
     content/messenger/mailOverlay.xul                                          (content/mailOverlay.xul)