1 /* See license.txt for terms of usage */ 2 /* Reused code from Keyconfig by Dorando: http://mozilla.dorando.at/keyconfig.xpi*/ 3 4 // ************************************************************************************************ 5 // Constants 6 7 const Cc = Components.classes; 8 const Ci = Components.interfaces; 9 10 Components.utils.import("resource://gre/modules/Services.jsm"); 11 12 var branch = Services.prefs.getBranch("extensions.firebug.key.shortcut."); 13 var defaultBranch = Services.prefs.getDefaultBranch("extensions.firebug.key.shortcut."); 14 15 // Initialized from window parameters. 16 var FBL; 17 var FBTrace; 18 var Locale; 19 20 // Global variables used by this dialog. 21 var shortcutNames = null; 22 var gVKNames = []; 23 var gLocaleKeys = []; 24 var gPlatformKeys = new Object(); 25 var updatedShortcuts = {} 26 var modified = false; 27 var mustBeKeyChars = { 28 VK_SEMICOLON : ";", 29 VK_EQUALS : "=", 30 VK_MULTIPLY : "*", 31 VK_ADD : "+", 32 VK_SUBTRACT : "-", 33 VK_DECIMAL : ".", 34 VK_DIVIDE : "/", 35 VK_COMMA : ",", 36 VK_PERIOD : ".", 37 VK_SLASH : "/", 38 VK_BACK_QUOTE : "`", 39 VK_OPEN_BRACKET : "[", 40 VK_BACK_SLASH : "\\", 41 VK_CLOSE_BRACKET : "]", 42 VK_QUOTE : "'" 43 }; 44 45 // ************************************************************************************************ 46 // Implemantation 47 48 function init() 49 { 50 var args = window.arguments[0]; 51 FBL = args.FBL; 52 FBTrace = args.FBTrace; 53 Locale = FBL; 54 55 setKeyInfo(); 56 shortcutNames = branch.getChildList("", {}); 57 shortcutNames.sort(); 58 shortcutNames.forEach(addShortcutRow); 59 setHandlers(); 60 document.title = Locale.$STR("customizeShortcuts"); 61 62 if (FBTrace.DBG_SHORTCUTS) 63 FBTrace.sysout("shortcuts.init; Customize Shortcuts dialog initialized."); 64 } 65 66 function setKeyInfo() 67 { 68 gLocaleKeys = document.getElementById("localeKeys"); 69 var platformKeys = document.getElementById("platformKeys"); 70 gPlatformKeys.shift = Locale.$STR("VK_SHIFT", platformKeys); 71 gPlatformKeys.meta = Locale.$STR("VK_META", platformKeys); 72 gPlatformKeys.alt = Locale.$STR("VK_ALT", platformKeys); 73 gPlatformKeys.ctrl = Locale.$STR("VK_CONTROL", platformKeys); 74 gPlatformKeys.sep = Locale.$STR("MODIFIER_SEPARATOR", platformKeys); 75 76 switch (Services.prefs.getIntPref("ui.key.accelKey")) 77 { 78 case KeyEvent.DOM_VK_CONTROL: 79 gPlatformKeys.accel = gPlatformKeys.ctrl; 80 break; 81 case KeyEvent.DOM_VK_ALT: 82 gPlatformKeys.accel = gPlatformKeys.alt; 83 break; 84 case KeyEvent.DOM_VK_META: 85 gPlatformKeys.accel = gPlatformKeys.meta; 86 break; 87 default: 88 gPlatformKeys.accel = (window.navigator.platform.search("Mac") == 0 ? gPlatformKeys.meta : gPlatformKeys.ctrl); 89 } 90 91 for ( var property in KeyEvent) 92 { 93 gVKNames[KeyEvent[property]] = property.replace("DOM_", ""); 94 } 95 gVKNames[8] = "VK_BACK"; 96 } 97 98 function setHandlers() 99 { 100 var i; 101 var shortcutSinks = document.getElementsByClassName("shortcutSink"); 102 for (i = 0; i < shortcutSinks.length; i++) 103 shortcutSinks[i].addEventListener("keydown", recognizeShortcut, false); 104 105 var resetBtns = document.getElementsByClassName("shortcutResetBtn"); 106 for (i = 0; i < resetBtns.length; i++) 107 resetBtns[i].addEventListener("command", handleResetBtn, false); 108 } 109 110 function saveChanges() 111 { 112 if (!modified) 113 return true; 114 115 shortcutNames.forEach(saveShortcut); 116 117 var e = Services.wm.getEnumerator("navigator:browser"); 118 while (e.hasMoreElements()) 119 { 120 var fbug = e.getNext().Firebug 121 fbug && fbug.ShortcutsModel.initShortcuts(); 122 } 123 return true; 124 } 125 126 function saveShortcut(shortcutId, index, array) 127 { 128 if (shortcutId in updatedShortcuts) 129 branch.setCharPref(shortcutId, updatedShortcuts[shortcutId]); 130 } 131 132 function handleResetBtn(event) 133 { 134 var element = event.target.id.replace("_reset", ""); 135 if (branch.prefHasUserValue(element)) 136 { 137 branch.clearUserPref(element); 138 modified = true; 139 } 140 141 event.target.hidden = true; 142 var textbox = document.getElementById(element + "_shortcut"); 143 if (textbox) 144 textbox.value = getHumanShortcut(element); 145 } 146 147 function getHumanShortcut(element, getDefault) 148 { 149 var shortcut = (getDefault ? defaultBranch : branch).getCharPref(element); 150 var tokens = shortcut.split(" "); 151 var keyCode = tokens.pop(); 152 153 if (keyCode.length == 1) 154 return getFormattedKey(tokens.join(","), keyCode, null); 155 else 156 return getFormattedKey(tokens.join(","), null, keyCode); 157 } 158 159 function addShortcutRow(element, index, array) 160 { 161 // Get key configuration from preference 162 var shortcut = getHumanShortcut(element); 163 var defaultShortcut = getHumanShortcut(element, true); 164 var rows = document.getElementById("shortcutGridRows"); 165 var row = document.createElement("row"); 166 167 var label = document.createElement("label"); 168 // Get the label from firebug.properties 169 var labelText = Locale.$STR("firebug.shortcut."+element+".label"); 170 var tooltipText = Locale.$STR("firebug.shortcut.tip."+element); 171 172 // $STR defaults to property name (label) if it's not defined. We don't want that 173 if (labelText == "label") 174 labelText = element; 175 label.setAttribute("value", labelText); 176 row.appendChild(label); 177 178 var textbox = document.createElement("textbox"); 179 textbox.id = element + "_shortcut"; 180 textbox.className = "shortcutSink"; 181 row.setAttribute("tooltiptext", tooltipText != "tip" ? tooltipText : ""); 182 textbox.setAttribute("value", shortcut); 183 textbox.setAttribute("default_value", defaultShortcut); 184 row.appendChild(textbox); 185 186 var resetBtn = document.createElement("toolbarbutton"); 187 resetBtn.id = element + "_reset"; 188 resetBtn.setAttribute("label", Locale.$STR("a11y.labels.reset")); 189 resetBtn.setAttribute("aria-label", Locale.$STRF("a11y.labels.reset_shortcut", [labelText])); 190 resetBtn.className = "shortcutResetBtn"; 191 resetBtn.hidden = defaultShortcut == shortcut; 192 row.appendChild(resetBtn); 193 rows.appendChild(row); 194 } 195 196 function recognizeShortcut(event) 197 { 198 // We're using keydown, so we always start with keycode 199 var shortcut = ""; 200 if ([KeyEvent.DOM_VK_TAB, KeyEvent.DOM_VK_SHIFT, KeyEvent.DOM_VK_CONTROL, KeyEvent.DOM_VK_ALT]. 201 indexOf(event.keyCode) != -1 || 202 ((!event.shiftKey && !event.altKey && !event.ctrlKey) && 203 [KeyEvent.DOM_VK_RETURN, KeyEvent.DOM_VK_ESCAPE].indexOf(event.keyCode) != -1)) 204 { 205 // Always let tab pass. Let enter & escape pass, if no modifiers are used 206 return; 207 } 208 209 modified = true; 210 event.preventDefault(); 211 event.stopPropagation(); 212 213 var target = event.target; 214 215 // Backspace pressed 216 if (event.keyCode == 8 && !event.shiftKey && !event.altKey && !event.ctrlKey) 217 { 218 updatedShortcuts[target.id.replace("_shortcut", "")] = ""; 219 target.value = ""; 220 221 // Update reset button visibility 222 target.nextSibling.hidden = false; 223 224 return false; 225 } 226 227 var modifiers = []; 228 if (event.altKey) 229 modifiers.push("alt"); 230 if (event.ctrlKey) 231 modifiers.push("control"); 232 if (event.metaKey) 233 modifiers.push("meta"); 234 if (event.shiftKey) 235 modifiers.push("shift"); 236 237 modifiers = modifiers.join(" "); 238 var keyConstant = key = null; 239 240 keyConstant = gVKNames[event.keyCode]; 241 242 if (!keyConstant) //should not happen 243 return; 244 245 // Check if the keycode is actually a printable character 246 // 1. Convert some of the punctuation keyConstants (e.g. VK_COMMA) back to actual characters 247 if (mustBeKeyChars[keyConstant]) 248 { 249 key = mustBeKeyChars[keyConstant]; 250 } 251 else 252 { 253 // 2. Detect basic alphanumeric keys 254 var keyNameGuess = keyConstant.replace("VK_", ""); 255 if (keyNameGuess.length == 1) 256 key = keyNameGuess.toLowerCase(); 257 } 258 259 if (modifiers.length > 0) 260 shortcut += modifiers+" "; 261 shortcut += (key ? key : keyConstant); 262 263 updatedShortcuts[target.id.replace("_shortcut", "")] = shortcut; 264 265 // Show formatted shortcut in textbox 266 modifiers = modifiers.replace(" ", ","); 267 var formatted = getFormattedKey(modifiers, key, keyConstant); 268 269 target.value = formatted; 270 271 // Update reset button visibility 272 target.nextSibling.hidden = formatted == target.getAttribute("default_value"); 273 return false; 274 } 275 276 function getFormattedKey(modifiers, key, keyConstant) 277 { 278 if ((modifiers == "shift,alt,control,accel" && keyConstant == "VK_SCROLL_LOCK") || 279 (key == "" || (!key && keyConstant == ""))) 280 { 281 return ""; 282 } 283 284 var val = ""; 285 if (modifiers) 286 { 287 val = modifiers.replace(/^[\s,]+|[\s,]+$/g, "").split(/[\s,]+/g).join(gPlatformKeys.sep). 288 replace("alt", gPlatformKeys.alt).replace("shift", gPlatformKeys.shift). 289 replace("control", gPlatformKeys.ctrl).replace("meta", gPlatformKeys.meta). 290 replace("accel", gPlatformKeys.accel) + 291 gPlatformKeys.sep; 292 } 293 294 if (key) 295 return val += key; 296 297 if (keyConstant) 298 { 299 try 300 { 301 // See if a localized version for keyConstant exists (F keys, arrow, enter, pgup, etc.) 302 val += gLocaleKeys.getString(keyConstant); 303 } 304 catch (e) 305 { 306 // Create human friendly alternative ourself 307 val += keyConstant.replace("VK_", "").replace("_", " ").toLowerCase(); 308 } 309 } 310 return val; 311 } 312 313 // ********************************************************************************************* // 314