diff options
Diffstat (limited to '')
-rw-r--r-- | comm/suite/mailnews/content/msgMail3PaneWindow.js | 1265 |
1 files changed, 1265 insertions, 0 deletions
diff --git a/comm/suite/mailnews/content/msgMail3PaneWindow.js b/comm/suite/mailnews/content/msgMail3PaneWindow.js new file mode 100644 index 0000000000..5cd3aa0693 --- /dev/null +++ b/comm/suite/mailnews/content/msgMail3PaneWindow.js @@ -0,0 +1,1265 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ + +/* This is where functions related to the 3 pane window are kept */ +const { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.js"); +const {msgDBCacheManager} = ChromeUtils.import("resource:///modules/msgDBCacheManager.js"); +const {PeriodicFilterManager} = ChromeUtils.import("resource:///modules/PeriodicFilterManager.jsm"); + +// from MailNewsTypes.h +const nsMsgKey_None = 0xFFFFFFFF; +const nsMsgViewIndex_None = 0xFFFFFFFF; +const kMailCheckOncePrefName = "mail.startup.enabledMailCheckOnce"; + +var gSearchInput; + +var gUnreadCount = null; +var gTotalCount = null; + +var gCurrentLoadingFolderURI; +var gCurrentFolderToReroot; +var gCurrentLoadingFolderSortType = 0; +var gCurrentLoadingFolderSortOrder = 0; +var gCurrentLoadingFolderViewType = 0; +var gCurrentLoadingFolderViewFlags = 0; +var gRerootOnFolderLoad = false; +var gCurrentDisplayedMessage = null; +var gNextMessageAfterDelete = null; +var gNextMessageAfterLoad = null; +var gNextMessageViewIndexAfterDelete = -2; +var gCurrentlyDisplayedMessage=nsMsgViewIndex_None; +var gStartMsgKey = nsMsgKey_None; +var gSearchEmailAddress = null; +var gRightMouseButtonDown = false; +// Global var to keep track of which row in the thread pane has been selected +// This is used to make sure that the row with the currentIndex has the selection +// after a Delete or Move of a message that has a row index less than currentIndex. +var gThreadPaneCurrentSelectedIndex = -1; +// Account Wizard can exceptionally override this feature. +var gLoadStartFolder = true; + +// Global var to keep track of if the 'Delete Message' or 'Move To' thread pane +// context menu item was triggered. This helps prevent the tree view from +// not updating on one of those menu item commands. +var gThreadPaneDeleteOrMoveOccurred = false; + +//If we've loaded a message, set to true. Helps us keep the start page around. +var gHaveLoadedMessage; + +var gDisplayStartupPage = false; + +function SelectAndScrollToKey(aMsgKey) +{ + // select the desired message + // if the key isn't found, we won't select anything + if (!gDBView) + return false; + gDBView.selectMsgByKey(aMsgKey); + + // is there a selection? + // if not, bail out. + var indicies = GetSelectedIndices(gDBView); + if (!indicies || !indicies.length) + return false; + + // now scroll to it + EnsureRowInThreadTreeIsVisible(indicies[0]); + return true; +} + +// A helper routine called after a folder is loaded to make sure +// we select and scroll to the correct message (could be the first new message, +// could be the last displayed message, etc.) +function ScrollToMessageAfterFolderLoad(folder) +{ + var scrolled = Services.prefs.getBoolPref("mailnews.scroll_to_new_message") && + ScrollToMessage(nsMsgNavigationType.firstNew, true, false /* selectMessage */); + if (!scrolled && folder && Services.prefs.getBoolPref("mailnews.remember_selected_message")) + { + // If we failed to scroll to a new message, + // reselect the last selected message + var lastMessageLoaded = folder.lastMessageLoaded; + if (lastMessageLoaded != nsMsgKey_None) + scrolled = SelectAndScrollToKey(lastMessageLoaded); + } + + if (!scrolled) + { + // if we still haven't scrolled, + // scroll to the newest, which might be the top or the bottom + // depending on our sort order and sort type + if (gDBView && gDBView.sortOrder == nsMsgViewSortOrder.ascending) + { + switch (gDBView.sortType) + { + case nsMsgViewSortType.byDate: + case nsMsgViewSortType.byReceived: + case nsMsgViewSortType.byId: + case nsMsgViewSortType.byThread: + scrolled = ScrollToMessage(nsMsgNavigationType.lastMessage, true, false /* selectMessage */); + break; + } + } + + // if still we haven't scrolled, + // scroll to the top. + if (!scrolled) + EnsureRowInThreadTreeIsVisible(0); + } +} + +// the folderListener object +var folderListener = +{ + onFolderAdded: function(parentFolder, child) {}, + onMessageAdded: function(parentFolder, msg) {}, + onFolderRemoved: function(parentFolder, child) {}, + onMessageRemoved: function(parentFolder, msg) {}, + + onFolderPropertyChanged: function(item, property, oldValue, newValue) {}, + onFolderBoolPropertyChanged: function(item, property, oldValue, newValue) {}, + onFolderUnicharPropertyChanged: function(item, property, oldValue, newValue) {}, + onFolderPropertyFlagChanged: function(item, property, oldFlag, newFlag) {}, + + onFolderIntPropertyChanged: function(item, property, oldValue, newValue) + { + // handle the currently visible folder + if (item == gMsgFolderSelected) + { + if (property == "TotalMessages" || property == "TotalUnreadMessages") + { + UpdateStatusMessageCounts(gMsgFolderSelected); + } + } + + // check folders shown in tabs + if (item instanceof Ci.nsIMsgFolder) + { + // find corresponding tabinfos + // we may have the folder openened in more than one tab + let tabmail = GetTabMail(); + for (let i = 0; i < tabmail.tabInfo.length; ++i) + { + // if we never switched away from the tab, we only have just one + let tabFolder = tabmail.tabInfo[i].msgSelectedFolder || gMsgFolderSelected; + if (tabFolder == item) + { + // update tab title incl. any icon styles + tabmail.setTabTitle(tabmail.tabInfo[i]); + } + } + } + }, + + onFolderEvent: function(folder, event) { + if (event == "FolderLoaded") { + if (folder) { + var scrolled = false; + var msgFolder = folder.QueryInterface(Ci.nsIMsgFolder); + var uri = folder.URI; + var rerootingFolder = (uri == gCurrentFolderToReroot); + if (rerootingFolder) { + viewDebug("uri = gCurrentFolderToReroot, setting gQSViewIsDirty\n"); + gQSViewIsDirty = true; + gCurrentFolderToReroot = null; + if (msgFolder) { + msgFolder.endFolderLoading(); + // Suppress command updating when rerooting the folder. + // When rerooting, we'll be clearing the selection + // which will cause us to update commands. + if (gDBView) { + gDBView.suppressCommandUpdating = true; + // If the db's view isn't set, something went wrong and we + // should reroot the folder, which will re-open the view. + if (!gDBView.db) + gRerootOnFolderLoad = true; + } + if (gRerootOnFolderLoad) + RerootFolder(uri, msgFolder, gCurrentLoadingFolderViewType, gCurrentLoadingFolderViewFlags, gCurrentLoadingFolderSortType, gCurrentLoadingFolderSortOrder); + + if (gDBView) + gDBView.suppressCommandUpdating = false; + + gCurrentLoadingFolderSortType = 0; + gCurrentLoadingFolderSortOrder = 0; + gCurrentLoadingFolderViewType = 0; + gCurrentLoadingFolderViewFlags = 0; + + // Used for rename folder msg loading after folder is loaded. + scrolled = LoadCurrentlyDisplayedMessage(); + + if (gStartMsgKey != nsMsgKey_None) { + scrolled = SelectAndScrollToKey(gStartMsgKey); + gStartMsgKey = nsMsgKey_None; + } + + if (gNextMessageAfterLoad) { + var type = gNextMessageAfterLoad; + gNextMessageAfterLoad = null; + + // Scroll to and select the proper message. + scrolled = ScrollToMessage(type, true, true /* selectMessage */); + } + } + } + if (uri == gCurrentLoadingFolderURI) { + viewDebug("uri == current loading folder uri\n"); + gCurrentLoadingFolderURI = ""; + // Scroll to message for virtual folders is done in + // gSearchNotificationListener.OnSearchDone (see searchBar.js). + if (!scrolled && gMsgFolderSelected && + !(gMsgFolderSelected.flags & Ci.nsMsgFolderFlags.Virtual)) + ScrollToMessageAfterFolderLoad(msgFolder); + SetBusyCursor(window, false); + } + // Folder loading is over, + // now issue quick search if there is an email address. + if (gVirtualFolderTerms) + viewDebug("in folder loaded gVirtualFolderTerms = " + + gVirtualFolderTerms + "\n"); + if (gMsgFolderSelected) + viewDebug("in folder loaded gMsgFolderSelected = " + + gMsgFolderSelected.URI + "\n"); + if (rerootingFolder) + { + if (gSearchEmailAddress) + { + Search(gSearchEmailAddress); + gSearchEmailAddress = null; + } + else if (gVirtualFolderTerms) + { + gDefaultSearchViewTerms = null; + viewDebug("searching gVirtualFolderTerms\n"); + gDBView.viewFolder = gMsgFolderSelected; + ViewChangeByFolder(gMsgFolderSelected); + } + else if (gMsgFolderSelected && + gMsgFolderSelected.flags & Ci.nsMsgFolderFlags.Virtual) + { + viewDebug("selected folder is virtual\n"); + gDefaultSearchViewTerms = null; + } + else + { + // Get the view value from the folder. + if (msgFolder) + { + // If our new view is the same as the old view and we already + // have the list of search terms built up for the old view, + // just re-use it. + var result = GetMailViewForFolder(msgFolder); + if (GetSearchInput() && gCurrentViewValue == result && gDefaultSearchViewTerms) + { + viewDebug("searching gDefaultSearchViewTerms and rerootingFolder\n"); + Search(""); + } + else + { + viewDebug("changing view by value\n"); + ViewChangeByValue(result); + } + } + } + } + } + } + else if (event == "ImapHdrDownloaded") { + if (folder) { + var imapFolder = folder.QueryInterface(Ci.nsIMsgImapMailFolder); + if (imapFolder) { + var hdrParser = imapFolder.hdrParser; + if (hdrParser) { + var msgHdr = hdrParser.GetNewMsgHdr(); + if (msgHdr) + { + var hdrs = hdrParser.headers; + if (hdrs && hdrs.includes("X-attachment-size:")) { + msgHdr.OrFlags(Ci.nsMsgMessageFlags + .Attachment); + } + if (hdrs && hdrs.includes("X-image-size:")) { + msgHdr.setStringProperty("imageSize", "1"); + } + } + } + } + } + } + else if (event == "DeleteOrMoveMsgCompleted") { + HandleDeleteOrMoveMsgCompleted(folder); + } + else if (event == "DeleteOrMoveMsgFailed") { + HandleDeleteOrMoveMsgFailed(folder); + } + else if (event == "AboutToCompact") { + if (gDBView) + gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage; + } + else if (event == "CompactCompleted") { + HandleCompactCompleted(folder); + } + else if (event == "RenameCompleted") { + // Clear this so we don't try to clear its new messages. + gMsgFolderSelected = null; + gFolderTreeView.selectFolder(folder); + } + else if (event == "JunkStatusChanged") { + HandleJunkStatusChanged(folder); + } + } +} + +function HandleDeleteOrMoveMsgFailed(folder) +{ + gDBView.onDeleteCompleted(false); + if(IsCurrentLoadedFolder(folder)) { + if(gNextMessageAfterDelete) { + gNextMessageAfterDelete = null; + gNextMessageViewIndexAfterDelete = -2; + } + } + + // fix me??? + // ThreadPaneSelectionChange(true); +} + +// WARNING +// this is a fragile and complicated function. +// be careful when hacking on it. +// Don't forget about things like different imap +// delete models, multiple views (from multiple thread panes, +// search windows, stand alone message windows) +function HandleDeleteOrMoveMsgCompleted(folder) +{ + // you might not have a db view. this can happen if + // biff fires when the 3 pane is set to account central. + if (!gDBView) + return; + + gDBView.onDeleteCompleted(true); + + if (!IsCurrentLoadedFolder(folder)) { + // default value after delete/move/copy is over + gNextMessageViewIndexAfterDelete = -2; + return; + } + + var treeView = gDBView.QueryInterface(Ci.nsITreeView); + var treeSelection = treeView.selection; + + if (gNextMessageViewIndexAfterDelete == -2) { + // a move or delete can cause our selection can change underneath us. + // this can happen when the user + // deletes message from the stand alone msg window + // or the search view, or another 3 pane + if (treeSelection.count == 0) { + // this can happen if you double clicked a message + // in the thread pane, and deleted it from the stand alone msg window + // see bug #172392 + treeSelection.clearSelection(); + setTitleFromFolder(folder, null); + ClearMessagePane(); + UpdateMailToolbar("delete from another view, 0 rows now selected"); + } + else if (treeSelection.count == 1) { + // this can happen if you had two messages selected + // in the thread pane, and you deleted one of them from another view + // (like the view in the stand alone msg window) + // since one item is selected, we should load it. + var startIndex = {}; + var endIndex = {}; + treeSelection.getRangeAt(0, startIndex, endIndex); + + // select the selected item, so we'll load it + treeSelection.select(startIndex.value); + treeView.selectionChanged(); + + EnsureRowInThreadTreeIsVisible(startIndex.value); + + UpdateMailToolbar("delete from another view, 1 row now selected"); + } + else { + // this can happen if you have more than 2 messages selected + // in the thread pane, and you deleted one of them from another view + // (like the view in the stand alone msg window) + // since multiple messages are still selected, do nothing. + } + } + else { + if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None) + { + var viewSize = treeView.rowCount; + if (gNextMessageViewIndexAfterDelete >= viewSize) + { + if (viewSize > 0) + gNextMessageViewIndexAfterDelete = viewSize - 1; + else + { + gNextMessageViewIndexAfterDelete = nsMsgViewIndex_None; + + // there is nothing to select since viewSize is 0 + treeSelection.clearSelection(); + setTitleFromFolder(folder, null); + ClearMessagePane(); + UpdateMailToolbar("delete from current view, 0 rows left"); + } + } + } + + // if we are about to set the selection with a new element then DON'T clear + // the selection then add the next message to select. This just generates + // an extra round of command updating notifications that we are trying to + // optimize away. + if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None) + { + // When deleting a message we don't update the commands + // when the selection goes to 0 + // (we have a hack in nsMsgDBView which prevents that update) + // so there is no need to + // update commands when we select the next message after the delete; + // the commands already + // have the right update state... + gDBView.suppressCommandUpdating = true; + + // This check makes sure that the tree does not perform a + // selection on a non selected row (row < 0), else assertions will + // be thrown. + if (gNextMessageViewIndexAfterDelete >= 0) + treeSelection.select(gNextMessageViewIndexAfterDelete); + + // If gNextMessageViewIndexAfterDelete has the same value + // as the last index we had selected, the tree won't generate a + // selectionChanged notification for the tree view. So force a manual + // selection changed call. + // (don't worry it's cheap if we end up calling it twice). + if (treeView) + treeView.selectionChanged(); + + EnsureRowInThreadTreeIsVisible(gNextMessageViewIndexAfterDelete); + gDBView.suppressCommandUpdating = false; + + // hook for extra toolbar items + // XXX TODO + // I think there is a bug in the suppression code above. + // What if I have two rows selected, and I hit delete, + // and so we load the next row. + // What if I have commands that only enable where + // exactly one row is selected? + UpdateMailToolbar("delete from current view, at least one row selected"); + } + } + + // default value after delete/move/copy is over + gNextMessageViewIndexAfterDelete = -2; +} + +function HandleCompactCompleted(folder) +{ + if (folder && folder.server.type != "imap") + { + let msgFolder = msgWindow.openFolder; + if (msgFolder && folder.URI == msgFolder.URI) + { + // pretend the selection changed, to reselect the current folder+view. + gMsgFolderSelected = null; + msgWindow.openFolder = null; + FolderPaneSelectionChange(); + LoadCurrentlyDisplayedMessage(); + } + } +} + +function LoadCurrentlyDisplayedMessage() +{ + var scrolled = (gCurrentlyDisplayedMessage != nsMsgViewIndex_None); + if (scrolled) + { + var treeView = gDBView.QueryInterface(Ci.nsITreeView); + var treeSelection = treeView.selection; + treeSelection.select(gCurrentlyDisplayedMessage); + if (treeView) + treeView.selectionChanged(); + EnsureRowInThreadTreeIsVisible(gCurrentlyDisplayedMessage); + SetFocusThreadPane(); + gCurrentlyDisplayedMessage = nsMsgViewIndex_None; //reset + } + return scrolled; +} + +function IsCurrentLoadedFolder(aFolder) +{ + let msgFolderUri = aFolder.QueryInterface(Ci.nsIMsgFolder) + .URI; + let currentLoadedFolder = GetThreadPaneFolder(); + + // If the currently loaded folder is virtual, + // check if aFolder is one of its searched folders. + if (currentLoadedFolder.flags & Ci.nsMsgFolderFlags.Virtual) + { + return currentLoadedFolder.msgDatabase.dBFolderInfo + .getCharProperty("searchFolderUri").split("|") + .includes(msgFolderUri); + } + + // Is aFolder the currently loaded folder? + return currentLoadedFolder.URI == msgFolderUri; +} + +function ServerContainsFolder(server, folder) +{ + if (!folder || !server) + return false; + + return server.equals(folder.server); +} + +function SelectServer(server) +{ + gFolderTreeView.selectFolder(server.rootFolder); +} + +// we have this incoming server listener in case we need to +// alter the folder pane selection when a server is removed +// or changed (currently, when the real username or real hostname change) +var gThreePaneIncomingServerListener = { + onServerLoaded: function(server) {}, + onServerUnloaded: function(server) { + var selectedFolders = GetSelectedMsgFolders(); + for (var i = 0; i < selectedFolders.length; i++) { + if (ServerContainsFolder(server, selectedFolders[i])) { + if (accountManager.defaultAccount) + SelectServer(accountManager.defaultAccount.incomingServer); + // we've made a new selection, we're done + return; + } + } + + // if nothing is selected at this point, better go select the default + // this could happen if nothing was selected when the server was removed + selectedFolders = GetSelectedMsgFolders(); + if (selectedFolders.length == 0) { + if (accountManager.defaultAccount) + SelectServer(accountManager.defaultAccount.incomingServer); + } + }, + onServerChanged: function(server) { + // if the current selected folder is on the server that changed + // and that server is an imap or news server, + // we need to update the selection. + // on those server types, we'll be reconnecting to the server + // and our currently selected folder will need to be reloaded + // or worse, be invalid. + if (server.type != "imap" && server.type !="nntp") + return; + + var selectedFolders = GetSelectedMsgFolders(); + for (var i = 0; i < selectedFolders.length; i++) { + // if the selected item is a server, we don't have to update + // the selection + if (!(selectedFolders[i].isServer) && ServerContainsFolder(server, selectedFolders[i])) { + SelectServer(server); + // we've made a new selection, we're done + return; + } + } + } +} + +function UpdateMailPaneConfig() { + const dynamicIds = ["messagesBox", "mailContent", "messengerBox"]; + var desiredId = dynamicIds[Services.prefs.getIntPref("mail.pane_config.dynamic")]; + var messagePane = GetMessagePane(); + if (messagePane.parentNode.id != desiredId) { + ClearAttachmentList(); + var messagePaneSplitter = GetThreadAndMessagePaneSplitter(); + var desiredParent = document.getElementById(desiredId); + // See Bug 381992. The ctor for the browser element will fire again when we + // re-insert the messagePaneBox back into the document. + // But the dtor doesn't fire when the element is removed from the document. + // Manually call destroy here to avoid a nasty leak. + getMessageBrowser().destroy(); + desiredParent.appendChild(messagePaneSplitter); + desiredParent.appendChild(messagePane); + messagePaneSplitter.orient = desiredParent.orient; + // Reroot message display + InvalidateTabDBs(); + let tabmail = GetTabMail(); + tabmail.currentTabInfo = null; + tabmail.updateCurrentTab(); + } +} + +var MailPrefObserver = { + observe: function observe(subject, topic, prefName) { + if (topic == "nsPref:changed") { + if (prefName == "mail.pane_config.dynamic") { + UpdateMailPaneConfig(); + } else if (prefName == "mail.showCondensedAddresses") { + let currentDisplayNameVersion = + Services.prefs.getIntPref("mail.displayname.version"); + Services.prefs.setIntPref("mail.displayname.version", + ++currentDisplayNameVersion); + + // Refresh the thread pane. + GetThreadTree().treeBoxObject.invalid(); + } + } + } +}; + +/* Functions related to startup */ +function OnLoadMessenger() +{ + AddMailOfflineObserver(); + CreateMailWindowGlobals(); + Services.prefs.addObserver("mail.pane_config.dynamic", MailPrefObserver); + Services.prefs.addObserver("mail.showCondensedAddresses", MailPrefObserver); + UpdateMailPaneConfig(); + Create3PaneGlobals(); + verifyAccounts(null, false); + msgDBCacheManager.init(); + + // set the messenger default window size relative to the screen size + // initial default dimensions are 2/3 and 1/2 of the screen dimensions + if (!document.documentElement.hasAttribute("width")) { + let screenHeight = window.screen.availHeight; + let screenWidth = window.screen.availWidth; + let defaultHeight = Math.floor(screenHeight * 2 / 3); + let defaultWidth = Math.floor(screenWidth / 2); + + // minimum dimensions are 1024x768 less padding unless restrained by screen + const minHeight = 768; + const minWidth = 1024; + + if (defaultHeight < minHeight) + defaultHeight = Math.min(minHeight, screenHeight); + if (defaultWidth < minWidth) + defaultWidth = Math.min(minWidth, screenWidth); + + // keep some distance to the borders, accounting for window decoration + document.documentElement.setAttribute("height", defaultHeight - 48); + document.documentElement.setAttribute("width", defaultWidth - 24); + } + + // initialize tabmail system - see tabmail.js and tabmail.xml for details + let tabmail = GetTabMail(); + tabmail.registerTabType(gMailNewsTabsType); + tabmail.openFirstTab(); + Services.obs.addObserver(MailWindowIsClosing, + "quit-application-requested"); + + InitMsgWindow(); + messenger.setWindow(window, msgWindow); + + InitPanes(); + + MigrateJunkMailSettings(); + + accountManager.setSpecialFolders(); + accountManager.loadVirtualFolders(); + accountManager.addIncomingServerListener(gThreePaneIncomingServerListener); + + AddToSession(); + + var startFolderUri = null; + //need to add to session before trying to load start folder otherwise listeners aren't + //set up correctly. + // argument[0] --> folder uri + // argument[1] --> optional message key + // argument[2] --> optional email address; // Will come from aim; needs to show msgs from buddy's email address. + if ("arguments" in window) + { + var args = window.arguments; + // filter our any feed urls that came in as arguments to the new window... + if (args.length && /^feed:/i.test(args[0])) + { + var feedHandler = + Cc["@mozilla.org/newsblog-feed-downloader;1"] + .getService(Ci.nsINewsBlogFeedDownloader); + if (feedHandler) + feedHandler.subscribeToFeed(args[0], null, msgWindow); + } + else + { + startFolderUri = (args.length > 0) ? args[0] : null; + } + gStartMsgKey = (args.length > 1) ? args[1] : nsMsgKey_None; + gSearchEmailAddress = (args.length > 2) ? args[2] : null; + } + + window.setTimeout(loadStartFolder, 0, startFolderUri); + + Services.obs.notifyObservers(window, "mail-startup-done"); + + // FIX ME - later we will be able to use onload from the overlay + OnLoadMsgHeaderPane(); + + gHaveLoadedMessage = false; + + //Set focus to the Thread Pane the first time the window is opened. + SetFocusThreadPane(); + + // Before and after callbacks for the customizeToolbar code + var mailToolbox = getMailToolbox(); + mailToolbox.customizeInit = MailToolboxCustomizeInit; + mailToolbox.customizeDone = MailToolboxCustomizeDone; + mailToolbox.customizeChange = MailToolboxCustomizeChange; + + // initialize the sync UI + // gSyncUI.init(); + + window.addEventListener("AppCommand", HandleAppCommandEvent, true); + + // Load the periodic filter timer. + PeriodicFilterManager.setupFiltering(); +} + +function HandleAppCommandEvent(evt) +{ + evt.stopPropagation(); + switch (evt.command) + { + case "Back": + goDoCommand('cmd_goBack'); + break; + case "Forward": + goDoCommand('cmd_goForward'); + break; + case "Stop": + goDoCommand('cmd_stop'); + break; + case "Search": + goDoCommand('cmd_search'); + break; + case "Bookmarks": + toAddressBook(); + break; + case "Reload": + goDoCommand('cmd_reload'); + break; + case "Home": + goDoCommand('cmd_goStartPage'); + break; + default: + break; + } +} + +function OnUnloadMessenger() +{ + Services.prefs.removeObserver("mail.pane_config.dynamic", MailPrefObserver, false); + Services.prefs.removeObserver("mail.showCondensedAddresses", MailPrefObserver, false); + window.removeEventListener("AppCommand", HandleAppCommandEvent, true); + Services.obs.removeObserver(MailWindowIsClosing, + "quit-application-requested"); + + OnLeavingFolder(gMsgFolderSelected); // mark all read in current folder + accountManager.removeIncomingServerListener(gThreePaneIncomingServerListener); + GetTabMail().closeTabs(); + + // FIX ME - later we will be able to use onload from the overlay + OnUnloadMsgHeaderPane(); + UnloadPanes(); + OnMailWindowUnload(); +} + +// we probably want to warn if more than one tab is closed +function MailWindowIsClosing(aCancelQuit, aTopic, aData) +{ + if (aTopic == "quit-application-requested" && + aCancelQuit instanceof Ci.nsISupportsPRBool && + aCancelQuit.data) + return false; + + let tabmail = GetTabMail(); + let reallyClose = tabmail.warnAboutClosingTabs(tabmail.closingTabsEnum.ALL); + + if (!reallyClose && aTopic == "quit-application-requested") + aCancelQuit.data = true; + + return reallyClose; +} + +function Create3PaneGlobals() +{ + // Update <mailWindow.js> global variables. + accountCentralBox = document.getElementById("accountCentralBox"); + gDisableViewsSearch = document.getElementById("mailDisableViewsSearch"); + + GetMessagePane().collapsed = true; +} + +function loadStartFolder(initialUri) +{ + var defaultServer = null; + var startFolder; + var isLoginAtStartUpEnabled = false; + + //First get default account + if (initialUri) { + startFolder = MailUtils.getFolderForURI(initialUri); + } else { + var defaultAccount = accountManager.defaultAccount; + if (defaultAccount) { + defaultServer = defaultAccount.incomingServer; + var rootMsgFolder = defaultServer.rootMsgFolder; + + startFolder = rootMsgFolder; + // Enable check new mail once by turning checkmail pref 'on' to bring + // all users to one plane. This allows all users to go to Inbox. User can + // always go to server settings panel and turn off "Check for new mail at startup" + if (!Services.prefs.getBoolPref(kMailCheckOncePrefName)) + { + Services.prefs.setBoolPref(kMailCheckOncePrefName, true); + defaultServer.loginAtStartUp = true; + } + + // Get the user pref to see if the login at startup is enabled for default account + isLoginAtStartUpEnabled = defaultServer.loginAtStartUp; + + // Get Inbox only if login at startup is enabled. + if (isLoginAtStartUpEnabled) + { + //now find Inbox + const kInboxFlag = Ci.nsMsgFolderFlags.Inbox; + var inboxFolder = rootMsgFolder.getFolderWithFlags(kInboxFlag); + if (inboxFolder) + startFolder = inboxFolder; + } + } else { + // If no default account then show account central page. + ShowAccountCentral(); + } + + } + + if (startFolder) { + try { + gFolderTreeView.selectFolder(startFolder); + } catch(ex) { + // This means we tried to select a folder that isn't in the current + // view. Just select the first one in the view then. + if (gFolderTreeView._rowMap.length) + gFolderTreeView.selectFolder(gFolderTreeView._rowMap[0]._folder); + } + + // Perform biff on the server to check for new mail, if: + // the login at startup is enabled, and + // this feature is not exceptionally overridden, and + // the account is not deferred-to or deferred. + if (isLoginAtStartUpEnabled && + gLoadStartFolder && + !defaultServer.isDeferredTo && + defaultServer.rootFolder == defaultServer.rootMsgFolder) + defaultServer.performBiff(msgWindow); + } + + MsgGetMessagesForAllServers(defaultServer); + + if (CheckForUnsentMessages() && !Services.io.offline) + { + InitPrompts(); + InitServices(); + + var sendUnsentWhenGoingOnlinePref = Services.prefs.getIntPref("offline.send.unsent_messages"); + if (sendUnsentWhenGoingOnlinePref == 0) // pref is "ask" + { + var buttonPressed = Services.prompt.confirmEx(window, + gOfflinePromptsBundle.getString('sendMessagesOfflineWindowTitle'), + gOfflinePromptsBundle.getString('sendMessagesLabel2'), + Services.prompt.BUTTON_TITLE_IS_STRING * (Services.prompt.BUTTON_POS_0 + + Services.prompt.BUTTON_POS_1), + gOfflinePromptsBundle.getString('sendMessagesSendButtonLabel'), + gOfflinePromptsBundle.getString('sendMessagesNoSendButtonLabel'), + null, null, {value:0}); + if (buttonPressed == 0) + SendUnsentMessages(); + } + else if(sendUnsentWhenGoingOnlinePref == 1) // pref is "yes" + SendUnsentMessages(); + } +} + +function AddToSession() +{ + var nsIFolderListener = Ci.nsIFolderListener; + var notifyFlags = nsIFolderListener.intPropertyChanged | + nsIFolderListener.event; + MailServices.mailSession.AddFolderListener(folderListener, notifyFlags); +} + +function InitPanes() +{ + gFolderTreeView.load(document.getElementById("folderTree"), + "folderTree.json"); + var folderTree = document.getElementById("folderTree"); + folderTree.addEventListener("click", FolderPaneOnClick, true); + folderTree.addEventListener("mousedown", TreeOnMouseDown, true); + + OnLoadThreadPane(); + SetupCommandUpdateHandlers(); +} + +function UnloadPanes() +{ + var folderTree = document.getElementById("folderTree"); + folderTree.removeEventListener("click", FolderPaneOnClick, true); + folderTree.removeEventListener("mousedown", TreeOnMouseDown, true); + gFolderTreeView.unload("folderTree.json"); + UnloadCommandUpdateHandlers(); +} + +function AddMutationObserver(callback) +{ + new MutationObserver(callback).observe(callback(), {attributes: true, attributeFilter: ["hidden"]}); +} + +function OnLoadThreadPane() +{ + AddMutationObserver(UpdateAttachmentCol); +} + +function UpdateAttachmentCol() +{ + var attachmentCol = document.getElementById("attachmentCol"); + var threadTree = GetThreadTree(); + threadTree.setAttribute("noattachcol", attachmentCol.getAttribute("hidden")); + threadTree.treeBoxObject.clearStyleAndImageCaches(); + return attachmentCol; +} + +function GetSearchInput() +{ + if (!gSearchInput) + gSearchInput = document.getElementById("searchInput"); + return gSearchInput; +} + +function GetMessagePaneFrame() +{ + return window.content; +} + +function FindInSidebar(currentWindow, id) +{ + var item = currentWindow.document.getElementById(id); + if (item) + return item; + + for (var i = 0; i < currentWindow.frames.length; ++i) + { + var frameItem = FindInSidebar(currentWindow.frames[i], id); + if (frameItem) + return frameItem; + } + + return null; +} + +function GetUnreadCountElement() +{ + if (!gUnreadCount) + gUnreadCount = document.getElementById('unreadMessageCount'); + return gUnreadCount; +} + +function GetTotalCountElement() +{ + if (!gTotalCount) + gTotalCount = document.getElementById('totalMessageCount'); + return gTotalCount; +} + +function ClearThreadPaneSelection() +{ + try { + if (gDBView) { + var treeView = gDBView.QueryInterface(Ci.nsITreeView); + var treeSelection = treeView.selection; + if (treeSelection) + treeSelection.clearSelection(); + } + } + catch (ex) { + dump("ClearThreadPaneSelection: ex = " + ex + "\n"); + } +} + +function ClearMessagePane() +{ + if (gHaveLoadedMessage) + { + gHaveLoadedMessage = false; + gCurrentDisplayedMessage = null; + if (GetMessagePaneFrame().location.href != "about:blank") + GetMessagePaneFrame().location.href = "about:blank"; + + // hide the message header view AND the message pane... + HideMessageHeaderPane(); + gMessageNotificationBar.clearMsgNotifications(); + ClearPendingReadTimer(); + } +} + +// Function to change the highlighted row to where the mouse was clicked +// without loading the contents of the selected row. +// It will also keep the outline/dotted line in the original row. +function ChangeSelectionWithoutContentLoad(event, tree) +{ + // usually, we're only interested in tree content clicks, not scrollbars etc. + if (event.originalTarget.localName != "treechildren") + return; + + var treeBoxObj = tree.treeBoxObject; + var treeSelection = tree.view.selection; + + var row = treeBoxObj.getRowAt(event.clientX, event.clientY); + // make sure that row.value is valid so that it doesn't mess up + // the call to ensureRowIsVisible(). + if ((row >= 0) && !treeSelection.isSelected(row)) + { + var saveCurrentIndex = treeSelection.currentIndex; + treeSelection.selectEventsSuppressed = true; + treeSelection.select(row); + treeSelection.currentIndex = saveCurrentIndex; + treeBoxObj.ensureRowIsVisible(row); + treeSelection.selectEventsSuppressed = false; + + // Keep track of which row in the thread pane is currently selected. + if (tree.id == "threadTree") + gThreadPaneCurrentSelectedIndex = row; + } + event.stopPropagation(); +} + +function TreeOnMouseDown(event) +{ + // Detect right mouse click and change the highlight to the row + // where the click happened without loading the message headers in + // the Folder or Thread Pane. + // Same for middle click, which will open the folder/message in a tab. + gRightMouseButtonDown = event.button == kMouseButtonRight; + if (!gRightMouseButtonDown) + gRightMouseButtonDown = AllowOpenTabOnMiddleClick() && + event.button == kMouseButtonMiddle; + if (gRightMouseButtonDown) + ChangeSelectionWithoutContentLoad(event, event.target.parentNode); +} + +function FolderPaneContextMenuNewTab(event) { + var bgLoad = Services.prefs.getBoolPref("mail.tabs.loadInBackground"); + if (event.shiftKey) + bgLoad = !bgLoad; + MsgOpenNewTabForFolder(bgLoad); +} + +function FolderPaneOnClick(event) +{ + // usually, we're only interested in tree content clicks, not scrollbars etc. + if (event.originalTarget.localName != "treechildren") + return; + + var folderTree = document.getElementById("folderTree"); + + // we may want to open the folder in a new tab on middle click + if (event.button == kMouseButtonMiddle) + { + if (AllowOpenTabOnMiddleClick()) + { + FolderPaneContextMenuNewTab(event); + RestoreSelectionWithoutContentLoad(folderTree); + return; + } + } + + // otherwise, we only care about left click events + if (event.button != kMouseButtonLeft) + return; + + var cell = folderTree.treeBoxObject.getCellAt(event.clientX, event.clientY); + if (cell.row == -1) + { + if (event.originalTarget.localName == "treecol") + { + // clicking on the name column in the folder pane should not sort + event.stopPropagation(); + } + } +} + +function OpenMessageInNewTab(event) { + var bgLoad = Services.prefs.getBoolPref("mail.tabs.loadInBackground"); + if (event.shiftKey) + bgLoad = !bgLoad; + + MsgOpenNewTabForMessage(bgLoad); +} + +function GetSelectedMsgFolders() +{ + return gFolderTreeView.getSelectedFolders(); +} + +function GetSelectedIndices(dbView) +{ + try { + return dbView.getIndicesForSelection(); + } + catch (ex) { + dump("ex = " + ex + "\n"); + return null; + } +} + +function GetLoadedMsgFolder() +{ + if (!gDBView) return null; + return gDBView.msgFolder; +} + +function GetLoadedMessage() +{ + try { + return gDBView.URIForFirstSelectedMessage; + } + catch (ex) { + return null; + } +} + +//Clear everything related to the current message. called after load start page. +function ClearMessageSelection() +{ + ClearThreadPaneSelection(); +} + +// Figures out how many messages are selected (hilighted - does not necessarily +// have the dotted outline) above a given index row value in the thread pane. +function NumberOfSelectedMessagesAboveCurrentIndex(index) +{ + var numberOfMessages = 0; + var indicies = GetSelectedIndices(gDBView); + + if (indicies && indicies.length) + { + for (var i = 0; i < indicies.length; i++) + { + if (indicies[i] < index) + ++numberOfMessages; + else + break; + } + } + return numberOfMessages; +} + +function SetNextMessageAfterDelete() +{ + var treeSelection = GetThreadTree().view.selection; + + if (treeSelection.isSelected(treeSelection.currentIndex)) + gNextMessageViewIndexAfterDelete = gDBView.msgToSelectAfterDelete; + else if(gDBView.removeRowOnMoveOrDelete) + { + // Only set gThreadPaneDeleteOrMoveOccurred to true if the message was + // truly moved to the trash or deleted, as opposed to an IMAP delete + // (where it is only "marked as deleted". This will prevent bug 142065. + // + // If it's an IMAP delete, then just set gNextMessageViewIndexAfterDelete + // to treeSelection.currentIndex (where the outline is at) because nothing + // was moved or deleted from the folder. + gThreadPaneDeleteOrMoveOccurred = true; + gNextMessageViewIndexAfterDelete = treeSelection.currentIndex - NumberOfSelectedMessagesAboveCurrentIndex(treeSelection.currentIndex); + } + else + gNextMessageViewIndexAfterDelete = treeSelection.currentIndex; +} + +function EnsureFolderIndex(treeView, msgFolder) { + // Try to get the index of the folder in the tree. + let index = treeView.getIndexOfFolder(msgFolder); + if (!index) { + // If we couldn't find the folder, open the parents. + let folder = msgFolder; + while (!index && folder) { + folder = folder.parent; + index = EnsureFolderIndex(treeView, folder); + } + if (index) { + treeView.toggleOpenState(index); + index = treeView.getIndexOfFolder(msgFolder); + } + } + return index; +} + +function SelectMsgFolder(msgFolder) { + gFolderTreeView.selectFolder(msgFolder); +} + +function SelectMessage(messageUri) +{ + var msgHdr = messenger.msgHdrFromURI(messageUri); + if (msgHdr) + gDBView.selectMsgByKey(msgHdr.messageKey); +} + +function ReloadMessage() +{ + gDBView.reloadMessage(); +} + +function SetBusyCursor(window, enable) +{ + // setCursor() is only available for chrome windows. + // However one of our frames is the start page which + // is a non-chrome window, so check if this window has a + // setCursor method + if ("setCursor" in window) { + if (enable) + window.setCursor("progress"); + else + window.setCursor("auto"); + } + + var numFrames = window.frames.length; + for(var i = 0; i < numFrames; i++) + SetBusyCursor(window.frames[i], enable); +} + +function GetDBView() +{ + return gDBView; +} + +// Some of the per account junk mail settings have been +// converted to global prefs. Let's try to migrate some +// of those settings from the default account. +function MigrateJunkMailSettings() +{ + var junkMailSettingsVersion = Services.prefs.getIntPref("mail.spam.version"); + if (!junkMailSettingsVersion) + { + // Get the default account, check to see if we have values for our + // globally migrated prefs. + var defaultAccount = accountManager.defaultAccount; + if (defaultAccount) + { + // we only care about + var prefix = "mail.server." + defaultAccount.incomingServer.key + "."; + if (Services.prefs.prefHasUserValue(prefix + "manualMark")) + Services.prefs.setBoolPref("mail.spam.manualMark", Services.prefs.getBoolPref(prefix + "manualMark")); + if (Services.prefs.prefHasUserValue(prefix + "manualMarkMode")) + Services.prefs.setIntPref("mail.spam.manualMarkMode", Services.prefs.getIntPref(prefix + "manualMarkMode")); + if (Services.prefs.prefHasUserValue(prefix + "spamLoggingEnabled")) + Services.prefs.setBoolPref("mail.spam.logging.enabled", Services.prefs.getBoolPref(prefix + "spamLoggingEnabled")); + if (Services.prefs.prefHasUserValue(prefix + "markAsReadOnSpam")) + Services.prefs.setBoolPref("mail.spam.markAsReadOnSpam", Services.prefs.getBoolPref(prefix + "markAsReadOnSpam")); + } + // bump the version so we don't bother doing this again. + Services.prefs.setIntPref("mail.spam.version", 1); + } +} |