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