Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  * @param {!WebInspector.ContextMenu} topLevelMenu
     34  * @param {string} type
     35  * @param {string=} label
     36  * @param {boolean=} disabled
     37  * @param {boolean=} checked
     38  */
     39 WebInspector.ContextMenuItem = function(topLevelMenu, type, label, disabled, checked)
     40 {
     41     this._type = type;
     42     this._label = label;
     43     this._disabled = disabled;
     44     this._checked = checked;
     45     this._contextMenu = topLevelMenu;
     46     if (type === "item" || type === "checkbox")
     47         this._id = topLevelMenu.nextId();
     48 }
     49 
     50 WebInspector.ContextMenuItem.prototype = {
     51     /**
     52      * @return {number}
     53      */
     54     id: function()
     55     {
     56         return this._id;
     57     },
     58 
     59     /**
     60      * @return {string}
     61      */
     62     type: function()
     63     {
     64         return this._type;
     65     },
     66 
     67     /**
     68      * @return {boolean}
     69      */
     70     isEnabled: function()
     71     {
     72         return !this._disabled;
     73     },
     74 
     75     /**
     76      * @param {boolean} enabled
     77      */
     78     setEnabled: function(enabled)
     79     {
     80         this._disabled = !enabled;
     81     },
     82 
     83     /**
     84      * @return {!InspectorFrontendHostAPI.ContextMenuDescriptor}
     85      */
     86     _buildDescriptor: function()
     87     {
     88         switch (this._type) {
     89         case "item":
     90             return { type: "item", id: this._id, label: this._label, enabled: !this._disabled };
     91         case "separator":
     92             return { type: "separator" };
     93         case "checkbox":
     94             return { type: "checkbox", id: this._id, label: this._label, checked: !!this._checked, enabled: !this._disabled };
     95         }
     96         throw new Error("Invalid item type:"  + this._type);
     97     }
     98 }
     99 
    100 /**
    101  * @constructor
    102  * @extends {WebInspector.ContextMenuItem}
    103  * @param {!WebInspector.ContextMenu} topLevelMenu
    104  * @param {string=} label
    105  * @param {boolean=} disabled
    106  */
    107 WebInspector.ContextSubMenuItem = function(topLevelMenu, label, disabled)
    108 {
    109     WebInspector.ContextMenuItem.call(this, topLevelMenu, "subMenu", label, disabled);
    110     /** @type {!Array.<!WebInspector.ContextMenuItem>} */
    111     this._items = [];
    112 }
    113 
    114 WebInspector.ContextSubMenuItem.prototype = {
    115     /**
    116      * @param {string} label
    117      * @param {function(?)} handler
    118      * @param {boolean=} disabled
    119      * @return {!WebInspector.ContextMenuItem}
    120      */
    121     appendItem: function(label, handler, disabled)
    122     {
    123         var item = new WebInspector.ContextMenuItem(this._contextMenu, "item", label, disabled);
    124         this._pushItem(item);
    125         this._contextMenu._setHandler(item.id(), handler);
    126         return item;
    127     },
    128 
    129     /**
    130      * @param {string} label
    131      * @param {boolean=} disabled
    132      * @return {!WebInspector.ContextSubMenuItem}
    133      */
    134     appendSubMenuItem: function(label, disabled)
    135     {
    136         var item = new WebInspector.ContextSubMenuItem(this._contextMenu, label, disabled);
    137         this._pushItem(item);
    138         return item;
    139     },
    140 
    141     /**
    142      * @param {string} label
    143      * @param {function()} handler
    144      * @param {boolean=} checked
    145      * @param {boolean=} disabled
    146      * @return {!WebInspector.ContextMenuItem}
    147      */
    148     appendCheckboxItem: function(label, handler, checked, disabled)
    149     {
    150         var item = new WebInspector.ContextMenuItem(this._contextMenu, "checkbox", label, disabled, checked);
    151         this._pushItem(item);
    152         this._contextMenu._setHandler(item.id(), handler);
    153         return item;
    154     },
    155 
    156     appendSeparator: function()
    157     {
    158         if (this._items.length)
    159             this._pendingSeparator = true;
    160     },
    161 
    162     /**
    163      * @param {!WebInspector.ContextMenuItem} item
    164      */
    165     _pushItem: function(item)
    166     {
    167         if (this._pendingSeparator) {
    168             this._items.push(new WebInspector.ContextMenuItem(this._contextMenu, "separator"));
    169             delete this._pendingSeparator;
    170         }
    171         this._items.push(item);
    172     },
    173 
    174     /**
    175      * @return {boolean}
    176      */
    177     isEmpty: function()
    178     {
    179         return !this._items.length;
    180     },
    181 
    182     /**
    183      * @return {!InspectorFrontendHostAPI.ContextMenuDescriptor}
    184      */
    185     _buildDescriptor: function()
    186     {
    187         var result = { type: "subMenu", label: this._label, enabled: !this._disabled, subItems: [] };
    188         for (var i = 0; i < this._items.length; ++i)
    189             result.subItems.push(this._items[i]._buildDescriptor());
    190         return result;
    191     },
    192 
    193     __proto__: WebInspector.ContextMenuItem.prototype
    194 }
    195 
    196 /**
    197  * @constructor
    198  * @extends {WebInspector.ContextSubMenuItem}
    199  * @param {!Event} event
    200  */
    201 WebInspector.ContextMenu = function(event)
    202 {
    203     WebInspector.ContextSubMenuItem.call(this, this, "");
    204     this._event = event;
    205     this._handlers = {};
    206     this._id = 0;
    207 }
    208 
    209 WebInspector.ContextMenu.initialize = function()
    210 {
    211     InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.SetUseSoftMenu, setUseSoftMenu);
    212     /**
    213      * @param {!WebInspector.Event} event
    214      */
    215     function setUseSoftMenu(event)
    216     {
    217         WebInspector.ContextMenu._useSoftMenu = /** @type {boolean} */ (event.data);
    218     }
    219 }
    220 
    221 WebInspector.ContextMenu.prototype = {
    222     /**
    223      * @return {number}
    224      */
    225     nextId: function()
    226     {
    227         return this._id++;
    228     },
    229 
    230     show: function()
    231     {
    232         var menuObject = this._buildDescriptor();
    233 
    234         if (menuObject.length) {
    235             WebInspector._contextMenu = this;
    236             if (WebInspector.ContextMenu._useSoftMenu || InspectorFrontendHost.isHostedMode()) {
    237                 var softMenu = new WebInspector.SoftContextMenu(menuObject, this._itemSelected.bind(this));
    238                 softMenu.show(this._event.x, this._event.y);
    239             } else {
    240                 InspectorFrontendHost.showContextMenuAtPoint(this._event.x, this._event.y, menuObject);
    241                 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.ContextMenuCleared, this._menuCleared, this);
    242                 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.ContextMenuItemSelected, this._onItemSelected, this);
    243             }
    244             this._event.consume(true);
    245         }
    246     },
    247 
    248     /**
    249      * @param {number} id
    250      * @param {function(?)} handler
    251      */
    252     _setHandler: function(id, handler)
    253     {
    254         if (handler)
    255             this._handlers[id] = handler;
    256     },
    257 
    258     /**
    259      * @return {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>}
    260      */
    261     _buildDescriptor: function()
    262     {
    263         var result = [];
    264         for (var i = 0; i < this._items.length; ++i)
    265             result.push(this._items[i]._buildDescriptor());
    266         return result;
    267     },
    268 
    269     /**
    270      * @param {!WebInspector.Event} event
    271      */
    272     _onItemSelected: function(event)
    273     {
    274         this._itemSelected(/** @type {string} */ (event.data));
    275     },
    276 
    277     /**
    278      * @param {string} id
    279      */
    280     _itemSelected: function(id)
    281     {
    282         if (this._handlers[id])
    283             this._handlers[id].call(this);
    284         this._menuCleared();
    285     },
    286 
    287     _menuCleared: function()
    288     {
    289         InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.ContextMenuCleared, this._menuCleared, this);
    290         InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.ContextMenuItemSelected, this._onItemSelected, this);
    291     },
    292 
    293     /**
    294      * @param {!Object} target
    295      */
    296     appendApplicableItems: function(target)
    297     {
    298         self.runtime.extensions(WebInspector.ContextMenu.Provider, target).forEach(processProviders.bind(this));
    299 
    300         /**
    301          * @param {!Runtime.Extension} extension
    302          * @this {WebInspector.ContextMenu}
    303          */
    304         function processProviders(extension)
    305         {
    306             var provider = /** @type {!WebInspector.ContextMenu.Provider} */ (extension.instance());
    307             this.appendSeparator();
    308             provider.appendApplicableItems(this._event, this, target);
    309             this.appendSeparator();
    310         }
    311     },
    312 
    313     __proto__: WebInspector.ContextSubMenuItem.prototype
    314 }
    315 
    316 /**
    317  * @interface
    318  */
    319 WebInspector.ContextMenu.Provider = function() {
    320 }
    321 
    322 WebInspector.ContextMenu.Provider.prototype = {
    323     /**
    324      * @param {!Event} event
    325      * @param {!WebInspector.ContextMenu} contextMenu
    326      * @param {!Object} target
    327      */
    328     appendApplicableItems: function(event, contextMenu, target) { }
    329 }
    330