| 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 |