diff options
Diffstat (limited to 'comm/mail/components/customizableui/content')
5 files changed, 1629 insertions, 0 deletions
diff --git a/comm/mail/components/customizableui/content/customizeMode.inc.xhtml b/comm/mail/components/customizableui/content/customizeMode.inc.xhtml new file mode 100644 index 0000000000..fc7eb0595b --- /dev/null +++ b/comm/mail/components/customizableui/content/customizeMode.inc.xhtml @@ -0,0 +1,128 @@ +<!-- 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/. --> + +<box id="customization-content-container"> +<box flex="1" id="customization-palette-container"> + <label id="customization-header" data-l10n-id="customize-mode-menu-and-toolbars-header"></label> + <vbox id="customization-palette" class="customization-palette" hidden="true"/> + <vbox id="customization-pong-arena" hidden="true"/> + <spacer id="customization-spacer"/> +</box> +<vbox id="customization-panel-container"> + <vbox id="customization-panelWrapper"> + <box class="panel-arrowbox"> + <image class="panel-arrow" side="top"/> + </box> + <box class="panel-arrowcontent" side="top" flex="1"> + <vbox id="customization-panelHolder"> + <description id="customization-panelHeader" data-l10n-id="customize-mode-overflow-list-title"></description> + <description id="customization-panelDescription" data-l10n-id="customize-mode-overflow-list-description"></description> + </vbox> + <box class="panel-inner-arrowcontentfooter" hidden="true"/> + </box> + </vbox> +</vbox> +</box> +<hbox id="customization-footer"> +<checkbox id="customization-titlebar-visibility-checkbox" class="customizationmode-checkbox" +# NB: because oncommand fires after click, by the time we've fired, the checkbox binding +# will already have switched the button's state, so this is correct: + oncommand="gCustomizeMode.toggleTitlebar(this.checked)" data-l10n-id="customize-mode-titlebar"/> +<checkbox id="customization-extra-drag-space-checkbox" class="customizationmode-checkbox" + data-l10n-id="customize-mode-extra-drag-space" + oncommand="gCustomizeMode.toggleDragSpace(this.checked)"/> +<button id="customization-toolbar-visibility-button" class="customizationmode-button" type="menu" data-l10n-id="customize-mode-toolbars"> + <menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/> +</button> +<button id="customization-lwtheme-button" data-l10n-id="customize-mode-lwthemes" class="customizationmode-button" type="menu"> + <panel type="arrow" id="customization-lwtheme-menu" + orient="vertical" + onpopupshowing="gCustomizeMode.onThemesMenuShowing(event);" + position="topcenter bottomleft" + flip="none" + role="menu"> + <label id="customization-lwtheme-menu-header" data-l10n-id="customize-mode-lwthemes-my-themes"/> + <hbox id="customization-lwtheme-menu-footer"> + <toolbarbutton class="customization-lwtheme-menu-footeritem" + data-l10n-id="customize-mode-lwthemes-menu-manage" + tabindex="0" + oncommand="gCustomizeMode.openAddonsManagerThemes(event);"/> + <toolbarbutton class="customization-lwtheme-menu-footeritem" + data-l10n-id="customize-mode-lwthemes-menu-get-more" + tabindex="0" + oncommand="gCustomizeMode.getMoreThemes(event);"/> + </hbox> + </panel> +</button> +<button id="customization-uidensity-button" + data-l10n-id="customize-mode-uidensity" + class="customizationmode-button" + type="menu"> + <panel type="arrow" id="customization-uidensity-menu" + onpopupshowing="gCustomizeMode.onUIDensityMenuShowing();" + position="topcenter bottomleft" + flip="none" + role="menu"> + <menuitem id="customization-uidensity-menuitem-compact" + class="menuitem-iconic customization-uidensity-menuitem" + role="menuitemradio" + data-l10n-id="customize-mode-uidensity-menu-compact" + tabindex="0" + onfocus="gCustomizeMode.updateUIDensity(this.mode);" + onmouseover="gCustomizeMode.updateUIDensity(this.mode);" + onblur="gCustomizeMode.resetUIDensity();" + onmouseout="gCustomizeMode.resetUIDensity();" + oncommand="gCustomizeMode.setUIDensity(this.mode);"/> + <menuitem id="customization-uidensity-menuitem-normal" + class="menuitem-iconic customization-uidensity-menuitem" + role="menuitemradio" + data-l10n-id="customize-mode-uidensity-menu-normal" + tabindex="0" + onfocus="gCustomizeMode.updateUIDensity(this.mode);" + onmouseover="gCustomizeMode.updateUIDensity(this.mode);" + onblur="gCustomizeMode.resetUIDensity();" + onmouseout="gCustomizeMode.resetUIDensity();" + oncommand="gCustomizeMode.setUIDensity(this.mode);"/> +#ifndef XP_MACOSX + <menuitem id="customization-uidensity-menuitem-touch" + class="menuitem-iconic customization-uidensity-menuitem" + role="menuitemradio" + data-l10n-id="customize-mode-uidensity-menu-touch" + tabindex="0" + onfocus="gCustomizeMode.updateUIDensity(this.mode);" + onmouseover="gCustomizeMode.updateUIDensity(this.mode);" + onblur="gCustomizeMode.resetUIDensity();" + onmouseout="gCustomizeMode.resetUIDensity();" + oncommand="gCustomizeMode.setUIDensity(this.mode);"> + </menuitem> + <spacer hidden="true" id="customization-uidensity-touch-spacer"/> + <checkbox id="customization-uidensity-autotouchmode-checkbox" + hidden="true" + data-l10n-id="customize-mode-uidensity-auto-touch-mode-checkbox" + oncommand="gCustomizeMode.updateAutoTouchMode(this.checked)"/> +#endif + </panel> +</button> + +<button id="whimsy-button" + type="checkbox" + class="customizationmode-button" + oncommand="gCustomizeMode.togglePong(this.checked);" + hidden="true"/> + +<spacer id="customization-footer-spacer"/> +<button id="customization-undo-reset-button" + class="customizationmode-button" + hidden="true" + oncommand="gCustomizeMode.undoReset();" + data-l10n-id="customize-mode-undo-cmd"/> +<button id="customization-reset-button" + oncommand="gCustomizeMode.reset();" + data-l10n-id="customize-mode-restore-defaults" + class="customizationmode-button"/> +<button id="customization-done-button" + oncommand="gCustomizeMode.exit();" + data-l10n-id="customize-mode-done" + class="customizationmode-button"/> +</hbox> diff --git a/comm/mail/components/customizableui/content/jar.mn b/comm/mail/components/customizableui/content/jar.mn new file mode 100644 index 0000000000..db1978fdb0 --- /dev/null +++ b/comm/mail/components/customizableui/content/jar.mn @@ -0,0 +1,6 @@ +# 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/. + +messenger.jar: + content/messenger/panelUI.js diff --git a/comm/mail/components/customizableui/content/moz.build b/comm/mail/components/customizableui/content/moz.build new file mode 100644 index 0000000000..d988c0ff9b --- /dev/null +++ b/comm/mail/components/customizableui/content/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ["jar.mn"] diff --git a/comm/mail/components/customizableui/content/panelUI.inc.xhtml b/comm/mail/components/customizableui/content/panelUI.inc.xhtml new file mode 100644 index 0000000000..3b965da756 --- /dev/null +++ b/comm/mail/components/customizableui/content/panelUI.inc.xhtml @@ -0,0 +1,606 @@ +<!-- 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/. --> + +<panel id="appMenu-popup" + class="cui-widget-panel panel-no-padding" + role="group" + type="arrow" + hidden="true" + flip="slide" + position="bottomright topright" + noautofocus="true"> + <panelmultiview id="appMenu-multiView" + mainViewId="appMenu-mainView" + viewCacheId="appMenu-viewCache"> + + <!-- Main Appmenu View --> + <panelview id="appMenu-mainView" class="PanelUI-subView"> + <vbox id="appMenu-mainViewItems" + class="panel-subview-body"> + <vbox id="appMenu-addon-banners"/> + <toolbarbutton class="panel-banner-item" + oncommand="PanelUI._onBannerItemSelected(event)" + hidden="true"/> +#ifdef NIGHTLY_BUILD + <toolbarbutton id="appmenu_signin" + data-l10n-id="appmenu-signin-panel" + class="subviewbutton subviewbutton-iconic" + hidden="true" + oncommand="gSync.initFxA();"/> + <toolbarbutton id="appmenu_sync" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + hidden="true" + align="center" + closemenu="none" + oncommand="PanelUI.showSubView('appMenu-syncView', this)"> + <hbox flex="1"> + <html:img id="appmenu-sync-icon" + class="toolbarbutton-icon" + alt=""/> + <vbox flex="1"> + <label id="appmenu-sync-sync" + crop="end" + data-l10n-id="appmenu-sync-sync"/> + <label id="appmenu-sync-account" + class="appmenu-sync-account-email" + crop="end" + data-l10n-id="appmenu-sync-account"/> + </vbox> + </hbox> + </toolbarbutton> + <toolbarseparator id="syncSeparator" hidden="true"/> +#endif + <toolbarbutton id="appmenu_new" + data-l10n-id="appmenu-new-account-panel" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + closemenu="none" + oncommand="PanelUI.showSubView('appMenu-newView', this)"/> + <toolbarbutton id="appmenu_create" + data-l10n-id="appmenu-create-panel" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + closemenu="none" + oncommand="PanelUI.showSubView('appMenu-createView', this)"/> + <toolbarseparator id="appmenu_createPopupMenuSeparator"/> + <toolbarbutton id="appmenu_open" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + data-l10n-id="appmenu-open-file-panel" + closemenu="none" + oncommand="PanelUI.showSubView('appMenu-openView', this)"/> + <toolbarseparator/> + <toolbarbutton id="appmenu_View" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + data-l10n-id="appmenu-view-panel" + closemenu="none" + oncommand="PanelUI.showSubView('appMenu-viewView', this)"/> + <toolbaritem id="appMenu-uiDensity-controls" + class="subviewbutton subviewbutton-iconic toolbaritem-combined-buttons" + closemenu="none"> + <html:img class="toolbarbutton-icon" src="" alt=""/> + <label class="toolbarbutton-text" data-l10n-id="appmenu-mail-uidensity-value"/> + <toolbarbutton id="appmenu_uiDensityCompact" + class="subviewbutton subviewbutton-iconic subviewbutton" + data-l10n-id="appmenu-uidensity-compact" + type="radio" + oncommand="PanelUI.setUIDensity(event);"/> + <toolbarbutton id="appmenu_uiDensityNormal" + class="subviewbutton subviewbutton-iconic subviewbutton" + data-l10n-id="appmenu-uidensity-default" + type="radio" + oncommand="PanelUI.setUIDensity(event);"/> + <toolbarbutton id="appmenu_uiDensityTouch" + class="subviewbutton subviewbutton-iconic subviewbutton" + data-l10n-id="appmenu-uidensity-relaxed" + type="radio" + oncommand="PanelUI.setUIDensity(event);"/> + </toolbaritem> + <toolbaritem id="appMenu-fontSize-controls" + class="subviewbutton subviewbutton-iconic toolbaritem-combined-buttons" + closemenu="none"> + <html:img class="toolbarbutton-icon" src="" alt=""/> + <label class="toolbarbutton-text" data-l10n-id="appmenu-font-size-value"/> + <toolbarbutton id="appMenu-fontSizeReduce-button" + class="subviewbutton subviewbutton-iconic" + oncommand="UIFontSize.reduceSize();" + data-l10n-id="appmenuitem-font-size-reduce"/> + <toolbarbutton id="appMenu-fontSizeReset-button" + class="subviewbutton" + oncommand="UIFontSize.resetSize();" + tooltip="fontSizeReset"/> + <toolbarbutton id="appMenu-fontSizeEnlarge-button" + class="subviewbutton subviewbutton-iconic" + oncommand="UIFontSize.increaseSize();" + data-l10n-id="appmenuitem-font-size-enlarge"/> + </toolbaritem> + <toolbarseparator/> + <toolbarbutton id="appmenu_preferences" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-settings" + oncommand="openOptionsDialog();"/> + <toolbarbutton id="appmenu_accountmgr" + class="subviewbutton subviewbutton-iconic" + label="&accountManagerCmd2.label;" + oncommand="MsgAccountManager(null);"/> + <toolbarbutton id="appmenu_addons" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-addons-and-themes" + oncommand="openAddonsMgr();"/> + <toolbarseparator/> + <toolbarbutton id="appmenu_toolsMenu" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + data-l10n-id="appmenu-tools-panel" + closemenu="none" + oncommand="PanelUI.showSubView('appMenu-toolsView', this)"/> + <toolbarbutton id="appmenu_help" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + data-l10n-id="menu-help-help-title" + closemenu="none" + oncommand="buildHelpMenu(); PanelUI.showSubView('appMenu-helpView', this)"/> + <toolbarseparator/> + <toolbarbutton id="appmenu-quit" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="menu-quit" + key="key_quitApplication" + command="cmd_quitApplication"/> + </vbox> + </panelview> +#ifdef NIGHTLY_BUILD + <!-- Sync --> + <panelview id="appMenu-syncView" + data-l10n-id="appmenu-sync-panel-title" + class="PanelUI-subView"> + <vbox id="appMenu-syncViewItems" + class="panel-subview-body"> + <toolbarbutton id="appmenu_manageSyncAccountMenuItem" + class="subviewbutton subviewbutton-iconic" + align="center" + oncommand="gSync.openFxAManagePage();"> + <hbox flex="1"> + <html:img id="appmenu-manage-sync-icon" + class="toolbarbutton-icon" + alt=""/> + <vbox flex="1"> + <label id="appmenu-sync-menu-manage" + crop="end" + data-l10n-id="appmenu-sync-manage"/> + <label id="appmenu-sync-menu-account" + class="appmenu-sync-account-email" + crop="end" + data-l10n-id="appmenu-sync-account"/> + </vbox> + </hbox> + </toolbarbutton> + + <toolbarbutton id="appmenu-submenu-sync-now" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-sync-now" + closemenu="none" + oncommand="Weave.Service.sync({});"/> + <toolbarbutton id="appmenu-submenu-sync-settings" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-sync-settings" + oncommand="openPreferencesTab('sync');"/> + <toolbarseparator/> + <toolbarbutton id="appmenu-submenu-sync-sign-out" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-sync-sign-out" + oncommand="gSync.disconnect({ confirm: true });"/> + </vbox> + </panelview> +#endif + <!-- New --> + <panelview id="appMenu-newView" + data-l10n-id="appmenu-new-account-panel-title" + class="PanelUI-subView"> + <vbox id="appMenu-newViewItems" + class="panel-subview-body"> + <toolbarbutton id="appmenu_newCreateEmailAccountMenuItem" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-create-new-mail-account" + oncommand="openAccountProvisionerTab();"/> + <toolbarbutton id="appmenu_newMailAccountMenuItem" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-new-mail-account" + oncommand="openAccountSetupTab();"/> +#ifdef MAIN_WINDOW + <toolbarbutton id="appmenu_calendar-new-calendar-menu-item" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-new-calendar" + command="calendar_new_calendar_command"/> +#endif + <toolbarbutton id="appmenu_newAB" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + data-l10n-id="appmenu-newab-panel" + closemenu="none" + oncommand="PanelUI.showSubView('appMenu-newabView', this)"/> + <toolbarbutton id="appmenu_newIMAccountMenuItem" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-new-chat-account" + oncommand="openIMAccountWizard();"/> + <toolbarbutton id="appmenu_newFeedAccountMenuItem" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-new-feed" + oncommand="AddFeedAccount();"/> + <toolbarbutton id="appmenu_newNewsgroupAccountMenuItem" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-new-newsgroup" + oncommand="openNewsgroupAccountWizard();"/> + </vbox> + </panelview> + + <!-- New AB --> + <panelview id="appMenu-newabView" + data-l10n-id="appmenu-newab-panel-title" + class="PanelUI-subView"> + <vbox id="appMenu-newABItems" + class="panel-subview-body"> + <toolbarbutton id="appmenu_newABMenuItem" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-new-addressbook" + oncommand="openNewABDialog();"/> + <toolbarbutton id="appmenu_newCardDAVMenuItem" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-new-carddav" + oncommand="openNewABDialog('CARDDAV');"/> + <toolbarbutton id="appmenu_newLdapMenuItem" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-new-ldap" + oncommand="openNewABDialog('LDAP');"/> + </vbox> + </panelview> + + <!-- Create --> + <panelview id="appMenu-createView" + data-l10n-id="appmenu-create-panel-title" + class="PanelUI-subView"> + <vbox id="appMenu-createViewItems" + class="panel-subview-body"> + <toolbarbutton id="appmenu_newNewMsgCmd" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-create-message" + key = "key_newMessage2" + command="cmd_newMessage"/> +#ifdef MAIN_WINDOW + <toolbarbutton id="appmenu_calendar-new-event-menu-item" + class="subviewbutton subviewbutton-iconic hide-when-calendar-deactivated" + data-l10n-id="appmenu-create-event" + command="calendar_new_event_command"/> + <toolbarbutton id="appmenu_calendar-new-task-menu-item" + class="subviewbutton subviewbutton-iconic hide-when-calendar-deactivated" + data-l10n-id="appmenu-create-task" + command="calendar_new_todo_command"/> +#endif + <toolbarbutton id="appmenu_newCard" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-create-contact" + command="cmd_newCard"/> + </vbox> + </panelview> + + <!-- Open --> + <panelview id="appMenu-openView" + data-l10n-id="appmenu-open-file-panel-title" + class="PanelUI-subView"> + <vbox id="appMenu-openViewItems" + class="panel-subview-body"> + <toolbarbutton id="appmenu_OpenMessageFileMenuitem" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-open-message" + oncommand="MsgOpenFromFile();"/> + <toolbarbutton id="appmenu_OpenCalendarFileMenuitem" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-open-calendar" + oncommand="openLocalCalendar();"/> + </vbox> + </panelview> + + <!-- View / Toolbars --> + <panelview id="appMenu-toolbarsView" + title="&viewToolbarsMenu.label;" + class="PanelUI-subView"> + <vbox class="panel-subview-body"> +#ifdef MAIN_WINDOW + <toolbarbutton id="appmenu_quickFilterBar" + class="subviewbutton subviewbutton-iconic" + type="checkbox" + closemenu="none" + data-l10n-id="quick-filter-bar-toggle" + command="cmd_toggleQuickFilterBar"/> + <toolbarbutton id="appmenu_spacesToolbar" + class="subviewbutton subviewbutton-iconic" + type="checkbox" + data-l10n-id="menu-spaces-toolbar-button" + closemenu="none" + oncommand="gSpacesToolbar.toggleToolbarFromMenu();"/> +#endif + <toolbarbutton id="appmenu_showStatusbar" + class="subviewbutton subviewbutton-iconic" + type="checkbox" + label="&showTaskbarCmd.label;" + oncommand="goToggleToolbar('status-bar', 'menu_showTaskbar')" + closemenu="none" + checked="true" + observes="menu_showTaskbar"/> + <toolbarseparator id="appmenu_toggleToolbarsSeparator"/> + <toolbarbutton id="appmenu_toolbarLayout" + class="subviewbutton subviewbutton-iconic" + label="&appmenuToolbarLayout.label;" + command="cmd_CustomizeMailToolbar"/> + </vbox> + </panelview> + + <!-- View / Layout --> + <panelview id="appMenu-preferencesLayoutView" + title="&messagePaneLayoutStyle.label;" + class="PanelUI-subView"> + <vbox class="panel-subview-body"> + <toolbarbutton id="appmenu_messagePaneClassic" + class="subviewbutton subviewbutton-iconic" + type="radio" + label="&messagePaneClassic.label;" + name="viewlayoutgroup" + command="cmd_viewClassicMailLayout"/> + <toolbarbutton id="appmenu_messagePaneWide" + class="subviewbutton subviewbutton-iconic" + type="radio" + label="&messagePaneWide.label;" + name="viewlayoutgroup" + command="cmd_viewWideMailLayout"/> + <toolbarbutton id="appmenu_messagePaneVertical" + class="subviewbutton subviewbutton-iconic" + type="radio" + label="&messagePaneVertical.label;" + name="viewlayoutgroup" + command="cmd_viewVerticalMailLayout"/> + <toolbarseparator id="appmenu_viewMenuAfterPaneVerticalSeparator"/> + <toolbarbutton id="appmenu_showFolderPane" + class="subviewbutton subviewbutton-iconic" + type="checkbox" + closemenu="none" + label="&showFolderPaneCmd.label;" + command="cmd_toggleFolderPane"/> + <toolbarbutton id="appmenu_toggleThreadPaneHeader" + class="subviewbutton subviewbutton-iconic" + type="checkbox" + name="threadheader" + closemenu="none" + data-l10n-id="appmenuitem-toggle-thread-pane-header" + command="cmd_toggleThreadPaneHeader"/> + <toolbarbutton id="appmenu_showMessage" + class="subviewbutton subviewbutton-iconic" + type="checkbox" + closemenu="none" + label="&showMessageCmd.label;" + key="key_toggleMessagePane" + command="cmd_toggleMessagePane"/> + <toolbarseparator/> + <toolbarbutton id="appmenu_calShowTodayPane-2" + class="subviewbutton subviewbutton-iconic" + label="&todaypane.showTodayPane.label;" + type="checkbox" + command="calendar_toggle_todaypane_command"/> + </vbox> + </panelview> + + <!-- View --> + <panelview id="appMenu-viewView" + class="PanelUI-subView" + data-l10n-id="appmenu-view-panel-title"> + <vbox id="appMenu-viewViewItems" + class="panel-subview-body"> + <toolbarbutton id="appmenu_Toolbars" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + label="&viewToolbarsMenu.label;" + accesskey="&viewToolbarsMenu.accesskey;" + closemenu="none" + oncommand="PanelUI.showSubView('appMenu-toolbarsView', this)"/> + <toolbarbutton id="appmenu_MessagePaneLayout" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + label="&messagePaneLayoutStyle.label;" + closemenu="none" + oncommand="PanelUI.showSubView('appMenu-preferencesLayoutView', this)"/> + <toolbarbutton id="appmenu_FolderViews" + class="subviewbutton subviewbutton-iconic subviewbutton-nav" + label="&folderView.label;" + closemenu="none" + oncommand="PanelUI.showSubView('appMenu-foldersView', this)"/> + </vbox> + </panelview> + + <!-- View / Folders --> + <panelview id="appMenu-foldersView" + title="&folderView.label;" + class="PanelUI-subView"> + <vbox class="panel-subview-body"> + <toolbarbutton id="appmenu_toggleFolderHeader" + class="subviewbutton subviewbutton-iconic" + name="paneheader" + value="toggle-header" + data-l10n-id="menu-view-folders-toggle-header" + type="checkbox" + closemenu="none" + oncommand="PanelUI.folderViewMenuOnCommand(event);"/> + <toolbarseparator id="appmenu_folderModesSeparator"/> + <toolbarbutton id="appmenu_allFolders" + class="subviewbutton subviewbutton-iconic" + value="all" + data-l10n-id="show-all-folders-label" + type="checkbox" + name="viewmessages" + closemenu="none" + oncommand="PanelUI.folderViewMenuOnCommand(event);"/> + <toolbarbutton id="appmenu_smartFolders" + class="subviewbutton subviewbutton-iconic" + value="smart" + data-l10n-id="show-smart-folders-label" + type="checkbox" + name="viewmessages" + closemenu="none" + oncommand="PanelUI.folderViewMenuOnCommand(event);"/> + <toolbarbutton id="appmenu_unreadFolders" + class="subviewbutton subviewbutton-iconic" + value="unread" + data-l10n-id="show-unread-folders-label" + type="checkbox" + name="viewmessages" + closemenu="none" + oncommand="PanelUI.folderViewMenuOnCommand(event);"/> + <toolbarbutton id="appmenu_favoriteFolders" + class="subviewbutton subviewbutton-iconic" + value="favorite" + data-l10n-id="show-favorite-folders-label" + type="checkbox" + name="viewmessages" + closemenu="none" + oncommand="PanelUI.folderViewMenuOnCommand(event);"/> + <toolbarbutton id="appmenu_recentFolders" + class="subviewbutton subviewbutton-iconic" + value="recent" + data-l10n-id="show-recent-folders-label" + type="checkbox" + name="viewmessages" + closemenu="none" + oncommand="PanelUI.folderViewMenuOnCommand(event);"/> + <toolbarseparator/> + <toolbarbutton id="appmenu_tagsFolders" + class="subviewbutton subviewbutton-iconic" + value="tags" + data-l10n-id="show-tags-folders-label" + type="checkbox" + name="viewmessages" + closemenu="none" + oncommand="PanelUI.folderViewMenuOnCommand(event);"/> + <toolbarseparator id="appmenu_compactPropertiesSeparator"/> + <toolbarbutton id="appmenu_compactMode" + class="subviewbutton subviewbutton-iconic" + value="compact" + data-l10n-id="folder-toolbar-toggle-folder-compact-view" + type="checkbox" + name="viewmessages" + closemenu="none" + oncommand="PanelUI.folderCompactMenuOnCommand(event)"/> + <toolbarseparator id="appmenu_favoritePropertiesSeparator"/> + <toolbarbutton id="appmenu_favoriteFolder" + class="subviewbutton subviewbutton-iconic" + type="checkbox" + label="&menuFavoriteFolder.label;" + checked="false" + command="cmd_toggleFavoriteFolder"/> + <toolbarbutton id="appmenu_properties" + class="subviewbutton subviewbutton-iconic" + command="cmd_properties"/> + </vbox> + </panelview> + + <!-- View / Messages / Tags --> + <!-- Dynamically populated when shown. --> + <panelview id="appMenu-viewMessagesTagsView" + title="&viewTags.label;" + class="PanelUI-subView" + oncommand="ViewChangeByMenuitem(event.target);"> + <vbox class="panel-subview-body"/> + </panelview> + + <!-- View / Messages / Custom Views --> + <!-- Dynamically populated when shown. --> + <panelview id="appMenu-viewMessagesCustomViewsView" + title="&viewCustomViews.label;" + class="PanelUI-subView" + oncommand="ViewChangeByMenuitem(event.target);"> + <vbox class="panel-subview-body"/> + </panelview> + + <!-- Tools --> + <panelview id="appMenu-toolsView" + data-l10n-id="appmenu-tools-panel-title" + class="PanelUI-subView"> + <vbox class="panel-subview-body"> + <toolbarbutton id="appmenu_import" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-tools-import" + oncommand="toImport();"/> + <toolbarbutton id="appmenu_export" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-tools-export" + oncommand="toExport();"/> + <toolbarseparator id="importExportSeparator"/> + <toolbarbutton id="appmenu_searchCmd" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-tools-message-search" + key="key_searchMail" + command="cmd_searchMessages"/> + <toolbarbutton id="appmenu_filtersCmd" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-tools-message-filters" + oncommand="MsgFilters();"/> + <toolbarbutton id="appmenu_manageKeysOpenPGP" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="openpgp-manage-keys-openpgp-cmd" + oncommand="openKeyManager()"/> + <toolbarbutton id="appmenu_openSavedFilesWnd" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-tools-download-manager" + key="key_savedFiles" + oncommand="openSavedFilesWnd();"/> + <toolbarbutton id="appmenu_activityManager" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-tools-activity-manager" + oncommand="openActivityMgr();"/> + <toolbarseparator id="devToolsSeparator"/> + <toolbarbutton id="appmenu_devtoolsToolbox" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-tools-dev-tools" + key="key_devtoolsToolbox" + oncommand="BrowserToolboxLauncher.init();"/> + </vbox> + </panelview> + + <!-- Help --> + <panelview id="appMenu-helpView" + data-l10n-id="appmenu-help-panel-title" + class="PanelUI-subView"> + <vbox class="panel-subview-body"> + <toolbarbutton id="appmenu_openHelp" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-help-get-help" + key="key_openHelp" + oncommand="openSupportURL();"/> + <toolbarbutton id="appmenu_openTour" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-help-explore-features" + oncommand="openLinkText(event, 'tourURL');"/> + <toolbarbutton id="appmenu_keyboardShortcuts" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-help-shortcuts" + oncommand="openLinkText(event, 'keyboardShortcutsURL');"/> + <toolbarseparator/> + <toolbarbutton id="appmenu_getInvolved" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-help-get-involved" + oncommand="openLinkText(event, 'getInvolvedURL');"/> + <toolbarbutton id="appmenu_makeDonation" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-help-donation" + oncommand="openLinkText(event, 'donateURL');"/> + <toolbarbutton id="appmenu_submitFeedback" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-help-share-feedback" + oncommand="openLinkText(event, 'feedbackURL');"/> + <toolbarseparator/> + <toolbarbutton id="appmenu_troubleshootMode" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-help-enter-troubleshoot-mode2" + oncommand="safeModeRestart();"/> + <toolbarbutton id="appmenu_troubleshootingInfo" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-help-troubleshooting-info" + oncommand="openAboutSupport();"/> + <toolbarseparator/> + <toolbarbutton id="appmenu_about" + class="subviewbutton subviewbutton-iconic" + data-l10n-id="appmenu-help-about-product" + oncommand="openAboutDialog();"/> + </vbox> + </panelview> + </panelmultiview> +</panel> diff --git a/comm/mail/components/customizableui/content/panelUI.js b/comm/mail/components/customizableui/content/panelUI.js new file mode 100644 index 0000000000..bad418abb4 --- /dev/null +++ b/comm/mail/components/customizableui/content/panelUI.js @@ -0,0 +1,882 @@ +/* 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/. */ + +/* import-globals-from ../../../base/content/globalOverlay.js */ +/* import-globals-from ../../../base/content/mailCore.js */ +/* import-globals-from ../../../base/content/mailWindowOverlay.js */ +/* import-globals-from ../../../base/content/messenger.js */ +/* import-globals-from ../../../extensions/mailviews/content/msgViewPickerOverlay.js */ + +var { ExtensionParent } = ChromeUtils.importESModule( + "resource://gre/modules/ExtensionParent.sys.mjs" +); +var { ExtensionSupport } = ChromeUtils.import( + "resource:///modules/ExtensionSupport.jsm" +); +var { ShortcutUtils } = ChromeUtils.importESModule( + "resource://gre/modules/ShortcutUtils.sys.mjs" +); +var { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); +var { UIDensity } = ChromeUtils.import("resource:///modules/UIDensity.jsm"); +var { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.sys.mjs", + CustomizableUI: "resource:///modules/CustomizableUI.sys.mjs", + PanelMultiView: "resource:///modules/PanelMultiView.sys.mjs", +}); +ChromeUtils.defineModuleGetter( + this, + "ExtensionsUI", + "resource:///modules/ExtensionsUI.jsm" +); + +/** + * Maintains the state and dispatches events for the main menu panel. + */ +const PanelUI = { + /** Panel events that we listen for. */ + get kEvents() { + return [ + "popupshowing", + "popupshown", + "popuphiding", + "popuphidden", + "ViewShowing", + ]; + }, + /** + * Used for lazily getting and memoizing elements from the document. Lazy + * getters are set in init, and memoizing happens after the first retrieval. + */ + get kElements() { + return { + mainView: "appMenu-mainView", + multiView: "appMenu-multiView", + menuButton: "button-appmenu", + panel: "appMenu-popup", + addonNotificationContainer: "appMenu-addon-banners", + navbar: "mail-bar3", + }; + }, + + kAppMenuButtons: new Set(), + + _initialized: false, + _notifications: null, + + init() { + this._initElements(); + this.initAppMenuButton("button-appmenu", "mail-toolbox"); + + this.menuButton = this.menuButtonMail; + + Services.obs.addObserver(this, "fullscreen-nav-toolbox"); + Services.obs.addObserver(this, "appMenu-notifications"); + + XPCOMUtils.defineLazyPreferenceGetter( + this, + "autoHideToolbarInFullScreen", + "browser.fullscreen.autohide", + false, + (pref, previousValue, newValue) => { + // On OSX, or with autohide preffed off, MozDOMFullscreen is the only + // event we care about, since fullscreen should behave just like non + // fullscreen. Otherwise, we don't want to listen to these because + // we'd just be spamming ourselves with both of them whenever a user + // opened a video. + if (newValue) { + window.removeEventListener("MozDOMFullscreen:Entered", this); + window.removeEventListener("MozDOMFullscreen:Exited", this); + window.addEventListener("fullscreen", this); + } else { + window.addEventListener("MozDOMFullscreen:Entered", this); + window.addEventListener("MozDOMFullscreen:Exited", this); + window.removeEventListener("fullscreen", this); + } + + this._updateNotifications(false); + }, + autoHidePref => autoHidePref && Services.appinfo.OS !== "Darwin" + ); + + if (this.autoHideToolbarInFullScreen) { + window.addEventListener("fullscreen", this); + } else { + window.addEventListener("MozDOMFullscreen:Entered", this); + window.addEventListener("MozDOMFullscreen:Exited", this); + } + + window.addEventListener("activate", this); + + Services.obs.notifyObservers( + null, + "appMenu-notifications-request", + "refresh" + ); + + this._initialized = true; + }, + + _initElements() { + for (let [k, v] of Object.entries(this.kElements)) { + // Need to do fresh let-bindings per iteration + let getKey = k; + let id = v; + this.__defineGetter__(getKey, function () { + delete this[getKey]; + // eslint-disable-next-line consistent-return + return (this[getKey] = document.getElementById(id)); + }); + } + }, + + initAppMenuButton(id, toolboxId) { + let button = document.getElementById(id); + if (!button) { + // If not in the document, the button should be in the toolbox palette, + // which isn't part of the document. + let toolbox = document.getElementById(toolboxId); + if (toolbox) { + button = toolbox.palette.querySelector(`#${id}`); + } + } + + if (button) { + button.addEventListener("mousedown", PanelUI); + button.addEventListener("keypress", PanelUI); + + this.kAppMenuButtons.add(button); + } + }, + + _eventListenersAdded: false, + _ensureEventListenersAdded() { + if (this._eventListenersAdded) { + return; + } + this._addEventListeners(); + }, + + _addEventListeners() { + for (let event of this.kEvents) { + this.panel.addEventListener(event, this); + } + this._eventListenersAdded = true; + }, + + _removeEventListeners() { + for (let event of this.kEvents) { + this.panel.removeEventListener(event, this); + } + this._eventListenersAdded = false; + }, + + uninit() { + this._removeEventListeners(); + + Services.obs.removeObserver(this, "fullscreen-nav-toolbox"); + Services.obs.removeObserver(this, "appMenu-notifications"); + + window.removeEventListener("MozDOMFullscreen:Entered", this); + window.removeEventListener("MozDOMFullscreen:Exited", this); + window.removeEventListener("fullscreen", this); + window.removeEventListener("activate", this); + + [this.menuButtonMail, this.menuButtonChat].forEach(button => { + // There's no chat button in the messageWindow.xhtml context. + if (button) { + button.removeEventListener("mousedown", this); + button.removeEventListener("keypress", this); + } + }); + }, + + /** + * Opens the menu panel if it's closed, or closes it if it's open. + * + * @param event the event that triggers the toggle. + */ + toggle(event) { + // Don't show the panel if the window is in customization mode, + // since this button doubles as an exit path for the user in this case. + if (document.documentElement.hasAttribute("customizing")) { + return; + } + + // Since we have several menu buttons, make sure the current one is used. + // This works for now, but in the long run, if we're showing badges etc. + // then the current menuButton needs to be set when the app's view/tab + // changes, not just when the menu is toggled. + this.menuButton = event.target; + + this._ensureEventListenersAdded(); + if (this.panel.state == "open") { + this.hide(); + } else if (this.panel.state == "closed") { + this.show(event); + } + }, + + /** + * Opens the menu panel. If the event target has a child with the + * toolbarbutton-icon attribute, the panel will be anchored on that child. + * Otherwise, the panel is anchored on the event target itself. + * + * @param aEvent the event (if any) that triggers showing the menu. + */ + show(aEvent) { + this._ensureShortcutsShown(); + (async () => { + await this.ensureReady(); + + if ( + this.panel.state == "open" || + document.documentElement.hasAttribute("customizing") + ) { + return; + } + + let domEvent = null; + if (aEvent && aEvent.type != "command") { + domEvent = aEvent; + } + + // We try to use the event.target to account for clicks triggered + // from the #button-chat-appmenu. In case the opening of the menu isn't + // triggered by a click event, fallback to the main menu button as anchor. + let anchor = this._getPanelAnchor( + aEvent ? aEvent.target : this.menuButton + ); + await PanelMultiView.openPopup(this.panel, anchor, { + triggerEvent: domEvent, + }); + })().catch(console.error); + }, + + /** + * If the menu panel is being shown, hide it. + */ + hide() { + if (document.documentElement.hasAttribute("customizing")) { + return; + } + + PanelMultiView.hidePopup(this.panel); + }, + + observe(subject, topic, status) { + switch (topic) { + case "fullscreen-nav-toolbox": + if (this._notifications) { + this._updateNotifications(false); + } + break; + case "appMenu-notifications": + // Don't initialize twice. + if (status == "init" && this._notifications) { + break; + } + this._notifications = AppMenuNotifications.notifications; + this._updateNotifications(true); + break; + } + }, + + handleEvent(event) { + // Ignore context menus and menu button menus showing and hiding: + if (event.type.startsWith("popup") && event.target != this.panel) { + return; + } + switch (event.type) { + case "popupshowing": + initAppMenuPopup(); + // Fall through + case "popupshown": + if (event.type == "popupshown") { + CustomizableUI.addPanelCloseListeners(this.panel); + } + // Fall through + case "popuphiding": + // Fall through + case "popuphidden": + this._updateNotifications(); + this._updatePanelButton(event.target); + if (event.type == "popuphidden") { + CustomizableUI.removePanelCloseListeners(this.panel); + } + break; + case "mousedown": + if (event.button == 0) { + this.toggle(event); + } + break; + case "keypress": + if (event.key == " " || event.key == "Enter") { + this.toggle(event); + event.stopPropagation(); + } + break; + case "MozDOMFullscreen:Entered": + case "MozDOMFullscreen:Exited": + case "fullscreen": + case "activate": + this._updateNotifications(); + break; + case "ViewShowing": + PanelUI._handleViewShowingEvent(event); + break; + } + }, + + /** + * When a ViewShowing event happens when a <panelview> element is shown, + * do any required set up for that particular view. + * + * @param {ViewShowingEvent} event - ViewShowing event. + */ + _handleViewShowingEvent(event) { + // Typically event.target for "ViewShowing" is a <panelview> element. + PanelUI._ensureShortcutsShown(event.target); + + switch (event.target.id) { + case "appMenu-foldersView": + this._onFoldersViewShow(event); + break; + case "appMenu-addonsView": + initAddonPrefsMenu( + event.target.querySelector(".panel-subview-body"), + "toolbarbutton", + "subviewbutton subviewbutton-iconic", + "subviewbutton subviewbutton-iconic" + ); + break; + case "appMenu-toolbarsView": + onViewToolbarsPopupShowing( + event, + "mail-toolbox", + document.getElementById("appmenu_quickFilterBar"), + "toolbarbutton", + "subviewbutton subviewbutton-iconic", + true + ); + break; + case "appMenu-preferencesLayoutView": + PanelUI._onPreferencesLayoutViewShow(event); + break; + // View + case "appMenu-viewMessagesTagsView": + PanelUI._refreshDynamicView(event, RefreshTagsPopup); + break; + case "appMenu-viewMessagesCustomViewsView": + PanelUI._refreshDynamicView(event, RefreshCustomViewsPopup); + break; + } + }, + + /** + * Refreshes some views that are dynamically populated. Typically called by + * event listeners responding to a ViewShowing event. It calls a given refresh + * function (that populates the view), passing appmenu-specific arguments. + * + * @param {ViewShowingEvent} event - ViewShowing event. + * @param {Function} refreshFunction - Function that refreshes a particular view. + */ + _refreshDynamicView(event, refreshFunction) { + refreshFunction( + event.target.querySelector(".panel-subview-body"), + "toolbarbutton", + "subviewbutton subviewbutton-iconic", + "toolbarseparator" + ); + }, + + get isReady() { + return !!this._isReady; + }, + + /** + * Registering the menu panel is done lazily for performance reasons. This + * method is exposed so that CustomizationMode can force panel-readyness in the + * event that customization mode is started before the panel has been opened + * by the user. + * + * @param aCustomizing (optional) set to true if this was called while entering + * customization mode. If that's the case, we trust that customization + * mode will handle calling beginBatchUpdate and endBatchUpdate. + * + * @returns a Promise that resolves once the panel is ready to roll. + */ + async ensureReady() { + if (this._isReady) { + return; + } + + await window.delayedStartupPromise; + this._ensureEventListenersAdded(); + this.panel.hidden = false; + this._isReady = true; + }, + + /** + * Shows a subview in the panel with a given ID. + * + * @param aViewId the ID of the subview to show. + * @param aAnchor the element that spawned the subview. + */ + async showSubView(aViewId, aAnchor) { + this._ensureEventListenersAdded(); + let viewNode = document.getElementById(aViewId); + if (!viewNode) { + console.error("Could not show panel subview with id: " + aViewId); + return; + } + + if (!aAnchor) { + console.error( + "Expected an anchor when opening subview with id: " + aViewId + ); + return; + } + + let container = aAnchor.closest("panelmultiview"); + if (container) { + container.showSubView(aViewId, aAnchor); + } + }, + + /** + * NB: The enable- and disableSingleSubviewPanelAnimations methods only + * affect the hiding/showing animations of single-subview panels (tempPanel + * in the showSubView method). + */ + disableSingleSubviewPanelAnimations() { + this._disableAnimations = true; + }, + + enableSingleSubviewPanelAnimations() { + this._disableAnimations = false; + }, + + /** + * Sets the anchor node into the open or closed state, depending + * on the state of the panel. + */ + _updatePanelButton() { + this.menuButton.open = + this.panel.state == "open" || this.panel.state == "showing"; + }, + + /** + * Event handler for showing the Preferences/Layout view. Removes "checked" + * from all layout menu items and then checks the current layout menu item. + * + * @param {ViewShowingEvent} event - ViewShowing event. + */ + _onPreferencesLayoutViewShow(event) { + event.target + .querySelectorAll("[name='viewlayoutgroup']") + .forEach(item => item.removeAttribute("checked")); + + InitViewLayoutStyleMenu(event, true); + }, + + /** + * Event listener for showing the Folders view. + * + * @param {ViewShowingEvent} event - ViewShowing event. + */ + _onFoldersViewShow(event) { + let about3Pane = document.getElementById("tabmail").currentAbout3Pane; + let folder = about3Pane.gFolder; + + const paneHeaderMenuitem = event.target.querySelector( + '[name="paneheader"]' + ); + if (about3Pane.folderPane.isFolderPaneHeaderHidden()) { + paneHeaderMenuitem.removeAttribute("checked"); + } else { + paneHeaderMenuitem.setAttribute("checked", "true"); + } + + let { activeModes, canBeCompact, isCompact } = about3Pane.folderPane; + if (isCompact) { + activeModes.push("compact"); + } + + for (let item of event.target.querySelectorAll('[name="viewmessages"]')) { + let mode = item.getAttribute("value"); + if (activeModes.includes(mode)) { + item.setAttribute("checked", "true"); + if (mode == "all") { + item.disabled = activeModes.length == 1; + } + } else { + item.removeAttribute("checked"); + } + if (mode == "compact") { + item.disabled = !canBeCompact; + } + } + + goUpdateCommand("cmd_properties"); + let propertiesMenuItem = document.getElementById("appmenu_properties"); + if (folder?.server.type == "nntp") { + document.l10n.setAttributes( + propertiesMenuItem, + "menu-edit-newsgroup-properties" + ); + } else { + document.l10n.setAttributes( + propertiesMenuItem, + "menu-edit-folder-properties" + ); + } + + let favoriteFolderMenu = document.getElementById("appmenu_favoriteFolder"); + if (folder?.getFlag(Ci.nsMsgFolderFlags.Favorite)) { + favoriteFolderMenu.setAttribute("checked", "true"); + } else { + favoriteFolderMenu.removeAttribute("checked"); + } + }, + + _onToolsMenuShown(event) { + let noAccounts = MailServices.accounts.accounts.length == 0; + event.target.querySelector("#appmenu_searchCmd").disabled = noAccounts; + event.target.querySelector("#appmenu_filtersCmd").disabled = noAccounts; + }, + + _updateNotifications(notificationsChanged) { + let notifications = this._notifications; + if (!notifications || !notifications.length) { + if (notificationsChanged) { + this._clearAllNotifications(); + } + return; + } + + let doorhangers = notifications.filter( + n => !n.dismissed && !n.options.badgeOnly + ); + + if (this.panel.state == "showing" || this.panel.state == "open") { + // If the menu is already showing, then we need to dismiss all notifications + // since we don't want their doorhangers competing for attention + doorhangers.forEach(n => { + n.dismissed = true; + if (n.options.onDismissed) { + n.options.onDismissed(window); + } + }); + this._clearBadge(); + if (!notifications[0].options.badgeOnly) { + this._showBannerItem(notifications[0]); + } + } else if (doorhangers.length > 0) { + // Only show the doorhanger if the window is focused and not fullscreen + if ( + (window.fullScreen && this.autoHideToolbarInFullScreen) || + Services.focus.activeWindow !== window + ) { + this._showBadge(doorhangers[0]); + this._showBannerItem(doorhangers[0]); + } else { + this._clearBadge(); + } + } else { + this._showBadge(notifications[0]); + this._showBannerItem(notifications[0]); + } + }, + + _clearAllNotifications() { + this._clearBadge(); + this._clearBannerItem(); + }, + + _formatDescriptionMessage(n) { + let text = {}; + let array = n.options.message.split("<>"); + text.start = array[0] || ""; + text.name = n.options.name || ""; + text.end = array[1] || ""; + return text; + }, + + _showBadge(notification) { + let badgeStatus = this._getBadgeStatus(notification); + for (let menuButton of this.kAppMenuButtons) { + menuButton.setAttribute("badge-status", badgeStatus); + } + }, + + // "Banner item" here refers to an item in the hamburger panel menu. They will + // typically show up as a colored row in the panel. + _showBannerItem(notification) { + const supportedIds = [ + "update-downloading", + "update-available", + "update-manual", + "update-unsupported", + "update-restart", + ]; + if (!supportedIds.includes(notification.id)) { + return; + } + + if (!this._panelBannerItem) { + this._panelBannerItem = this.mainView.querySelector(".panel-banner-item"); + } + + let l10nId = "appmenuitem-banner-" + notification.id; + document.l10n.setAttributes(this._panelBannerItem, l10nId); + + this._panelBannerItem.setAttribute("notificationid", notification.id); + this._panelBannerItem.hidden = false; + this._panelBannerItem.notification = notification; + }, + + _clearBadge() { + for (let menuButton of this.kAppMenuButtons) { + menuButton.removeAttribute("badge-status"); + } + }, + + _clearBannerItem() { + if (this._panelBannerItem) { + this._panelBannerItem.notification = null; + this._panelBannerItem.hidden = true; + } + }, + + _onNotificationButtonEvent(event, type) { + let notificationEl = getNotificationFromElement(event.target); + + if (!notificationEl) { + throw new Error( + "PanelUI._onNotificationButtonEvent: couldn't find notification element" + ); + } + + if (!notificationEl.notification) { + throw new Error( + "PanelUI._onNotificationButtonEvent: couldn't find notification" + ); + } + + let notification = notificationEl.notification; + + if (type == "secondarybuttoncommand") { + AppMenuNotifications.callSecondaryAction(window, notification); + } else { + AppMenuNotifications.callMainAction(window, notification, true); + } + }, + + _onBannerItemSelected(event) { + let target = event.target; + if (!target.notification) { + throw new Error( + "menucommand target has no associated action/notification" + ); + } + + event.stopPropagation(); + AppMenuNotifications.callMainAction(window, target.notification, false); + }, + + _getPopupId(notification) { + return "appMenu-" + notification.id + "-notification"; + }, + + _getBadgeStatus(notification) { + return notification.id; + }, + + _getPanelAnchor(candidate) { + let iconAnchor = candidate.badgeStack || candidate.icon; + return iconAnchor || candidate; + }, + + _ensureShortcutsShown(view = this.mainView) { + if (view.hasAttribute("added-shortcuts")) { + return; + } + view.setAttribute("added-shortcuts", "true"); + for (let button of view.querySelectorAll("toolbarbutton[key]")) { + let keyId = button.getAttribute("key"); + let key = document.getElementById(keyId); + if (!key) { + continue; + } + button.setAttribute("shortcut", ShortcutUtils.prettifyShortcut(key)); + } + }, + + folderViewMenuOnCommand(event) { + let about3Pane = document.getElementById("tabmail").currentAbout3Pane; + if (!about3Pane) { + return; + } + + let mode = event.target.getAttribute("value"); + if (mode == "toggle-header") { + about3Pane.folderPane.toggleHeader(event.target.hasAttribute("checked")); + return; + } + + let activeModes = about3Pane.folderPane.activeModes; + let index = activeModes.indexOf(mode); + if (event.target.hasAttribute("checked")) { + if (index == -1) { + activeModes.push(mode); + } + } else if (index >= 0) { + activeModes.splice(index, 1); + } + about3Pane.folderPane.activeModes = activeModes; + + this._onFoldersViewShow({ target: event.target.parentNode }); + }, + + folderCompactMenuOnCommand(event) { + let about3Pane = document.getElementById("tabmail").currentAbout3Pane; + if (!about3Pane) { + return; + } + + about3Pane.folderPane.isCompact = event.target.hasAttribute("checked"); + }, + + setUIDensity(event) { + // Loops through all available options and uncheck them. This is necessary + // since the toolbarbuttons don't uncheck themselves even if they're radio. + for (let item of event.originalTarget + .closest(".panel-subview-body") + .querySelectorAll("toolbarbutton")) { + // Skip this item if it's the one clicked. + if (item == event.originalTarget) { + continue; + } + + item.removeAttribute("checked"); + } + // Update the UI density. + UIDensity.setMode(event.originalTarget.mode); + }, +}; + +XPCOMUtils.defineConstant(this, "PanelUI", PanelUI); + +/** + * Gets the currently selected locale for display. + * + * @returns the selected locale + */ +function getLocale() { + return Services.locale.appLocaleAsBCP47; +} + +/** + * Given a DOM node inside a <popupnotification>, return the parent <popupnotification>. + */ +function getNotificationFromElement(aElement) { + return aElement.closest("popupnotification"); +} + +/** + * This object is Thunderbird's version of the same object in + * browser/base/content/browser-addons.js. + */ +var gExtensionsNotifications = { + initialized: false, + init() { + this.updateAlerts(); + this.boundUpdate = this.updateAlerts.bind(this); + ExtensionsUI.on("change", this.boundUpdate); + this.initialized = true; + }, + + uninit() { + // uninit() can race ahead of init() in some cases, if that happens, + // we have no handler to remove. + if (!this.initialized) { + return; + } + ExtensionsUI.off("change", this.boundUpdate); + }, + + get l10n() { + if (this._l10n) { + return this._l10n; + } + return (this._l10n = new Localization( + ["messenger/addonNotifications.ftl", "branding/brand.ftl"], + true + )); + }, + + _createAddonButton(l10nId, addon, callback) { + let text = this.l10n.formatValueSync(l10nId, { addonName: addon.name }); + let button = document.createXULElement("toolbarbutton"); + button.setAttribute("wrap", "true"); + button.setAttribute("label", text); + button.setAttribute("tooltiptext", text); + const DEFAULT_EXTENSION_ICON = + "chrome://messenger/skin/icons/new/compact/extension.svg"; + button.setAttribute("image", addon.iconURL || DEFAULT_EXTENSION_ICON); + button.className = "addon-banner-item subviewbutton"; + + button.addEventListener("command", callback); + PanelUI.addonNotificationContainer.appendChild(button); + }, + + updateAlerts() { + let gBrowser = document.getElementById("tabmail"); + let sideloaded = ExtensionsUI.sideloaded; + let updates = ExtensionsUI.updates; + + let container = PanelUI.addonNotificationContainer; + + while (container.firstChild) { + container.firstChild.remove(); + } + + let items = 0; + for (let update of updates) { + if (++items > 4) { + break; + } + this._createAddonButton( + "webext-perms-update-menu-item", + update.addon, + evt => { + ExtensionsUI.showUpdate(gBrowser, update); + } + ); + } + + for (let addon of sideloaded) { + if (++items > 4) { + break; + } + this._createAddonButton("webext-perms-sideload-menu-item", addon, evt => { + // We need to hide the main menu manually because the toolbarbutton is + // removed immediately while processing this event, and PanelUI is + // unable to identify which panel should be closed automatically. + PanelUI.hide(); + ExtensionsUI.showSideloaded(gBrowser, addon); + }); + } + }, +}; + +addEventListener("unload", () => gExtensionsNotifications.uninit(), { + once: true, +}); |