Home | History | Annotate | Download | only in ui
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 /**
      6  * @constructor
      7  * @param {!WebInspector.ActionRegistry} actionRegistry
      8  */
      9 WebInspector.ShortcutRegistry = function(actionRegistry)
     10 {
     11     this._actionRegistry = actionRegistry;
     12     /** @type {!StringMultimap.<string>} */
     13     this._defaultKeyToActions = new StringMultimap();
     14     /** @type {!StringMultimap.<!WebInspector.KeyboardShortcut.Descriptor>} */
     15     this._defaultActionToShortcut = new StringMultimap();
     16     this._registerBindings();
     17 }
     18 
     19 WebInspector.ShortcutRegistry.prototype = {
     20     /**
     21      * @param {number} key
     22      * @return {!Array.<string>}
     23      */
     24     applicableActions: function(key)
     25     {
     26         return this._actionRegistry.applicableActions(this._actionIdsForKey(key), WebInspector.context);
     27     },
     28 
     29     /**
     30      * @param {number} key
     31      * @return {!Array.<string>}
     32      */
     33     _actionIdsForKey: function(key)
     34     {
     35         var result = new StringSet();
     36         var defaults = this._defaultActionsForKey(key);
     37         defaults.values().forEach(function(actionId) {
     38             result.add(actionId);
     39         }, this);
     40 
     41         return result.values();
     42     },
     43 
     44     /**
     45      * @param {number} key
     46      * @return {!Set.<string>}
     47      */
     48     _defaultActionsForKey: function(key)
     49     {
     50         return this._defaultKeyToActions.get(String(key));
     51     },
     52 
     53     /**
     54      * @param {string} actionId
     55      * @return {!Array.<!WebInspector.KeyboardShortcut.Descriptor>}
     56      */
     57     shortcutDescriptorsForAction: function(actionId)
     58     {
     59         return this._defaultActionToShortcut.get(actionId).values();
     60     },
     61 
     62     /**
     63      * @param {!Array.<string>} actionIds
     64      * @return {!Array.<number>}
     65      */
     66     keysForActions: function(actionIds)
     67     {
     68         var result = [];
     69         for (var i = 0; i < actionIds.length; ++i) {
     70             var descriptors = this.shortcutDescriptorsForAction(actionIds[i]);
     71             for (var j = 0; j < descriptors.length; ++j)
     72                 result.push(descriptors[j].key);
     73         }
     74         return result;
     75     },
     76 
     77     /**
     78      * @param {!KeyboardEvent} event
     79      */
     80     handleShortcut: function(event)
     81     {
     82         this.handleKey(WebInspector.KeyboardShortcut.makeKeyFromEvent(event), event.keyIdentifier, event);
     83     },
     84 
     85     /**
     86      * @param {number} key
     87      * @param {string} keyIdentifier
     88      * @param {!KeyboardEvent=} event
     89      */
     90     handleKey: function(key, keyIdentifier, event)
     91     {
     92         var keyModifiers = key >> 8;
     93         var actionIds = this.applicableActions(key);
     94         if (WebInspector.GlassPane.DefaultFocusedViewStack.length > 1) {
     95             if (actionIds.length && !isPossiblyInputKey())
     96                 event.consume(true);
     97             return;
     98         }
     99 
    100         for (var i = 0; i < actionIds.length; ++i) {
    101             if (!isPossiblyInputKey()) {
    102                 if (handler.call(this, actionIds[i]))
    103                     break;
    104             } else {
    105                 this._pendingActionTimer = setTimeout(handler.bind(this, actionIds[i]), 0);
    106                 break;
    107             }
    108         }
    109 
    110         /**
    111          * @return {boolean}
    112          */
    113         function isPossiblyInputKey()
    114         {
    115             if (!event || !WebInspector.isBeingEdited(/** @type {!Node} */ (event.target)) || /^F\d+|Control|Shift|Alt|Meta|Win|U\+001B$/.test(keyIdentifier))
    116                 return false;
    117 
    118             if (!keyModifiers)
    119                 return true;
    120 
    121             var modifiers = WebInspector.KeyboardShortcut.Modifiers;
    122             if ((keyModifiers & (modifiers.Ctrl | modifiers.Alt)) === (modifiers.Ctrl | modifiers.Alt))
    123                 return WebInspector.isWin();
    124 
    125             return !hasModifier(modifiers.Ctrl) && !hasModifier(modifiers.Alt) && !hasModifier(modifiers.Meta);
    126         }
    127 
    128         /**
    129          * @param {number} mod
    130          * @return {boolean}
    131          */
    132         function hasModifier(mod)
    133         {
    134             return !!(keyModifiers & mod);
    135         }
    136 
    137         /**
    138          * @param {string} actionId
    139          * @return {boolean}
    140          * @this {WebInspector.ShortcutRegistry}
    141          */
    142         function handler(actionId)
    143         {
    144             var result = this._actionRegistry.execute(actionId);
    145             if (result && event)
    146                 event.consume(true);
    147             delete this._pendingActionTimer;
    148             return result;
    149         }
    150     },
    151 
    152     /**
    153      * @param {string} actionId
    154      * @param {string} shortcut
    155      */
    156     registerShortcut: function(actionId, shortcut)
    157     {
    158         var descriptor = WebInspector.KeyboardShortcut.makeDescriptorFromBindingShortcut(shortcut);
    159         if (!descriptor)
    160             return;
    161         this._defaultActionToShortcut.set(actionId, descriptor);
    162         this._defaultKeyToActions.set(String(descriptor.key), actionId);
    163     },
    164 
    165     dismissPendingShortcutAction: function()
    166     {
    167         if (this._pendingActionTimer) {
    168             clearTimeout(this._pendingActionTimer);
    169             delete this._pendingActionTimer;
    170         }
    171     },
    172 
    173     _registerBindings: function()
    174     {
    175         document.addEventListener("input", this.dismissPendingShortcutAction.bind(this), true);
    176         var extensions = self.runtime.extensions(WebInspector.ActionDelegate);
    177         extensions.forEach(registerExtension, this);
    178 
    179         /**
    180          * @param {!Runtime.Extension} extension
    181          * @this {WebInspector.ShortcutRegistry}
    182          */
    183         function registerExtension(extension)
    184         {
    185             var descriptor = extension.descriptor();
    186             var bindings = descriptor["bindings"];
    187             for (var i = 0; bindings && i < bindings.length; ++i) {
    188                 if (!platformMatches(bindings[i].platform))
    189                     continue;
    190                 var shortcuts = bindings[i]["shortcut"].split(/\s+/);
    191                 shortcuts.forEach(this.registerShortcut.bind(this, descriptor["actionId"]));
    192             }
    193         }
    194 
    195         /**
    196          * @param {string=} platformsString
    197          * @return {boolean}
    198          */
    199         function platformMatches(platformsString)
    200         {
    201             if (!platformsString)
    202                 return true;
    203             var platforms = platformsString.split(",");
    204             var isMatch = false;
    205             var currentPlatform = WebInspector.platform();
    206             for (var i = 0; !isMatch && i < platforms.length; ++i)
    207                 isMatch = platforms[i] === currentPlatform;
    208             return isMatch;
    209         }
    210     }
    211 }
    212 
    213 /**
    214  * @constructor
    215  */
    216 WebInspector.ShortcutRegistry.ForwardedShortcut = function()
    217 {
    218 }
    219 
    220 WebInspector.ShortcutRegistry.ForwardedShortcut.instance = new WebInspector.ShortcutRegistry.ForwardedShortcut();
    221 
    222 /** @type {!WebInspector.ShortcutRegistry} */
    223 WebInspector.shortcutRegistry;
    224