| OLD | NEW |
| (Empty) |
| 1 /* This Source Code Form is subject to the terms of the Mozilla Public | |
| 2 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| 4 | |
| 5 Cu.import("resource://gre/modules/Services.jsm"); | |
| 6 | |
| 7 let validModifiers = Object.create(null); | |
| 8 validModifiers.ACCEL = null; | |
| 9 validModifiers.CTRL = "control"; | |
| 10 validModifiers.CONTROL = "control"; | |
| 11 validModifiers.SHIFT = "shift"; | |
| 12 validModifiers.ALT = "alt"; | |
| 13 validModifiers.META = "meta"; | |
| 14 | |
| 15 let bindingsKeys = null; | |
| 16 (function() | |
| 17 { | |
| 18 let request = new XMLHttpRequest(); | |
| 19 request.open("GET", "chrome://global/content/platformHTMLBindings.xml"); | |
| 20 request.addEventListener("load", () => | |
| 21 { | |
| 22 bindingsKeys = request.responseXML.getElementsByTagName("handler"); | |
| 23 }); | |
| 24 request.send(); | |
| 25 })(); | |
| 26 | |
| 27 | |
| 28 /** | |
| 29 * Sets the correct value of validModifiers.ACCEL. | |
| 30 */ | |
| 31 function initAccelKey() | |
| 32 { | |
| 33 validModifiers.ACCEL = "control"; | |
| 34 try | |
| 35 { | |
| 36 let accelKey = Services.prefs.getIntPref("ui.key.accelKey"); | |
| 37 if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_CONTROL) | |
| 38 validModifiers.ACCEL = "control"; | |
| 39 else if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_ALT) | |
| 40 validModifiers.ACCEL = "alt"; | |
| 41 else if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_META) | |
| 42 validModifiers.ACCEL = "meta"; | |
| 43 } | |
| 44 catch(e) | |
| 45 { | |
| 46 Cu.reportError(e); | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 exports.KeySelector = KeySelector; | |
| 51 | |
| 52 /** | |
| 53 * This class provides capabilities to find and use available keyboard shortcut | |
| 54 * keys. | |
| 55 * @param {ChromeWindow} window the window where to look up existing shortcut | |
| 56 * keys | |
| 57 * @constructor | |
| 58 */ | |
| 59 function KeySelector(window) | |
| 60 { | |
| 61 this._initExistingShortcuts(window); | |
| 62 } | |
| 63 KeySelector.prototype = | |
| 64 { | |
| 65 /** | |
| 66 * Map listing existing shortcut keys as its keys. | |
| 67 * @type Object | |
| 68 */ | |
| 69 _existingShortcuts: null, | |
| 70 | |
| 71 /** | |
| 72 * Sets up _existingShortcuts property for a window. | |
| 73 */ | |
| 74 _initExistingShortcuts: function(/**ChromeWindow*/ window) | |
| 75 { | |
| 76 if (!validModifiers.ACCEL) | |
| 77 initAccelKey(); | |
| 78 | |
| 79 this._existingShortcuts = Object.create(null); | |
| 80 | |
| 81 let keys = Array.prototype.slice.apply(window.document.getElementsByTagName(
"key")); | |
| 82 if (bindingsKeys) | |
| 83 keys.push.apply(keys, bindingsKeys); | |
| 84 for (let i = 0; i < keys.length; i++) | |
| 85 { | |
| 86 let key = keys[i]; | |
| 87 let keyData = | |
| 88 { | |
| 89 shift: false, | |
| 90 meta: false, | |
| 91 alt: false, | |
| 92 control: false, | |
| 93 char: null, | |
| 94 code: null | |
| 95 }; | |
| 96 | |
| 97 let keyChar = key.getAttribute("key"); | |
| 98 if (keyChar && keyChar.length == 1) | |
| 99 keyData.char = keyChar.toUpperCase(); | |
| 100 | |
| 101 let keyCode = key.getAttribute("keycode"); | |
| 102 if (keyCode && "DOM_" + keyCode.toUpperCase() in Ci.nsIDOMKeyEvent) | |
| 103 keyData.code = Ci.nsIDOMKeyEvent["DOM_" + keyCode.toUpperCase()]; | |
| 104 | |
| 105 if (!keyData.char && !keyData.code) | |
| 106 continue; | |
| 107 | |
| 108 let keyModifiers = key.getAttribute("modifiers"); | |
| 109 if (keyModifiers) | |
| 110 for (let modifier of keyModifiers.toUpperCase().match(/\w+/g)) | |
| 111 if (modifier in validModifiers) | |
| 112 keyData[validModifiers[modifier]] = true; | |
| 113 | |
| 114 let canonical = [keyData.shift, keyData.meta, keyData.alt, keyData.control
, keyData.char || keyData.code].join(" "); | |
| 115 this._existingShortcuts[canonical] = true; | |
| 116 } | |
| 117 }, | |
| 118 | |
| 119 /** | |
| 120 * Selects a keyboard shortcut variant that isn't already taken, | |
| 121 * parses it into an object. | |
| 122 */ | |
| 123 selectKey: function(/**String*/ variants) /**Object*/ | |
| 124 { | |
| 125 for (let variant of variants.split(/\s*,\s*/)) | |
| 126 { | |
| 127 if (!variant) | |
| 128 continue; | |
| 129 | |
| 130 let keyData = | |
| 131 { | |
| 132 shift: false, | |
| 133 meta: false, | |
| 134 alt: false, | |
| 135 control: false, | |
| 136 char: null, | |
| 137 code: null, | |
| 138 codeName: null | |
| 139 }; | |
| 140 for (let part of variant.toUpperCase().split(/\s+/)) | |
| 141 { | |
| 142 if (part in validModifiers) | |
| 143 keyData[validModifiers[part]] = true; | |
| 144 else if (part.length == 1) | |
| 145 keyData.char = part; | |
| 146 else if ("DOM_VK_" + part in Ci.nsIDOMKeyEvent) | |
| 147 { | |
| 148 keyData.code = Ci.nsIDOMKeyEvent["DOM_VK_" + part]; | |
| 149 keyData.codeName = "VK_" + part; | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 if (!keyData.char && !keyData.code) | |
| 154 continue; | |
| 155 | |
| 156 let canonical = [keyData.shift, keyData.meta, keyData.alt, keyData.control
, keyData.char || keyData.code].join(" "); | |
| 157 if (canonical in this._existingShortcuts) | |
| 158 continue; | |
| 159 | |
| 160 return keyData; | |
| 161 } | |
| 162 | |
| 163 return null; | |
| 164 } | |
| 165 }; | |
| 166 | |
| 167 /** | |
| 168 * Creates the text representation for a key. | |
| 169 * @static | |
| 170 */ | |
| 171 KeySelector.getTextForKey = function (/**Object*/ key) /**String*/ | |
| 172 { | |
| 173 if (!key) | |
| 174 return null; | |
| 175 | |
| 176 if (!("text" in key)) | |
| 177 { | |
| 178 key.text = null; | |
| 179 try | |
| 180 { | |
| 181 let stringBundle = Services.strings.createBundle("chrome://global-platform
/locale/platformKeys.properties"); | |
| 182 let parts = []; | |
| 183 if (key.control) | |
| 184 parts.push(stringBundle.GetStringFromName("VK_CONTROL")); | |
| 185 if (key.alt) | |
| 186 parts.push(stringBundle.GetStringFromName("VK_ALT")); | |
| 187 if (key.meta) | |
| 188 parts.push(stringBundle.GetStringFromName("VK_META")); | |
| 189 if (key.shift) | |
| 190 parts.push(stringBundle.GetStringFromName("VK_SHIFT")); | |
| 191 if (key.char) | |
| 192 parts.push(key.char.toUpperCase()); | |
| 193 else | |
| 194 { | |
| 195 let stringBundle2 = Services.strings.createBundle("chrome://global/local
e/keys.properties"); | |
| 196 parts.push(stringBundle2.GetStringFromName(key.codeName)); | |
| 197 } | |
| 198 key.text = parts.join(stringBundle.GetStringFromName("MODIFIER_SEPARATOR")
); | |
| 199 } | |
| 200 catch (e) | |
| 201 { | |
| 202 Cu.reportError(e); | |
| 203 return null; | |
| 204 } | |
| 205 } | |
| 206 return key.text; | |
| 207 }; | |
| 208 | |
| 209 /** | |
| 210 * Tests whether a keypress event matches the given key. | |
| 211 * @static | |
| 212 */ | |
| 213 KeySelector.matchesKey = function(/**Event*/ event, /**Object*/ key) /**Boolean*
/ | |
| 214 { | |
| 215 if (event.defaultPrevented || !key) | |
| 216 return false; | |
| 217 if (key.shift != event.shiftKey || key.alt != event.altKey) | |
| 218 return false; | |
| 219 if (key.meta != event.metaKey || key.control != event.ctrlKey) | |
| 220 return false; | |
| 221 | |
| 222 if (key.char && event.charCode && String.fromCharCode(event.charCode).toUpperC
ase() == key.char) | |
| 223 return true; | |
| 224 if (key.code && event.keyCode && event.keyCode == key.code) | |
| 225 return true; | |
| 226 return false; | |
| 227 }; | |
| OLD | NEW |