Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2009 Apple Inc. All rights reserved.
      3  * Copyright (C) 2009 Google Inc. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  * 1.  Redistributions of source code must retain the above copyright
     10  *     notice, this list of conditions and the following disclaimer.
     11  * 2.  Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     15  *     its contributors may be used to endorse or promote products derived
     16  *     from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 /**
     31  * @constructor
     32  */
     33 WebInspector.KeyboardShortcut = function()
     34 {
     35 }
     36 
     37 /**
     38  * Constants for encoding modifier key set as a bit mask.
     39  * @see #_makeKeyFromCodeAndModifiers
     40  */
     41 WebInspector.KeyboardShortcut.Modifiers = {
     42     None: 0,   // Constant for empty modifiers set.
     43     Shift: 1,
     44     Ctrl: 2,
     45     Alt: 4,
     46     Meta: 8,   // Command key on Mac, Win key on other platforms.
     47     get CtrlOrMeta()
     48     {
     49         // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
     50         return WebInspector.isMac() ? this.Meta : this.Ctrl;
     51     },
     52     get ShiftOrOption()
     53     {
     54         // Shift on Mac, Alt on other platforms
     55         return WebInspector.isMac() ? this.Shift : this.Alt;
     56     }
     57 };
     58 
     59 /** @typedef {!{code: number, name: (string|!Object.<string, string>)}} */
     60 WebInspector.KeyboardShortcut.Key;
     61 
     62 /** @type {!Object.<string, !WebInspector.KeyboardShortcut.Key>} */
     63 WebInspector.KeyboardShortcut.Keys = {
     64     Backspace: { code: 8, name: "\u21a4" },
     65     Tab: { code: 9, name: { mac: "\u21e5", other: "Tab" } },
     66     Enter: { code: 13, name: { mac: "\u21a9", other: "Enter" } },
     67     Ctrl: { code: 17, name: "Ctrl" },
     68     Esc: { code: 27, name: { mac: "\u238b", other: "Esc" } },
     69     Space: { code: 32, name: "Space" },
     70     PageUp: { code: 33,  name: { mac: "\u21de", other: "PageUp" } },      // also NUM_NORTH_EAST
     71     PageDown: { code: 34, name: { mac: "\u21df", other: "PageDown" } },   // also NUM_SOUTH_EAST
     72     End: { code: 35, name: { mac: "\u2197", other: "End" } },             // also NUM_SOUTH_WEST
     73     Home: { code: 36, name: { mac: "\u2196", other: "Home" } },           // also NUM_NORTH_WEST
     74     Left: { code: 37, name: "\u2190" },           // also NUM_WEST
     75     Up: { code: 38, name: "\u2191" },             // also NUM_NORTH
     76     Right: { code: 39, name: "\u2192" },          // also NUM_EAST
     77     Down: { code: 40, name: "\u2193" },           // also NUM_SOUTH
     78     Delete: { code: 46, name: "Del" },
     79     Zero: { code: 48, name: "0" },
     80     H: { code: 72, name: "H" },
     81     Meta: { code: 91, name: "Meta" },
     82     F1: { code: 112, name: "F1" },
     83     F2: { code: 113, name: "F2" },
     84     F3: { code: 114, name: "F3" },
     85     F4: { code: 115, name: "F4" },
     86     F5: { code: 116, name: "F5" },
     87     F6: { code: 117, name: "F6" },
     88     F7: { code: 118, name: "F7" },
     89     F8: { code: 119, name: "F8" },
     90     F9: { code: 120, name: "F9" },
     91     F10: { code: 121, name: "F10" },
     92     F11: { code: 122, name: "F11" },
     93     F12: { code: 123, name: "F12" },
     94     Semicolon: { code: 186, name: ";" },
     95     NumpadPlus: { code: 107, name: "Numpad +" },
     96     NumpadMinus: { code: 109, name: "Numpad -" },
     97     Numpad0: { code: 96, name: "Numpad 0" },
     98     Plus: { code: 187, name: "+" },
     99     Comma: { code: 188, name: "," },
    100     Minus: { code: 189, name: "-" },
    101     Period: { code: 190, name: "." },
    102     Slash: { code: 191, name: "/" },
    103     QuestionMark: { code: 191, name: "?" },
    104     Apostrophe: { code: 192, name: "`" },
    105     Tilde: { code: 192, name: "Tilde" },
    106     Backslash: { code: 220, name: "\\" },
    107     SingleQuote: { code: 222, name: "\'" },
    108     get CtrlOrMeta()
    109     {
    110         // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
    111         return WebInspector.isMac() ? this.Meta : this.Ctrl;
    112     },
    113 };
    114 
    115 WebInspector.KeyboardShortcut.KeyBindings = {};
    116 
    117 (function() {
    118     for (var key in WebInspector.KeyboardShortcut.Keys) {
    119         var descriptor = WebInspector.KeyboardShortcut.Keys[key];
    120         if (typeof descriptor === "object" && descriptor["code"]) {
    121             var name = typeof descriptor["name"] === "string" ? descriptor["name"] : key;
    122             WebInspector.KeyboardShortcut.KeyBindings[name] = { code: descriptor["code"] };
    123         }
    124     }
    125 })();
    126 
    127 /**
    128  * Creates a number encoding keyCode in the lower 8 bits and modifiers mask in the higher 8 bits.
    129  * It is useful for matching pressed keys.
    130  *
    131  * @param {number|string} keyCode The code of the key, or a character "a-z" which is converted to a keyCode value.
    132  * @param {number=} modifiers Optional list of modifiers passed as additional parameters.
    133  * @return {number}
    134  */
    135 WebInspector.KeyboardShortcut.makeKey = function(keyCode, modifiers)
    136 {
    137     if (typeof keyCode === "string")
    138         keyCode = keyCode.charCodeAt(0) - (/^[a-z]/.test(keyCode) ? 32 : 0);
    139     modifiers = modifiers || WebInspector.KeyboardShortcut.Modifiers.None;
    140     return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, modifiers);
    141 }
    142 
    143 /**
    144  * @param {?KeyboardEvent} keyboardEvent
    145  * @return {number}
    146  */
    147 WebInspector.KeyboardShortcut.makeKeyFromEvent = function(keyboardEvent)
    148 {
    149     var modifiers = WebInspector.KeyboardShortcut.Modifiers.None;
    150     if (keyboardEvent.shiftKey)
    151         modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
    152     if (keyboardEvent.ctrlKey)
    153         modifiers |= WebInspector.KeyboardShortcut.Modifiers.Ctrl;
    154     if (keyboardEvent.altKey)
    155         modifiers |= WebInspector.KeyboardShortcut.Modifiers.Alt;
    156     if (keyboardEvent.metaKey)
    157         modifiers |= WebInspector.KeyboardShortcut.Modifiers.Meta;
    158 
    159     function keyCodeForEvent(keyboardEvent)
    160     {
    161         // Use either a real or a synthetic keyCode (for events originating from extensions).
    162         return keyboardEvent.keyCode || keyboardEvent["__keyCode"];
    163     }
    164     return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCodeForEvent(keyboardEvent), modifiers);
    165 }
    166 
    167 /**
    168  * @param {?KeyboardEvent} event
    169  * @return {boolean}
    170  */
    171 WebInspector.KeyboardShortcut.eventHasCtrlOrMeta = function(event)
    172 {
    173     return WebInspector.isMac() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
    174 }
    175 
    176 /**
    177  * @param {?Event} event
    178  * @return {boolean}
    179  */
    180 WebInspector.KeyboardShortcut.hasNoModifiers = function(event)
    181 {
    182     return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
    183 }
    184 
    185 /** @typedef {!{key: number, name: string}} */
    186 WebInspector.KeyboardShortcut.Descriptor;
    187 
    188 /**
    189  * @param {string|!WebInspector.KeyboardShortcut.Key} key
    190  * @param {number=} modifiers
    191  * @return {!WebInspector.KeyboardShortcut.Descriptor}
    192  */
    193 WebInspector.KeyboardShortcut.makeDescriptor = function(key, modifiers)
    194 {
    195     return {
    196         key: WebInspector.KeyboardShortcut.makeKey(typeof key === "string" ? key : key.code, modifiers),
    197         name: WebInspector.KeyboardShortcut.shortcutToString(key, modifiers)
    198     };
    199 }
    200 
    201 /**
    202  * @param {string} shortcut
    203  * @return {number}
    204  */
    205 WebInspector.KeyboardShortcut.makeKeyFromBindingShortcut = function(shortcut)
    206 {
    207     var parts = shortcut.split(/\+(?!$)/);
    208     var modifiers = 0;
    209     for (var i = 0; i < parts.length; ++i) {
    210         if (typeof WebInspector.KeyboardShortcut.Modifiers[parts[i]] !== "undefined") {
    211             modifiers |= WebInspector.KeyboardShortcut.Modifiers[parts[i]];
    212             continue;
    213         }
    214         console.assert(i === parts.length - 1, "Modifiers-only shortcuts are not allowed (encountered <" + shortcut + ">)");
    215         var key = WebInspector.KeyboardShortcut.Keys[parts[i]] || WebInspector.KeyboardShortcut.KeyBindings[parts[i]];
    216         if (key && key.shiftKey)
    217             modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
    218         return WebInspector.KeyboardShortcut.makeKey(key ? key.code : parts[i].toLowerCase(), modifiers)
    219     }
    220     console.assert(false);
    221     return 0;
    222 }
    223 
    224 /**
    225  * @param {string|!WebInspector.KeyboardShortcut.Key} key
    226  * @param {number=} modifiers
    227  * @return {string}
    228  */
    229 WebInspector.KeyboardShortcut.shortcutToString = function(key, modifiers)
    230 {
    231     return WebInspector.KeyboardShortcut._modifiersToString(modifiers) + WebInspector.KeyboardShortcut._keyName(key);
    232 }
    233 
    234 /**
    235  * @param {string|!WebInspector.KeyboardShortcut.Key} key
    236  * @return {string}
    237  */
    238 WebInspector.KeyboardShortcut._keyName = function(key)
    239 {
    240     if (typeof key === "string")
    241         return key.toUpperCase();
    242     if (typeof key.name === "string")
    243         return key.name;
    244     return key.name[WebInspector.platform()] || key.name.other || '';
    245 }
    246 
    247 /**
    248  * @param {number} keyCode
    249  * @param {?number} modifiers
    250  * @return {number}
    251  */
    252 WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers = function(keyCode, modifiers)
    253 {
    254     return (keyCode & 255) | (modifiers << 8);
    255 };
    256 
    257 /**
    258  * @param {number} key
    259  * @return {!{keyCode: number, modifiers: number}}
    260  */
    261 WebInspector.KeyboardShortcut.keyCodeAndModifiersFromKey = function(key)
    262 {
    263     return { keyCode: key & 255, modifiers: key >> 8 };
    264 }
    265 
    266 /**
    267  * @param {number|undefined} modifiers
    268  * @return {string}
    269  */
    270 WebInspector.KeyboardShortcut._modifiersToString = function(modifiers)
    271 {
    272     const cmdKey = "\u2318";
    273     const optKey = "\u2325";
    274     const shiftKey = "\u21e7";
    275     const ctrlKey = "\u2303";
    276 
    277     var isMac = WebInspector.isMac();
    278     var res = "";
    279     if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Ctrl)
    280         res += isMac ? ctrlKey : "Ctrl + ";
    281     if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Alt)
    282         res += isMac ? optKey : "Alt + ";
    283     if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Shift)
    284         res += isMac ? shiftKey : "Shift + ";
    285     if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Meta)
    286         res += isMac ? cmdKey : "Win + ";
    287 
    288     return res;
    289 };
    290 
    291 WebInspector.KeyboardShortcut.SelectAll = WebInspector.KeyboardShortcut.makeKey("a", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
    292