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         // Option on Mac, Shift on other platforms
     55         return WebInspector.isMac() ? this.Alt : this.Shift;
     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     Shift: { code: 16, name: { mac: "\u21e7", other: "Shift" } },
     68     Ctrl: { code: 17, name: "Ctrl" },
     69     Esc: { code: 27, name: { mac: "\u238b", other: "Esc" } },
     70     Space: { code: 32, name: "Space" },
     71     PageUp: { code: 33,  name: { mac: "\u21de", other: "PageUp" } },      // also NUM_NORTH_EAST
     72     PageDown: { code: 34, name: { mac: "\u21df", other: "PageDown" } },   // also NUM_SOUTH_EAST
     73     End: { code: 35, name: { mac: "\u2197", other: "End" } },             // also NUM_SOUTH_WEST
     74     Home: { code: 36, name: { mac: "\u2196", other: "Home" } },           // also NUM_NORTH_WEST
     75     Left: { code: 37, name: "\u2190" },           // also NUM_WEST
     76     Up: { code: 38, name: "\u2191" },             // also NUM_NORTH
     77     Right: { code: 39, name: "\u2192" },          // also NUM_EAST
     78     Down: { code: 40, name: "\u2193" },           // also NUM_SOUTH
     79     Delete: { code: 46, name: "Del" },
     80     Zero: { code: 48, name: "0" },
     81     H: { code: 72, name: "H" },
     82     Meta: { code: 91, name: "Meta" },
     83     F1: { code: 112, name: "F1" },
     84     F2: { code: 113, name: "F2" },
     85     F3: { code: 114, name: "F3" },
     86     F4: { code: 115, name: "F4" },
     87     F5: { code: 116, name: "F5" },
     88     F6: { code: 117, name: "F6" },
     89     F7: { code: 118, name: "F7" },
     90     F8: { code: 119, name: "F8" },
     91     F9: { code: 120, name: "F9" },
     92     F10: { code: 121, name: "F10" },
     93     F11: { code: 122, name: "F11" },
     94     F12: { code: 123, name: "F12" },
     95     Semicolon: { code: 186, name: ";" },
     96     NumpadPlus: { code: 107, name: "Numpad +" },
     97     NumpadMinus: { code: 109, name: "Numpad -" },
     98     Numpad0: { code: 96, name: "Numpad 0" },
     99     Plus: { code: 187, name: "+" },
    100     Comma: { code: 188, name: "," },
    101     Minus: { code: 189, name: "-" },
    102     Period: { code: 190, name: "." },
    103     Slash: { code: 191, name: "/" },
    104     QuestionMark: { code: 191, name: "?" },
    105     Apostrophe: { code: 192, name: "`" },
    106     Tilde: { code: 192, name: "Tilde" },
    107     Backslash: { code: 220, name: "\\" },
    108     SingleQuote: { code: 222, name: "\'" },
    109     get CtrlOrMeta()
    110     {
    111         // "default" command/ctrl key for platform, Command on Mac, Ctrl on other platforms
    112         return WebInspector.isMac() ? this.Meta : this.Ctrl;
    113     },
    114 };
    115 
    116 WebInspector.KeyboardShortcut.KeyBindings = {};
    117 
    118 (function() {
    119     for (var key in WebInspector.KeyboardShortcut.Keys) {
    120         var descriptor = WebInspector.KeyboardShortcut.Keys[key];
    121         if (typeof descriptor === "object" && descriptor["code"]) {
    122             var name = typeof descriptor["name"] === "string" ? descriptor["name"] : key;
    123             WebInspector.KeyboardShortcut.KeyBindings[name] = descriptor;
    124         }
    125     }
    126 })();
    127 
    128 /**
    129  * Creates a number encoding keyCode in the lower 8 bits and modifiers mask in the higher 8 bits.
    130  * It is useful for matching pressed keys.
    131  *
    132  * @param {number|string} keyCode The code of the key, or a character "a-z" which is converted to a keyCode value.
    133  * @param {number=} modifiers Optional list of modifiers passed as additional parameters.
    134  * @return {number}
    135  */
    136 WebInspector.KeyboardShortcut.makeKey = function(keyCode, modifiers)
    137 {
    138     if (typeof keyCode === "string")
    139         keyCode = keyCode.charCodeAt(0) - (/^[a-z]/.test(keyCode) ? 32 : 0);
    140     modifiers = modifiers || WebInspector.KeyboardShortcut.Modifiers.None;
    141     return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, modifiers);
    142 }
    143 
    144 /**
    145  * @param {?KeyboardEvent} keyboardEvent
    146  * @return {number}
    147  */
    148 WebInspector.KeyboardShortcut.makeKeyFromEvent = function(keyboardEvent)
    149 {
    150     var modifiers = WebInspector.KeyboardShortcut.Modifiers.None;
    151     if (keyboardEvent.shiftKey)
    152         modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
    153     if (keyboardEvent.ctrlKey)
    154         modifiers |= WebInspector.KeyboardShortcut.Modifiers.Ctrl;
    155     if (keyboardEvent.altKey)
    156         modifiers |= WebInspector.KeyboardShortcut.Modifiers.Alt;
    157     if (keyboardEvent.metaKey)
    158         modifiers |= WebInspector.KeyboardShortcut.Modifiers.Meta;
    159 
    160     // Use either a real or a synthetic keyCode (for events originating from extensions).
    161     var keyCode = keyboardEvent.keyCode || keyboardEvent["__keyCode"];
    162     return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, modifiers);
    163 }
    164 
    165 /**
    166  * @param {?KeyboardEvent} keyboardEvent
    167  * @return {number}
    168  */
    169 WebInspector.KeyboardShortcut.makeKeyFromEventIgnoringModifiers = function(keyboardEvent)
    170 {
    171     var keyCode = keyboardEvent.keyCode || keyboardEvent["__keyCode"];
    172     return WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers(keyCode, WebInspector.KeyboardShortcut.Modifiers.None);
    173 }
    174 
    175 /**
    176  * @param {?KeyboardEvent} event
    177  * @return {boolean}
    178  */
    179 WebInspector.KeyboardShortcut.eventHasCtrlOrMeta = function(event)
    180 {
    181     return WebInspector.isMac() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
    182 }
    183 
    184 /**
    185  * @param {!Event} event
    186  * @return {boolean}
    187  */
    188 WebInspector.KeyboardShortcut.hasNoModifiers = function(event)
    189 {
    190     return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
    191 }
    192 
    193 /** @typedef {!{key: number, name: string}} */
    194 WebInspector.KeyboardShortcut.Descriptor;
    195 
    196 /**
    197  * @param {string|!WebInspector.KeyboardShortcut.Key} key
    198  * @param {number=} modifiers
    199  * @return {!WebInspector.KeyboardShortcut.Descriptor}
    200  */
    201 WebInspector.KeyboardShortcut.makeDescriptor = function(key, modifiers)
    202 {
    203     return {
    204         key: WebInspector.KeyboardShortcut.makeKey(typeof key === "string" ? key : key.code, modifiers),
    205         name: WebInspector.KeyboardShortcut.shortcutToString(key, modifiers)
    206     };
    207 }
    208 
    209 /**
    210  * @param {string} shortcut
    211  * @return {?WebInspector.KeyboardShortcut.Descriptor}
    212  */
    213 WebInspector.KeyboardShortcut.makeDescriptorFromBindingShortcut = function(shortcut)
    214 {
    215     var parts = shortcut.split(/\+(?!$)/);
    216     var modifiers = 0;
    217     var keyString;
    218     for (var i = 0; i < parts.length; ++i) {
    219         if (typeof WebInspector.KeyboardShortcut.Modifiers[parts[i]] !== "undefined") {
    220             modifiers |= WebInspector.KeyboardShortcut.Modifiers[parts[i]];
    221             continue;
    222         }
    223         console.assert(i === parts.length - 1, "Only one key other than modifier is allowed in shortcut <" + shortcut + ">");
    224         keyString = parts[i];
    225         break;
    226     }
    227     console.assert(keyString, "Modifiers-only shortcuts are not allowed (encountered <" + shortcut + ">)");
    228     if (!keyString)
    229         return null;
    230 
    231     var key = WebInspector.KeyboardShortcut.Keys[keyString] || WebInspector.KeyboardShortcut.KeyBindings[keyString];
    232     if (key && key.shiftKey)
    233         modifiers |= WebInspector.KeyboardShortcut.Modifiers.Shift;
    234     return WebInspector.KeyboardShortcut.makeDescriptor(key ? key : keyString, modifiers);
    235 }
    236 
    237 /**
    238  * @param {string|!WebInspector.KeyboardShortcut.Key} key
    239  * @param {number=} modifiers
    240  * @return {string}
    241  */
    242 WebInspector.KeyboardShortcut.shortcutToString = function(key, modifiers)
    243 {
    244     return WebInspector.KeyboardShortcut._modifiersToString(modifiers) + WebInspector.KeyboardShortcut._keyName(key);
    245 }
    246 
    247 /**
    248  * @param {string|!WebInspector.KeyboardShortcut.Key} key
    249  * @return {string}
    250  */
    251 WebInspector.KeyboardShortcut._keyName = function(key)
    252 {
    253     if (typeof key === "string")
    254         return key.toUpperCase();
    255     if (typeof key.name === "string")
    256         return key.name;
    257     return key.name[WebInspector.platform()] || key.name.other || '';
    258 }
    259 
    260 /**
    261  * @param {number} keyCode
    262  * @param {?number} modifiers
    263  * @return {number}
    264  */
    265 WebInspector.KeyboardShortcut._makeKeyFromCodeAndModifiers = function(keyCode, modifiers)
    266 {
    267     return (keyCode & 255) | (modifiers << 8);
    268 };
    269 
    270 /**
    271  * @param {number} key
    272  * @return {!{keyCode: number, modifiers: number}}
    273  */
    274 WebInspector.KeyboardShortcut.keyCodeAndModifiersFromKey = function(key)
    275 {
    276     return { keyCode: key & 255, modifiers: key >> 8 };
    277 }
    278 
    279 /**
    280  * @param {number|undefined} modifiers
    281  * @return {string}
    282  */
    283 WebInspector.KeyboardShortcut._modifiersToString = function(modifiers)
    284 {
    285     const cmdKey = "\u2318";
    286     const optKey = "\u2325";
    287     const shiftKey = "\u21e7";
    288     const ctrlKey = "\u2303";
    289 
    290     var isMac = WebInspector.isMac();
    291     var res = "";
    292     if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Ctrl)
    293         res += isMac ? ctrlKey : "Ctrl + ";
    294     if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Alt)
    295         res += isMac ? optKey : "Alt + ";
    296     if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Shift)
    297         res += isMac ? shiftKey : "Shift + ";
    298     if (modifiers & WebInspector.KeyboardShortcut.Modifiers.Meta)
    299         res += isMac ? cmdKey : "Win + ";
    300 
    301     return res;
    302 };
    303 
    304 WebInspector.KeyboardShortcut.SelectAll = WebInspector.KeyboardShortcut.makeKey("a", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
    305