From ef98b797b3579b24b84584447e33e33800cab64d Mon Sep 17 00:00:00 2001 From: HarryHeres Date: Sat, 21 Sep 2024 12:37:50 +0200 Subject: [PATCH] Working on the shortcuts --- src/ZenKeyboardShortcuts.mjs | 921 ++++++++++++++++------------------- 1 file changed, 433 insertions(+), 488 deletions(-) diff --git a/src/ZenKeyboardShortcuts.mjs b/src/ZenKeyboardShortcuts.mjs index ea62b6a..8559d3e 100644 --- a/src/ZenKeyboardShortcuts.mjs +++ b/src/ZenKeyboardShortcuts.mjs @@ -3,573 +3,518 @@ const kZKSActions = { // otherwise "oncommand=" will be added. // Split view actions - zenSplitViewGrid: [ - "gZenViewSplitter.toggleShortcut('grid')", - "zen-split-view-grid", - "split-view-action", - ], - zenSplitViewVertical: [ - "gZenViewSplitter.toggleShortcut('vsep')", - "zen-split-view-vertical", - "split-view-action", - ], - zenSplitViewHorizontal: [ - "gZenViewSplitter.toggleShortcut('hsep')", - "zen-split-view-horizontal", - "split-view-action", - ], - zenSplitViewClose: [ - "gZenViewSplitter.toggleShortcut('unsplit')", - "zen-split-view-close", - "split-view-action", - ], + zenSplitViewGrid: ["gZenViewSplitter.toggleShortcut('grid')", 'zen-split-view-grid', 'split-view-action'], + zenSplitViewVertical: ["gZenViewSplitter.toggleShortcut('vsep')", 'zen-split-view-vertical', 'split-view-action'], + zenSplitViewHorizontal: ["gZenViewSplitter.toggleShortcut('hsep')", 'zen-split-view-horizontal', 'split-view-action'], + zenSplitViewClose: ["gZenViewSplitter.toggleShortcut('unsplit')", 'zen-split-view-close', 'split-view-action'], // Workspace actions - zenChangeWorkspace: [ - "ZenWorkspaces.changeWorkspaceShortcut()", - "zen-change-workspace", - "workspace-action", - ], + zenChangeWorkspace: ['ZenWorkspaces.changeWorkspaceShortcut()', 'zen-change-workspace', 'workspace-action'], // manage actions - openNewTab: [ - "command:cmd_newNavigatorTabNoEvent", - "open-new-tab", - "tab-action", - ], - duplicateTab: [ - "duplicateTabIn(gBrowser.selectedTab, 'tab')", - "duplicate-tab", - "tab-action", - ], - closeTab: ["command:cmd_close", "close-tab", "tab-action"], - openNewWindow: ["command:cmd_newNavigator", "open-new-window", "tab-action"], - openNewPrivateWindow: [ - "command:Tools:PrivateBrowsing", - "open-new-private-window", - "tab-action", - ], - closeWindow: ["command:cmd_closeWindow", "close-window", "tab-action"], - restoreLastTab: ["undoCloseTab()", "restore-last-session", "tab-action"], - restoreLastWindow: [ - "command:History:UndoCloseWindow", - "restore-last-window", - "tab-action", - ], - showNextTab: [ - "gBrowser.tabContainer.advanceSelectedTab(1, true)", - "show-next-tab", - "tab-action", - ], - showPreviousTab: [ - "gBrowser.tabContainer.advanceSelectedTab(-1, true)", - "show-previous-tab", - "tab-action", - ], - showAllTabsPanel: [ - "gTabsPanel.showAllTabsPanel()", - "show-all-tabs-panel", - "tab-action", - ], + openNewTab: ['command:cmd_newNavigatorTabNoEvent', 'open-new-tab', 'tab-action'], + duplicateTab: ["duplicateTabIn(gBrowser.selectedTab, 'tab')", 'duplicate-tab', 'tab-action'], + closeTab: ['command:cmd_close', 'close-tab', 'tab-action'], + openNewWindow: ['command:cmd_newNavigator', 'open-new-window', 'tab-action'], + openNewPrivateWindow: ['command:Tools:PrivateBrowsing', 'open-new-private-window', 'tab-action'], + closeWindow: ['command:cmd_closeWindow', 'close-window', 'tab-action'], + restoreLastTab: ['undoCloseTab()', 'restore-last-session', 'tab-action'], + restoreLastWindow: ['command:History:UndoCloseWindow', 'restore-last-window', 'tab-action'], + showNextTab: ['gBrowser.tabContainer.advanceSelectedTab(1, true)', 'show-next-tab', 'tab-action'], + showPreviousTab: ['gBrowser.tabContainer.advanceSelectedTab(-1, true)', 'show-previous-tab', 'tab-action'], + showAllTabsPanel: ['gTabsPanel.showAllTabsPanel()', 'show-all-tabs-panel', 'tab-action'], // Compact mode actions - zenToggleCompactMode: [ - "gZenCompactModeManager.toggle()", - "zen-toggle-compact-mode", - "compact-mode-action", - ], + zenToggleCompactMode: ['gZenCompactModeManager.toggle()', 'zen-toggle-compact-mode', 'compact-mode-action'], zenToggleCompactModeSidebar: [ - "gZenCompactModeManager.toggleSidebar()", - "zen-toggle-compact-mode-sidebar", - "compact-mode-action", + 'gZenCompactModeManager.toggleSidebar()', + 'zen-toggle-compact-mode-sidebar', + 'compact-mode-action', ], zenToggleCompactModeToolbar: [ - "gZenCompactModeManager.toggleToolbar()", - "zen-toggle-compact-mode-toolbar", - "compact-mode-action", + 'gZenCompactModeManager.toggleToolbar()', + 'zen-toggle-compact-mode-toolbar', + 'compact-mode-action', ], // Page actions - sendWithMail: ["command:Browser:SendLink", "send-with-mail", "page-action"], - savePage: ["command:Browser:SavePage", "save-page", "page-action"], - printPage: ["command:cmd_print", "print-page", "page-action"], - muteCurrentTab: ["command:cmd_toggleMute", "mute-current-tab", "page-action"], - showSourceOfPage: [ - "command:View:PageSource", - "show-source-of-page", - "page-action", - ], - showPageInfo: ["command:View:PageInfo", "show-page-info", "page-action"], + sendWithMail: ['command:Browser:SendLink', 'send-with-mail', 'page-action'], + savePage: ['command:Browser:SavePage', 'save-page', 'page-action'], + printPage: ['command:cmd_print', 'print-page', 'page-action'], + muteCurrentTab: ['command:cmd_toggleMute', 'mute-current-tab', 'page-action'], + showSourceOfPage: ['command:View:PageSource', 'show-source-of-page', 'page-action'], + showPageInfo: ['command:View:PageInfo', 'show-page-info', 'page-action'], // Visible actions - zoomIn: ["command:cmd_fullZoomEnlarge", "zoom-in", "visible-action"], - zoomOut: ["command:cmd_fullZoomReduce", "zoom-out", "visible-action"], - resetZoom: ["command:cmd_fullZoomReset", "reset-zoom", "visible-action"], + zoomIn: ['command:cmd_fullZoomEnlarge', 'zoom-in', 'visible-action'], + zoomOut: ['command:cmd_fullZoomReduce', 'zoom-out', 'visible-action'], + resetZoom: ['command:cmd_fullZoomReset', 'reset-zoom', 'visible-action'], // History actions - back: ["command:Browser:Back", "back", "history-action"], - forward: ["command:Browser:Forward", "forward", "history-action"], - stop: ["command:Browser:Stop", "stop", "history-action"], - reload: ["command:Browser:Reload", "reload", "history-action"], - forceReload: [ - "command:Browser:ReloadSkipCache", - "force-reload", - "history-action", - ], + back: ['command:Browser:Back', 'back', 'history-action'], + forward: ['command:Browser:Forward', 'forward', 'history-action'], + stop: ['command:Browser:Stop', 'stop', 'history-action'], + reload: ['command:Browser:Reload', 'reload', 'history-action'], + forceReload: ['command:Browser:ReloadSkipCache', 'force-reload', 'history-action'], // search actions - searchInThisPage: [ - "gLazyFindCommand('onFindCommand')", - "search-in-this-page", - "search-action", - ], - showNextSearchResult: [ - "gLazyFindCommand('onFindAgainCommand', false)", - "show-next-search-result", - "search-action", - ], - showPreviousSearchResult: [ - "gLazyFindCommand('onFindAgainCommand', true)", - "show-previous-search-result", - "search-action", - ], - searchTheWeb: ["command:Tools:Search", "search-the-web", "search-action"], + searchInThisPage: ["gLazyFindCommand('onFindCommand')", 'search-in-this-page', 'search-action'], + showNextSearchResult: ["gLazyFindCommand('onFindAgainCommand', false)", 'show-next-search-result', 'search-action'], + showPreviousSearchResult: ["gLazyFindCommand('onFindAgainCommand', true)", 'show-previous-search-result', 'search-action'], + searchTheWeb: ['command:Tools:Search', 'search-the-web', 'search-action'], // Tools actions - openMigrationWizard: [ - "command:cmd_file_importFromAnotherBrowser", - "open-migration-wizard", - "tools-action", - ], - quitFromApplication: [ - "command:goQuitApplication", - "quit-from-application", - "tools-action", - ], - enterIntoCustomizeMode: [ - "gCustomizeMode.enter()", - "enter-into-customize-mode", - "tools-action", - ], - enterIntoOfflineMode: [ - "command:cmd_toggleOfflineStatus", - "enter-into-offline-mode", - "tools-action", - ], - openScreenCapture: [ - "command:Browser:Screenshot", - "open-screen-capture", - "tools-action", - ], + openMigrationWizard: ['command:cmd_file_importFromAnotherBrowser', 'open-migration-wizard', 'tools-action'], + quitFromApplication: ['command:goQuitApplication', 'quit-from-application', 'tools-action'], + enterIntoCustomizeMode: ['gCustomizeMode.enter()', 'enter-into-customize-mode', 'tools-action'], + enterIntoOfflineMode: ['command:cmd_toggleOfflineStatus', 'enter-into-offline-mode', 'tools-action'], + openScreenCapture: ['command:Browser:Screenshot', 'open-screen-capture', 'tools-action'], // Bookmark actions bookmarkThisPage: [ "BrowserPageActions.doCommandForAction(PageActions.actionForID('bookmark'), event, this);", - "bookmark-this-page", - "bookmark-action", + 'bookmark-this-page', + 'bookmark-action', ], openBookmarkAddTool: [ - "PlacesUIUtils.showBookmarkPagesDialog(PlacesCommandHook.uniqueCurrentPages)", - "open-bookmark-add-tool", - "bookmark-action", - ], - openBookmarksManager: [ - "SidebarController.toggle('viewBookmarksSidebar');", - "open-bookmarks-manager", - "bookmark-action", + 'PlacesUIUtils.showBookmarkPagesDialog(PlacesCommandHook.uniqueCurrentPages)', + 'open-bookmark-add-tool', + 'bookmark-action', ], + openBookmarksManager: ["SidebarController.toggle('viewBookmarksSidebar');", 'open-bookmarks-manager', 'bookmark-action'], toggleBookmarkToolbar: [ "BookmarkingUI.toggleBookmarksToolbar('bookmark-tools')", - "toggle-bookmark-toolbar", - "bookmark-action", + 'toggle-bookmark-toolbar', + 'bookmark-action', ], // Open Page actions - openGeneralPreferences: [ - "openPreferences()", - "open-general-preferences", - "open-page-action", - ], - openPrivacyPreferences: [ - "openPreferences('panePrivacy')", - "open-privacy-preferences", - "open-page-action", - ], - openWorkspacesPreferences: [ - "openPreferences('paneWorkspaces')", - "open-workspaces-preferences", - "open-page-action", - ], - openContainersPreferences: [ - "openPreferences('paneContainers')", - "open-containers-preferences", - "open-page-action", - ], - openSearchPreferences: [ - "openPreferences('paneSearch')", - "open-search-preferences", - "open-page-action", - ], - openSyncPreferences: [ - "openPreferences('paneSync')", - "open-sync-preferences", - "open-page-action", - ], - openTaskManager: [ - "command:View:AboutProcesses", - "open-task-manager", - "open-page-action", - ], - openAddonsManager: [ - "command:Tools:Addons", - "open-addons-manager", - "open-page-action", - ], - openHomePage: ["BrowserHome()", "open-home-page", "open-page-action"], + openGeneralPreferences: ['openPreferences()', 'open-general-preferences', 'open-page-action'], + openPrivacyPreferences: ["openPreferences('panePrivacy')", 'open-privacy-preferences', 'open-page-action'], + openWorkspacesPreferences: ["openPreferences('paneWorkspaces')", 'open-workspaces-preferences', 'open-page-action'], + openContainersPreferences: ["openPreferences('paneContainers')", 'open-containers-preferences', 'open-page-action'], + openSearchPreferences: ["openPreferences('paneSearch')", 'open-search-preferences', 'open-page-action'], + openSyncPreferences: ["openPreferences('paneSync')", 'open-sync-preferences', 'open-page-action'], + openTaskManager: ['command:View:AboutProcesses', 'open-task-manager', 'open-page-action'], + openAddonsManager: ['command:Tools:Addons', 'open-addons-manager', 'open-page-action'], + openHomePage: ['BrowserHome()', 'open-home-page', 'open-page-action'], // History actions - forgetHistory: ["command:Tools:Sanitize", "forget-history", "history-action"], - quickForgetHistory: [ - "PlacesUtils.history.clear(true)", - "quick-forget-history", - "history-action", - ], - clearRecentHistory: [ - "command:cmd_closeWindow", - "clear-recent-history", - "history-action", - ], - restoreLastSession: [ - "command:Browser:RestoreLastSession", - "restore-last-session", - "history-action", - ], - searchHistory: [ - "command:History:SearchHistory", - "search-history", - "history-action", - ], - manageHistory: [ - "PlacesCommandHook.showPlacesOrganizer('History')", - "manage-history", - "history-action", - ], + forgetHistory: ['command:Tools:Sanitize', 'forget-history', 'history-action'], + quickForgetHistory: ['PlacesUtils.history.clear(true)', 'quick-forget-history', 'history-action'], + clearRecentHistory: ['command:cmd_closeWindow', 'clear-recent-history', 'history-action'], + restoreLastSession: ['command:Browser:RestoreLastSession', 'restore-last-session', 'history-action'], + searchHistory: ['command:History:SearchHistory', 'search-history', 'history-action'], + manageHistory: ["PlacesCommandHook.showPlacesOrganizer('History')", 'manage-history', 'history-action'], // Downloads actions - openDownloads: [ - "DownloadsPanel.showDownloadsHistory()", - "open-downloads", - "downloads-action", - ], + openDownloads: ['DownloadsPanel.showDownloadsHistory()', 'open-downloads', 'downloads-action'], // Sidebar actions - showBookmarkSidebar: [ - "SidebarController.show('viewBookmarksSidebar')", - "show-bookmark-sidebar", - "sidebar-action", - ], - showHistorySidebar: [ - "SidebarController.show('viewHistorySidebar')", - "show-history-sidebar", - "sidebar-action", - ], - showSyncedTabsSidebar: [ - "SidebarController.show('viewTabsSidebar')", - "show-synced-tabs-sidebar", - "sidebar-action", - ], - reverseSidebarPosition: [ - "SidebarController.reversePosition()", - "reverse-sidebar", - "sidebar-action", - ], - hideSidebar: ["SidebarController.hide()", "hide-sidebar", "sidebar-action"], - toggleSidebar: [ - "SidebarController.toggle()", - "toggle-sidebar", - "sidebar-action", - ], - zenToggleWebPanels: [ - "gZenBrowserManagerSidebar.toggle()", - "zen-toggle-web-panels", - "sidebar-action", - ], + showBookmarkSidebar: ["SidebarController.show('viewBookmarksSidebar')", 'show-bookmark-sidebar', 'sidebar-action'], + showHistorySidebar: ["SidebarController.show('viewHistorySidebar')", 'show-history-sidebar', 'sidebar-action'], + showSyncedTabsSidebar: ["SidebarController.show('viewTabsSidebar')", 'show-synced-tabs-sidebar', 'sidebar-action'], + reverseSidebarPosition: ['SidebarController.reversePosition()', 'reverse-sidebar', 'sidebar-action'], + hideSidebar: ['SidebarController.hide()', 'hide-sidebar', 'sidebar-action'], + toggleSidebar: ['SidebarController.toggle()', 'toggle-sidebar', 'sidebar-action'], + zenToggleWebPanels: ['gZenBrowserManagerSidebar.toggle()', 'zen-toggle-web-panels', 'sidebar-action'], }; const kZenDefaultShortcuts = { // Split view actions - zenSplitViewGrid: "Ctrl+Alt+G", - zenSplitViewVertical: "Ctrl+Alt+V", - zenSplitViewHorizontal: "Ctrl+Alt+H", - zenSplitViewClose: "Ctrl+Alt+U", + zenSplitViewGrid: 'Ctrl+Alt+G', + zenSplitViewVertical: 'Ctrl+Alt+V', + zenSplitViewHorizontal: 'Ctrl+Alt+H', + zenSplitViewClose: 'Ctrl+Alt+U', // Workspace actions - zenChangeWorkspace: "Ctrl+Shift+E", + zenChangeWorkspace: 'Ctrl+Shift+E', // Compact mode actions - zenToggleCompactMode: "Ctrl+Alt+C", - zenToggleCompactModeSidebar: "Ctrl+Alt+S", - zenToggleCompactModeToolbar: "Ctrl+Alt+T", + zenToggleCompactMode: 'Ctrl+Alt+C', + zenToggleCompactModeSidebar: 'Ctrl+Alt+S', + zenToggleCompactModeToolbar: 'Ctrl+Alt+T', // manage actions - zenToggleWebPanels: "Alt+P", + zenToggleWebPanels: 'Alt+P', }; // Section: ZenKeyboardShortcuts -const kZKSStorageKey = "zen.keyboard.shortcuts"; const kZKSKeyCodeMap = { - F1: "VK_F1", - F2: "VK_F2", - F3: "VK_F3", - F4: "VK_F4", - F5: "VK_F5", - F6: "VK_F6", - F7: "VK_F7", - F8: "VK_F8", - F9: "VK_F9", - F10: "VK_F10", - F11: "VK_F11", - F12: "VK_F12", - TAB: "VK_TAB", - ENTER: "VK_RETURN", - ESCAPE: "VK_ESCAPE", - SPACE: "VK_SPACE", - ARROWLEFT: "VK_LEFT", - ARROWRIGHT: "VK_RIGHT", - ARROWUP: "VK_UP", - ARROWDOWN: "VK_DOWN", - DELETE: "VK_DELETE", - BACKSPACE: "VK_BACK", + F1: 'VK_F1', + F2: 'VK_F2', + F3: 'VK_F3', + F4: 'VK_F4', + F5: 'VK_F5', + F6: 'VK_F6', + F7: 'VK_F7', + F8: 'VK_F8', + F9: 'VK_F9', + F10: 'VK_F10', + F11: 'VK_F11', + F12: 'VK_F12', + TAB: 'VK_TAB', + ENTER: 'VK_RETURN', + ESCAPE: 'VK_ESCAPE', + SPACE: 'VK_SPACE', + ARROWLEFT: 'VK_LEFT', + ARROWRIGHT: 'VK_RIGHT', + ARROWUP: 'VK_UP', + ARROWDOWN: 'VK_DOWN', + DELETE: 'VK_DELETE', + BACKSPACE: 'VK_BACK', }; -var gZenKeyboardShortcuts = { +const SHORTCUTS_STORAGE_KEY = 'zen.keyboard.shortcuts'; +const ZEN_SHORTCUTS_GROUP = 'zen'; +const FIREFOX_SHORTCUTS_GROUP = 'firefox'; +const VALID_SHORTCUT_GROUPS = [ZEN_SHORTCUTS_GROUP, FIREFOX_SHORTCUTS_GROUP]; + +class KeyShortcutModifiers { + #ctrl = false; + #alt = false; + #shift = false; + #meta = false; + + constructor(ctrl, alt, shift, meta) { + this.#ctrl = ctrl; + this.#alt = alt; + this.#shift = shift; + this.#meta = meta; + } + + static parseFromString(modifiers) { + if (modifiers == null) { + return new KeyShortcutModifiers(false, false, false, false); + } + + return new KeyShortcutModifiers(modifiers['ctrl'], modifiers['alt'], modifiers['shift'], modifiers['meta']); + } + + toUserString() { + let str = ''; + if (this.#ctrl) { + str += 'Ctrl+'; + } + if (this.#alt) { + str += AppConstants.platform == 'macosx' ? 'Option+' : 'Alt+'; + } + if (this.#shift) { + str += 'Shift+'; + } + if (this.#meta) { + str += AppConstants.platform == 'macosx' ? 'Cmd+' : 'Win+'; + } + return str; + } + + toString() { + let str = ''; + if (this.#ctrl) { + str += 'accel,'; + } + if (this.#alt) { + str += 'alt,'; + } + if (this.#shift) { + str += 'shift,'; + } + if (this.#meta) { + str += 'meta'; + } + return str; + } + + toJSONString() { + return { + ctrl: this.#ctrl, + alt: this.#alt, + shift: this.#shift, + meta: this.#meta, + }; + } + + areAnyActive() { + return this.#ctrl || this.#alt || this.#shift || this.#meta; + } +} + +class KeyShortcut { + #id = ''; + #key = ''; + #keycode = ''; + #group = FIREFOX_SHORTCUTS_GROUP; + #modifiers = new KeyShortcutModifiers(false, false, false, false); + #action = ''; + #l10nId = ''; + #disabled = false; + #reserved = false; + #internal = false; + + constructor(id, key, keycode, group, modifiers, action, l10nId, disabled, reserved, internal) { + this.#id = id; + this.#key = key; + this.#keycode = keycode; + + if (!VALID_SHORTCUT_GROUPS.includes(group)) { + throw new Error('Illegal group value: ' + group); + } + + this.#group = group; + this.#modifiers = modifiers; + this.#action = action; + this.#l10nId = l10nId; + this.#disabled = disabled; + this.#reserved = reserved; + this.#internal = internal; + } + + static parseFromSaved(json) { + let rv = []; + + for (let key of json) { + rv.push(this.#parseFromJSON(key)); + } + + return rv; + } + + static #parseFromJSON(json) { + return new KeyShortcut( + json['id'], + json['key'], + json['keycode'], + json['group'], + KeyShortcutModifiers.parseFromString(json['modifiers']), + json['action'], + json['l10nId'], + json['disabled'] == 'true', + json['reserved'] == 'true', + json['internal'] == 'true' + ); + } + + static parseFromXHTML(key, group) { + return new KeyShortcut( + key.getAttribute('id'), + key.getAttribute('key'), + key.getAttribute('keycode'), + group, + KeyShortcutModifiers.parseFromString(key.getAttribute('modifiers')), + key.getAttribute('command'), + key.getAttribute('data-l10n-id'), + key.getAttribute('disabled') == 'true', + key.getAttribute('reserved') == 'true', + key.getAttribute('internal') == 'true' + ); + } + + toXHTMLElement() { + let str = '`; + + return window.MozXULElement.parseXULToFragment(str); + } + + getID() { + return this.#id; + } + + getAction() { + return this.#action; + } + + getL10NID() { + return this.#l10nId; + } + + getGroup() { + return this.#group; + } + + setModifiers(modifiers) { + if ((!modifiers) instanceof KeyShortcutModifiers) { + throw new Error('Only KeyShortcutModifiers allowed'); + } + this.#modifiers = modifiers; + } + + toJSONForm() { + return { + id: this.#id, + key: this.#key, + keycode: this.#keycode, + group: this.#group, + l10nId: this.#l10nId, + modifiers: this.#modifiers.toJSONString(), + action: this.#action, + disabled: this.#disabled, + reserved: this.#reserved, + internal: this.#internal, + }; + } + + toUserString() { + let str = this.#modifiers.toUserString(); + if (this.#key) { + str += this.#key; + } else if (this.#keycode) { + str += this.#keycode; + } else if (this.#id) { + str += this.#id; + } else { + return ''; + } + return str; + } + + isUserEditable() { + if (!this.#id || !this.#action || this.#action == '' || this.#internal || this.#reserved) { + return false; + } + return true; + } + + clearKeybind() { + this.#key = ''; + this.#keycode = ''; + this.#modifiers = null; + } +} + +var gZenKeyboardShortcutsManager = { init() { - if (!Services.prefs.getBoolPref("zen.keyboard.shortcuts.enabled")) { - return; + if (window.location.href == 'chrome://browser/content/browser.xhtml') { + console.info('Zen CKS: Initializing shortcuts'); + + this._currentShortcutList = this._loadSaved(); + this._applyShortcuts(); + this._saveShortcuts(); + + console.info('Zen CKS: Initialized'); } - this._initShortcuts(); }, - get _savedShortcuts() { - if (!this.__savedShortcuts) { - try { - const data = Services.prefs.getStringPref(kZKSStorageKey); - if (data.length == 0) { - this._startUpShortcuts(); - return this._savedShortcuts; - } - this.__savedShortcuts = JSON.parse(data); - } catch (e) { - console.error("Zen CKS: Error parsing saved shortcuts", e); - this.__savedShortcuts = {}; - } + _loadSaved() { + let data = JSON.parse(Services.prefs.getStringPref(SHORTCUTS_STORAGE_KEY)); + if (!data || data.length == 0) { + return this._loadDefaults(); + } + + try { + return KeyShortcut.parseFromSaved(data); + } catch (e) { + console.error('Zen CKS: Error parsing saved shortcuts. Resetting to defaults...', e); + return this._loadDefaults(); } - return this.__savedShortcuts; }, - _startUpShortcuts() { - this.__savedShortcuts = {}; - this._addDefaultShortcuts(); - this._saveShortcuts(); + _loadDefaults() { + let newShortcutList = []; + + // Firefox's standard keyset + for (let key of _mainKeyset.children) { + let parsed = KeyShortcut.parseFromXHTML(key, FIREFOX_SHORTCUTS_GROUP); + newShortcutList.push(parsed); + } + + // TODO: Add Zen's custom actions + + return newShortcutList; + }, + + _applyShortcuts() { + console.debug(window.location.href); + let keySet = document.getElementById('mainKeyset'); + if (!keySet) { + throw new Error('Main keyset not found'); + } + + let parent = keySet.parentElement; + + keySet.remove(); + keySet.innerHTML = ''; + + for (let key of this._currentShortcutList) { + let child = key.toXHTMLElement(); + keySet.appendChild(child); + } + + parent.prepend(keySet); + console.debug(document.getElementById('mainKeyset')); }, _saveShortcuts() { - Services.prefs.setStringPref( - kZKSStorageKey, - JSON.stringify(this._savedShortcuts), - ); + let json = []; + for (shortcut of this._currentShortcutList) { + json.push(shortcut.toJSONForm()); + } + + Services.prefs.setStringPref(SHORTCUTS_STORAGE_KEY, JSON.stringify(json)); }, - _parseDefaultShortcut(shortcut) { - let ctrl = shortcut.includes("Ctrl+"); - let alt = shortcut.includes("Alt+") || shortcut.includes("Option+"); - let shift = shortcut.includes("Shift+"); - let meta = shortcut.includes("Meta+") || shortcut.includes("Cmd+"); - let key = shortcut.replace( - /Ctrl\+|Alt\+|Option\+|Shift\+|Meta\+|Cmd\+/g, - "", - ); - if ( - [ - "Tab", - "Enter", - "Escape", - "Space", - "ArrowLeft", - "ArrowRight", - "ArrowUp", - "ArrowDown", - ].includes(key) - ) { - return { ctrl, alt, shift, meta, key: undefined, keycode: key }; + setShortcut(action, newShortcut, modifiers) { + if (!action) { + throw new Error('Action cannot be null'); } - let isKeyCode = key.length > 1; - return { - ctrl, - alt, - shift, - meta, - key: isKeyCode ? undefined : key, - keycode: isKeyCode ? key : undefined, - }; - }, - _addDefaultShortcuts() { - for (let action in kZenDefaultShortcuts) { - if (!this._savedShortcuts[action]) { - this._savedShortcuts[action] = this._parseDefaultShortcut( - kZenDefaultShortcuts[action], - ); - } + // Unsetting shortcut + let targetShortcut = this._currentShortcutList.filter((key) => key.getAction() == action)[0]; + if (!targetShortcut) { + throw new Error('Shortcut for action ' + action + ' not found'); } - }, - setShortcut(id, shortcut) { - if (!shortcut) { - delete this._savedShortcuts[id]; - } else if (this.isValidShortcut(shortcut)) { - this._savedShortcuts[id] = shortcut; + console.debug(targetShortcut.toJSONForm()); + + if (!newShortcut && !modifiers) { + targetShortcut.clearKeybind(); + } else { + targetShortcut.setModifiers(modifiers); } + + this._applyShortcuts(); this._saveShortcuts(); }, - _initShortcuts() { - if (window.location.href == "chrome://browser/content/browser.xhtml") { - console.info("Zen CKS: Initializing shortcuts"); - Services.prefs.addObserver( - kZKSStorageKey, - this._onShortcutChange.bind(this), - ); - Services.prefs.addObserver( - "zen.keyboard.shortcuts.disable-firefox", - this._disableFirefoxShortcuts.bind(this), - ); - this._initSavedShortcuts(); - this._disableFirefoxShortcuts(); - } - }, - - _disableFirefoxShortcuts() { - let disable = Services.prefs.getBoolPref( - "zen.keyboard.shortcuts.disable-firefox", - ); - if (!disable) { - return; - } - window.SessionStore.promiseInitialized.then(() => { - let keySet = document.getElementById("mainKeyset"); - if (!keySet) { - throw new Error("Zen CKS: No main keyset found"); - } - for (let child of keySet.children) { - if (!child.id.startsWith("zen-key_")) { - child.setAttribute("disabled", true); - } - } - console.info("Remove already exist shortcut keys"); - }); - }, - - _onShortcutChange() { - console.info("Zen CKS: Shortcut changed"); - this.__savedShortcuts = null; - this._initSavedShortcuts(true); - }, - - _getCommandAttribute(action) { - if (action.startsWith("command:")) { - return `command="${action.substring(8)}"`; - } - return `oncommand="${action}"`; - }, - - _createShortcutElement(_action) { - let shortcut = this._savedShortcuts[_action]; - if (!shortcut) { - return null; + getModifiableShortcuts() { + let rv = []; + if (!this._currentShortcutList) { + this._currentShortcutList = this._loadSaved(); } - const action = kZKSActions[_action][0]; - const key = shortcut.key.toUpperCase(); - let modifiers = { - accel: shortcut.ctrl, - alt: shortcut.alt, - shift: shortcut.shift, - meta: shortcut.meta, - }; - - modifiers = Object.keys(modifiers) - .filter((mod) => modifiers[mod]) - .join(","); - - return window.MozXULElement.parseXULToFragment(` - - `); - }, - - _initSavedShortcuts(fromUpdate = false) { - let keySet = document.getElementById("mainKeyset"); - if (!keySet) { - throw new Error("Zen CKS: No main keyset found"); - } - - for (let action in kZKSActions) { - let id = `zen-key_${action}`; - let existing = document.getElementById(id); - if (existing) { - existing.remove(); - } - let shortcut = this._createShortcutElement(action); - if (shortcut) { - keySet.prepend(shortcut); + for (let shortcut of this._currentShortcutList) { + if (shortcut.isUserEditable()) { + rv.push(shortcut); } } - this._fixMainKeyset(); - }, - - _fixMainKeyset() { - let keySet = document.getElementById("mainKeyset"); - if (!keySet) { - throw new Error("Zen CKS: No main keyset found"); - } - const parent = keySet.parentElement; - // We need to re-append the main keyset to the document to make the shortcuts work - keySet.remove(); - parent.prepend(keySet); - }, - - getShortcut(action) { - return this._savedShortcuts[action]; - }, - - isValidShortcut(shortcut) { - return shortcut && (shortcut.key || shortcut.keycode); - }, - - shortCutToString(shortcut) { - let str = ""; - if (shortcut.ctrl) { - str += "Ctrl+"; - } - if (shortcut.alt) { - str += AppConstants.platform == "macosx" ? "Option+" : "Alt+"; - } - if (shortcut.shift) { - str += "Shift+"; - } - if (shortcut.meta) { - str += AppConstants.platform == "macosx" ? "Cmd+" : "Meta+"; - } - if (shortcut.key) { - str += shortcut.key; - } - return str; + return rv; }, };