summaryrefslogtreecommitdiffstats
path: root/comm/mail/base/content/mail3PaneWindowCommands.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/base/content/mail3PaneWindowCommands.js')
-rw-r--r--comm/mail/base/content/mail3PaneWindowCommands.js456
1 files changed, 456 insertions, 0 deletions
diff --git a/comm/mail/base/content/mail3PaneWindowCommands.js b/comm/mail/base/content/mail3PaneWindowCommands.js
new file mode 100644
index 0000000000..8042022dcb
--- /dev/null
+++ b/comm/mail/base/content/mail3PaneWindowCommands.js
@@ -0,0 +1,456 @@
+/* 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/. */
+
+/**
+ * Functionality for the main application window (aka the 3pane) usually
+ * consisting of folder pane, thread pane and message pane.
+ */
+
+/* global MozElements */
+
+/* import-globals-from ../../components/im/content/chat-messenger.js */
+/* import-globals-from mailCore.js */
+/* import-globals-from mailWindow.js */ // msgWindow and a loooot more
+/* import-globals-from utilityOverlay.js */
+
+/* globals MailOfflineMgr */ // From mail-offline.js
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+var { PluralForm } = ChromeUtils.importESModule(
+ "resource://gre/modules/PluralForm.sys.mjs"
+);
+ChromeUtils.defineModuleGetter(
+ this,
+ "MailUtils",
+ "resource:///modules/MailUtils.jsm"
+);
+
+// DefaultController object (handles commands when one of the trees does not have focus)
+var DefaultController = {
+ supportsCommand(command) {
+ switch (command) {
+ case "cmd_newMessage":
+ case "cmd_undoCloseTab":
+ case "cmd_undo":
+ case "cmd_redo":
+ case "cmd_sendUnsentMsgs":
+ case "cmd_subscribe":
+ case "cmd_getNewMessages":
+ case "cmd_getMsgsForAuthAccounts":
+ case "cmd_getNextNMessages":
+ case "cmd_settingsOffline":
+ case "cmd_viewAllHeader":
+ case "cmd_viewNormalHeader":
+ case "cmd_stop":
+ case "cmd_chat":
+ case "cmd_goFolder":
+ return true;
+ case "cmd_synchronizeOffline":
+ return MailOfflineMgr.isOnline();
+ case "cmd_joinChat":
+ case "cmd_addChatBuddy":
+ case "cmd_chatStatus":
+ return !!chatHandler;
+
+ default:
+ return false;
+ }
+ },
+
+ isCommandEnabled(command) {
+ if (document.getElementById("tabmail").globalOverlay) {
+ return false;
+ }
+ switch (command) {
+ case "cmd_newMessage":
+ return MailServices.accounts.allIdentities.length > 0;
+ case "cmd_viewAllHeader":
+ case "cmd_viewNormalHeader":
+ return true;
+ case "cmd_undoCloseTab":
+ return document.getElementById("tabmail").recentlyClosedTabs.length > 0;
+ case "cmd_stop":
+ return window.MsgStatusFeedback?._meteorsSpinning;
+ case "cmd_undo":
+ case "cmd_redo":
+ return SetupUndoRedoCommand(command);
+ case "cmd_sendUnsentMsgs":
+ return IsSendUnsentMsgsEnabled(null);
+ case "cmd_subscribe":
+ return IsSubscribeEnabled();
+ case "cmd_getNewMessages":
+ case "cmd_getMsgsForAuthAccounts":
+ return IsGetNewMessagesEnabled();
+ case "cmd_getNextNMessages":
+ return IsGetNextNMessagesEnabled();
+ case "cmd_synchronizeOffline":
+ return MailOfflineMgr.isOnline();
+ case "cmd_settingsOffline":
+ return IsAccountOfflineEnabled();
+ case "cmd_goFolder":
+ return isFolderPaneInitialized();
+ case "cmd_chat":
+ return true;
+ case "cmd_joinChat":
+ case "cmd_addChatBuddy":
+ case "cmd_chatStatus":
+ return !!chatHandler;
+ }
+ return false;
+ },
+
+ doCommand(command, event) {
+ // If the user invoked a key short cut then it is possible that we got here
+ // for a command which is really disabled. Kick out if the command should be disabled.
+ if (!this.isCommandEnabled(command)) {
+ return;
+ }
+
+ switch (command) {
+ case "cmd_getNewMessages":
+ MsgGetMessage();
+ break;
+ case "cmd_getMsgsForAuthAccounts":
+ MsgGetMessagesForAllAuthenticatedAccounts();
+ break;
+ case "cmd_getNextNMessages":
+ MsgGetNextNMessages();
+ break;
+ case "cmd_newMessage":
+ MsgNewMessage(event);
+ break;
+ case "cmd_undoCloseTab":
+ document.getElementById("tabmail").undoCloseTab();
+ break;
+ case "cmd_undo":
+ messenger.undo(msgWindow);
+ break;
+ case "cmd_redo":
+ messenger.redo(msgWindow);
+ break;
+ case "cmd_sendUnsentMsgs":
+ // if offline, prompt for sendUnsentMessages
+ if (MailOfflineMgr.isOnline()) {
+ SendUnsentMessages();
+ } else {
+ MailOfflineMgr.goOnlineToSendMessages(msgWindow);
+ }
+ return;
+ case "cmd_subscribe":
+ MsgSubscribe();
+ return;
+ case "cmd_stop":
+ msgWindow.StopUrls();
+ return;
+ case "cmd_viewAllHeader":
+ MsgViewAllHeaders();
+ return;
+ case "cmd_viewNormalHeader":
+ MsgViewNormalHeaders();
+ return;
+ case "cmd_synchronizeOffline":
+ MsgSynchronizeOffline();
+ break;
+ case "cmd_settingsOffline":
+ MailOfflineMgr.openOfflineAccountSettings();
+ break;
+ case "cmd_goFolder":
+ document
+ .getElementById("tabmail")
+ .currentAbout3Pane.displayFolder(event.target._folder);
+ break;
+ case "cmd_chat":
+ showChatTab();
+ break;
+ }
+ },
+
+ onEvent(event) {
+ // on blur events set the menu item texts back to the normal values
+ if (event == "blur") {
+ goSetMenuValue("cmd_undo", "valueDefault");
+ goSetMenuValue("cmd_redo", "valueDefault");
+ }
+ },
+};
+// This is the highest priority controller. It's followed by
+// tabmail.tabController and calendarController, then whatever Gecko adds.
+window.controllers.insertControllerAt(0, DefaultController);
+
+function CloseTabOrWindow() {
+ let tabmail = document.getElementById("tabmail");
+ if (tabmail.globalOverlay) {
+ return;
+ }
+ if (tabmail.tabInfo.length == 1) {
+ if (Services.prefs.getBoolPref("mail.tabs.closeWindowWithLastTab")) {
+ window.close();
+ }
+ } else {
+ tabmail.removeCurrentTab();
+ }
+}
+
+function IsSendUnsentMsgsEnabled(unsentMsgsFolder) {
+ // If no account has been configured, there are no messages for sending.
+ if (MailServices.accounts.accounts.length == 0) {
+ return false;
+ }
+
+ let msgSendlater;
+ try {
+ msgSendlater = Cc["@mozilla.org/messengercompose/sendlater;1"].getService(
+ Ci.nsIMsgSendLater
+ );
+ } catch (error) {}
+
+ // If we're currently sending unsent msgs, disable this cmd.
+ if (msgSendlater?.sendingMessages) {
+ return false;
+ }
+
+ if (unsentMsgsFolder) {
+ // If unsentMsgsFolder is non-null, it is the "Unsent Messages" folder.
+ // We're here because we've done a right click on the "Unsent Messages"
+ // folder (context menu), so we can use the folder and return true/false
+ // straight away.
+ return unsentMsgsFolder.getTotalMessages(false) > 0;
+ }
+
+ // Otherwise, we don't know where we are, so use the current identity and
+ // find out if we have messages or not via that.
+ let identity;
+ let folders = GetSelectedMsgFolders();
+ if (folders.length > 0) {
+ [identity] = MailUtils.getIdentityForServer(folders[0].server);
+ }
+
+ if (!identity) {
+ let defaultAccount = MailServices.accounts.defaultAccount;
+ if (defaultAccount) {
+ identity = defaultAccount.defaultIdentity;
+ }
+
+ if (!identity) {
+ return false;
+ }
+ }
+
+ let hasUnsentMessages = false;
+ try {
+ hasUnsentMessages = msgSendlater?.hasUnsentMessages(identity);
+ } catch (error) {}
+ return hasUnsentMessages;
+}
+
+/**
+ * Determine whether there exists any server for which to show the Subscribe dialog.
+ */
+function IsSubscribeEnabled() {
+ // If there are any IMAP or News servers, we can show the dialog any time and
+ // it will properly show those.
+ for (let server of MailServices.accounts.allServers) {
+ if (server.type == "imap" || server.type == "nntp") {
+ return true;
+ }
+ }
+
+ // RSS accounts use a separate Subscribe dialog that we can only show when
+ // such an account is selected.
+ let preselectedFolder = GetFirstSelectedMsgFolder();
+ if (preselectedFolder && preselectedFolder.server.type == "rss") {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Cycle through the various panes in the 3pane window.
+ *
+ * @param {Event} event - The keypress DOMEvent.
+ */
+function SwitchPaneFocus(event) {
+ let tabmail = document.getElementById("tabmail");
+ // Should not move the focus around when the entire window is covered with
+ // something else.
+ if (tabmail.globalOverlay) {
+ return;
+ }
+ // First, build an array of panes to cycle through based on our current state.
+ // This will usually be something like [folderTree, threadTree, messageBrowser].
+ let panes = [];
+ // The logically focused element. If the actually focused element is not one
+ // of the panes, the code below can change this variable to point to one of
+ // the panes.
+ let focusedElement = document.activeElement;
+ // If the actually focused element is between two of the panes, set this to
+ // -1, 0, or 1 (depending on the direction and where the focus is relative to
+ // `focusedElement`) so that the element to focus is correctly chosen.
+ let adjustment = 0;
+
+ let spacesElement = !gSpacesToolbar.isHidden
+ ? gSpacesToolbar.focusButton
+ : document.getElementById("spacesPinnedButton");
+ panes.push(spacesElement);
+
+ let toolbar = document.getElementById("unifiedToolbar");
+ if (!toolbar.hidden) {
+ // Prioritise the search bar, otherwise use the first available button.
+ let toolbarElement =
+ toolbar.querySelector("global-search-bar") ||
+ toolbar.querySelector("li:not([hidden]) button, #button-appmenu");
+ if (toolbarElement) {
+ panes.push(toolbarElement);
+ if (toolbar.matches(":focus-within") && focusedElement != spacesElement) {
+ focusedElement = toolbarElement;
+ }
+ }
+ }
+
+ let { currentTabInfo } = tabmail;
+ switch (currentTabInfo.mode.name) {
+ case "mail3PaneTab": {
+ let { contentWindow, contentDocument } = currentTabInfo.chromeBrowser;
+ let {
+ paneLayout,
+ folderTree,
+ threadTree,
+ webBrowser,
+ messageBrowser,
+ multiMessageBrowser,
+ accountCentralBrowser,
+ } = contentWindow;
+
+ if (paneLayout.folderPaneVisible) {
+ panes.push(folderTree);
+ }
+
+ if (accountCentralBrowser.hidden) {
+ panes.push(threadTree.table.body);
+ } else {
+ panes.push(accountCentralBrowser);
+ }
+
+ if (paneLayout.messagePaneVisible) {
+ if (!webBrowser.hidden) {
+ panes.push(webBrowser);
+ } else if (!messageBrowser.hidden) {
+ panes.push(messageBrowser.contentWindow.getMessagePaneBrowser());
+ } else if (!multiMessageBrowser.hidden) {
+ panes.push(multiMessageBrowser);
+ }
+ }
+
+ if (focusedElement == currentTabInfo.chromeBrowser) {
+ focusedElement = contentDocument.activeElement;
+ if (
+ focusedElement != folderTree &&
+ contentDocument.getElementById("folderPane").contains(focusedElement)
+ ) {
+ focusedElement = folderTree;
+ adjustment = event.shiftKey ? 0 : -1;
+ } else if (
+ contentDocument
+ .getElementById("threadPaneNotificationBox")
+ .contains(focusedElement)
+ ) {
+ focusedElement = threadTree.table.body;
+ adjustment = event.shiftKey ? 1 : 0;
+ } else if (
+ focusedElement != threadTree.table.body &&
+ contentDocument.getElementById("threadPane").contains(focusedElement)
+ ) {
+ focusedElement = threadTree.table.body;
+ adjustment = event.shiftKey ? 0 : -1;
+ } else if (focusedElement == messageBrowser) {
+ focusedElement = messageBrowser.contentWindow.getMessagePaneBrowser();
+ }
+ }
+ break;
+ }
+ case "mailMessageTab": {
+ let { content } = currentTabInfo.chromeBrowser.contentWindow;
+ panes.push(content);
+ if (focusedElement == currentTabInfo.chromeBrowser) {
+ focusedElement = content;
+ }
+ break;
+ }
+ case "addressBookTab": {
+ let { booksList, cardsPane, detailsPane } =
+ currentTabInfo.browser.contentWindow;
+
+ if (detailsPane.isEditing) {
+ panes.push(currentTabInfo.browser);
+ } else {
+ let targets = [
+ booksList,
+ cardsPane.searchInput,
+ cardsPane.cardsList.table.body,
+ ];
+ if (!detailsPane.node.hidden && !detailsPane.editButton.hidden) {
+ targets.push(detailsPane.editButton);
+ }
+
+ if (focusedElement == currentTabInfo.browser) {
+ focusedElement = targets.find(t => t.matches(":focus-within"));
+ }
+ panes.push(...targets);
+ }
+ break;
+ }
+ default:
+ if (currentTabInfo.browser) {
+ panes.push(currentTabInfo.browser);
+ }
+ break;
+ }
+
+ // Find our focused element in the array.
+ let focusedElementIndex = panes.indexOf(focusedElement) + adjustment;
+ if (event.shiftKey) {
+ focusedElementIndex--;
+ if (focusedElementIndex < 0) {
+ focusedElementIndex = panes.length - 1;
+ }
+ } else if (focusedElementIndex == -1) {
+ focusedElementIndex = 0;
+ } else {
+ focusedElementIndex++;
+ if (focusedElementIndex == panes.length) {
+ focusedElementIndex = 0;
+ }
+ }
+
+ if (panes[focusedElementIndex]) {
+ panes[focusedElementIndex].focus();
+ }
+}
+
+// Override F6 handling for remote browsers, and use our own logic to
+// determine the element to focus.
+addEventListener(
+ "keypress",
+ function (event) {
+ if (event.key == "F6" && Services.focus.focusedElement?.isRemoteBrowser) {
+ event.preventDefault();
+ SwitchPaneFocus(event);
+ }
+ },
+ true
+);
+
+/**
+ * Check the status of the folder pane, if available.
+ *
+ * @returns {boolean|undefined} The initialization state of the folder pane,
+ * or undefined if we can't access the document.
+ */
+function isFolderPaneInitialized() {
+ return document.getElementById("tabmail")?.currentAbout3Pane?.folderPane
+ .isInitialized;
+}