# HG changeset patch # User Ian Neal # Date 1623162001 14400 # Parent d2950bdd8f1a3978a622c35ebf82ab012d200313 Bug 507601 - Port |Bug 414038 - Replace rdf-driven folder pane with a js-driven/non-rdf treeview| to SeaMonkey - Update and tidy the moved code in gFolderTreeController. r=frg a=frg diff --git a/suite/mailnews/content/folderPane.js b/suite/mailnews/content/folderPane.js --- a/suite/mailnews/content/folderPane.js +++ b/suite/mailnews/content/folderPane.js @@ -18,40 +18,39 @@ ChromeUtils.import("resource://gre/modul 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; + let folder = aParent || GetSelectedMsgFolders()[0]; - 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"); - } + // Make sure we actually can create subfolders. + if (!folder.canCreateSubfolders) { + // Check if we can create them at the root. + let rootMsgFolder = folder.server.rootMsgFolder; + if (rootMsgFolder.canCreateSubfolders) + folder = rootMsgFolder; + else // just use the default account + folder = GetDefaultAccountRootFolder(); } - //xxx useless param + let dualUseFolders = true; + if (folder.server instanceof Ci.nsIImapIncomingServer) + dualUseFolders = folder.server.dualUseFolders; + function newFolderCallback(aName, aFolder) { + // createSubfolder can throw an exception, causing the newFolder dialog + // to not close and wait for another input. + // TODO: Rewrite this logic and move the opening of alert dialogs from + // nsMsgLocalMailFolder::CreateSubfolderInternal to here (bug 831190#c16). if (aName) - folder.createSubfolder(aName, msgWindow); + aFolder.createSubfolder(aName, msgWindow); } window.openDialog("chrome://messenger/content/newFolderDialog.xul", "", "chrome,modal,centerscreen", {folder: folder, dualUseFolders: dualUseFolders, okCallback: newFolderCallback}); @@ -86,18 +85,24 @@ var gFolderTreeController = { folder.rename(aNewName, msgWindow); } function rebuildSummary(msgFolder) { if (msgFolder.locked) { msgFolder.throwAlertMsg("operationFailedFolderBusy", msgWindow); return; } - let msgDB = msgFolder.msgDatabase; - msgDB.summaryValid = false; + if (msgFolder.supportsOffline) { + // Remove the offline store, if any. + let offlineStore = msgFolder.filePath; + if (offlineStore.exists()) + offlineStore.remove(false); + } + msgFolder.msgDatabase.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 @@ -125,176 +130,168 @@ var gFolderTreeController = { * 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]; + let controller = this; function renameCallback(aName, aUri) { + if (aUri != folder.URI) + Cu.reportError("got back a different folder to rename!"); + + controller._resetThreadPane(); 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"); - } + folder.rename(aName, msgWindow); } 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") + let canDelete = folder.isSpecialFolder(Ci.nsMsgFolderFlags.Junk, false) ? + CanRenameDeleteJunkMail(folder.URI) : folder.deletable; + if (!canDelete) 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) + if (ConfirmUnsubscribe(folder)) UnSubscribe(folder); + continue; } - 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); + + // We can delete this folder. + let array = toXPCOMArray([folder], Ci.nsIMutableArray); + try { + folder.parent.deleteSubFolders(array, msgWindow); + } + // Ignore known errors from canceled warning dialogs. + catch (ex) { + const NS_MSG_ERROR_COPY_FOLDER_ABORTED = 0x8055001a; + if (ex.result != NS_MSG_ERROR_COPY_FOLDER_ABORTED) { + throw ex; } - // 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 + * Prompts the user to confirm and empties the trash for the selected folder. + * The folder and its children are only emptied if it has the proper Trash + * flag. * - * @param aFolder (optional) the trash folder to empty - * @note Calling this function on a non-trash folder will result in strange - * behavior! + * @param aFolder (optional) The trash folder to empty. If unspecified or not + * a trash folder, the currently selected server's + * trash folder is used. */ emptyTrash(aFolder) { let folder = aFolder || GetSelectedMsgFolders()[0]; + if (!folder.getFlag(Ci.nsMsgFolderFlags.Trash)) + folder = folder.rootFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Trash); + if (!folder) + return; + if (this._checkConfirmationPrompt("emptyTrash")) folder.emptyTrash(msgWindow, null); }, /** - * Deletes everything (folders and messages) in this folder + * Deletes everything (folders and messages) in the selected folder. + * The folder is only emptied if it has the proper Junk flag. * - * @param aFolder (optional) the folder to empty + * @param aFolder (optional) The folder to empty. If unspecified, the + * currently selected folder is used, if it + * is junk. */ emptyJunk(aFolder) { let folder = aFolder || GetSelectedMsgFolders()[0]; + if (!folder || !folder.getFlag(Ci.nsMsgFolderFlags.Junk)) + return; + 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()); - } + let messages = Array.from(fixIterator(folder.messages)); + let children = toXPCOMArray(messages, Ci.nsIMutableArray); 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; - } + // Can't compact folders that have just been compacted + if (!isImapFolder && !folder.expungedBytes && !aCompactAll) + return; + + // Reset thread pane for non-imap folders. + if (!isImapFolder && gDBView && + (gDBView.msgFolder == folder || aCompactAll)) { + this._resetThreadPane(); } 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]; + if (!folder) + folder = GetDefaultAccountRootFolder(); + let name = folder.prettyName; if (aName) name += "-" + aName; window.openDialog("chrome://messenger/content/virtualFolderProperties.xul", "", "chrome,modal,centerscreen", {folder: folder, searchTerms: aSearchTems, newFolderName: name}); @@ -319,16 +316,40 @@ var gFolderTreeController = { window.openDialog("chrome://messenger/content/virtualFolderProperties.xul", "", "chrome,modal,centerscreen", {folder: folder, editExistingFolder: true, onOKCallback: editVirtualCallback, msgWindow:msgWindow}); }, /** + * Opens a search window with the given folder, or the selected one if none + * is given. + * + * @param [aFolder] the folder to open the search window for, if different + * from the selected one + */ + searchMessages(aFolder) { + MsgSearchMessages(aFolder || GetSelectedMsgFolders()[0]); + }, + + /** + * For certain folder commands, the thread pane needs to be invalidated, this + * takes care of doing so. + */ + _resetThreadPane() { + if (gDBView) + gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage; + + ClearThreadPaneSelection(); + ClearThreadPane(); + ClearMessagePane(); + }, + + /** * 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 diff --git a/suite/mailnews/content/mailWindowOverlay.js b/suite/mailnews/content/mailWindowOverlay.js --- a/suite/mailnews/content/mailWindowOverlay.js +++ b/suite/mailnews/content/mailWindowOverlay.js @@ -1433,53 +1433,16 @@ function MsgCreateFilter() } if (!folder) folder = GetFirstSelectedMsgFolder(); if (emailAddress) top.MsgFilters(emailAddress, folder); } -function getDestinationFolder(preselectedFolder, server) -{ - var destinationFolder = null; - - var isCreateSubfolders = preselectedFolder.canCreateSubfolders; - if (!isCreateSubfolders) - { - destinationFolder = server.rootMsgFolder; - - var verifyCreateSubfolders = null; - if (destinationFolder) - verifyCreateSubfolders = destinationFolder.canCreateSubfolders; - - // in case the server cannot have subfolders, - // get default account and set its incoming server as parent folder - if (!verifyCreateSubfolders) - { - try { - var defaultFolder = GetDefaultAccountRootFolder(); - var checkCreateSubfolders = null; - if (defaultFolder) - checkCreateSubfolders = defaultFolder.canCreateSubfolders; - - if (checkCreateSubfolders) - destinationFolder = defaultFolder; - - } catch (e) { - dump ("Exception: defaultAccount Not Available\n"); - } - } - } - else - destinationFolder = preselectedFolder; - - return destinationFolder; -} - function MsgSubscribe() { var preselectedFolder = GetFirstSelectedMsgFolder(); if (preselectedFolder && preselectedFolder.server.type == "rss") openSubscriptionsDialog(preselectedFolder); // open feed subscription dialog else Subscribe(preselectedFolder); // open imap/nntp subscription dialog diff --git a/suite/mailnews/content/mailWindowOverlay.xul b/suite/mailnews/content/mailWindowOverlay.xul --- a/suite/mailnews/content/mailWindowOverlay.xul +++ b/suite/mailnews/content/mailWindowOverlay.xul @@ -410,17 +410,17 @@ oncommand="MsgOpenNewTabForFolder();"/> + oncommand="gFolderTreeController.searchMessages();"/>