From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/mail/base/content/mail3PaneWindowCommands.js | 456 ++++++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 comm/mail/base/content/mail3PaneWindowCommands.js (limited to 'comm/mail/base/content/mail3PaneWindowCommands.js') 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; +} -- cgit v1.2.3