Home | History | Annotate | Download | only in front_end
      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.ContextSubMenuItem} 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     id: function()
     52     {
     53         return this._id;
     54     },
     55 
     56     type: function()
     57     {
     58         return this._type;
     59     },
     60 
     61     /**
     62      * @return {boolean}
     63      */
     64     isEnabled: function()
     65     {
     66         return !this._disabled;
     67     },
     68 
     69     /**
     70      * @param {boolean} enabled
     71      */
     72     setEnabled: function(enabled)
     73     {
     74         this._disabled = !enabled;
     75     },
     76 
     77     _buildDescriptor: function()
     78     {
     79         switch (this._type) {
     80         case "item":
     81             return { type: "item", id: this._id, label: this._label, enabled: !this._disabled };
     82         case "separator":
     83             return { type: "separator" };
     84         case "checkbox":
     85             return { type: "checkbox", id: this._id, label: this._label, checked: !!this._checked, enabled: !this._disabled };
     86         }
     87     }
     88 }
     89 
     90 /**
     91  * @constructor
     92  * @extends {WebInspector.ContextMenuItem}
     93  * @param topLevelMenu
     94  * @param {string=} label
     95  * @param {boolean=} disabled
     96  */
     97 WebInspector.ContextSubMenuItem = function(topLevelMenu, label, disabled)
     98 {
     99     WebInspector.ContextMenuItem.call(this, topLevelMenu, "subMenu", label, disabled);
    100     /** @type {!Array.<!WebInspector.ContextMenuItem>} */
    101     this._items = [];
    102 }
    103 
    104 WebInspector.ContextSubMenuItem.prototype = {
    105     /**
    106      * @param {string} label
    107      * @param {function(?)} handler
    108      * @param {boolean=} disabled
    109      * @return {!WebInspector.ContextMenuItem}
    110      */
    111     appendItem: function(label, handler, disabled)
    112     {
    113         var item = new WebInspector.ContextMenuItem(this._contextMenu, "item", label, disabled);
    114         this._pushItem(item);
    115         this._contextMenu._setHandler(item.id(), handler);
    116         return item;
    117     },
    118 
    119     /**
    120      * @param {string} label
    121      * @param {boolean=} disabled
    122      * @return {!WebInspector.ContextMenuItem}
    123      */
    124     appendSubMenuItem: function(label, disabled)
    125     {
    126         var item = new WebInspector.ContextSubMenuItem(this._contextMenu, label, disabled);
    127         this._pushItem(item);
    128         return item;
    129     },
    130 
    131     /**
    132      * @param {boolean=} disabled
    133      */
    134     appendCheckboxItem: function(label, handler, checked, disabled)
    135     {
    136         var item = new WebInspector.ContextMenuItem(this._contextMenu, "checkbox", label, disabled, checked);
    137         this._pushItem(item);
    138         this._contextMenu._setHandler(item.id(), handler);
    139         return item;
    140     },
    141 
    142     appendSeparator: function()
    143     {
    144         if (this._items.length)
    145             this._pendingSeparator = true;
    146     },
    147 
    148     /**
    149      * @param {!WebInspector.ContextMenuItem} item
    150      */
    151     _pushItem: function(item)
    152     {
    153         if (this._pendingSeparator) {
    154             this._items.push(new WebInspector.ContextMenuItem(this._contextMenu, "separator"));
    155             delete this._pendingSeparator;
    156         }
    157         this._items.push(item);
    158     },
    159 
    160     /**
    161      * @return {boolean}
    162      */
    163     isEmpty: function()
    164     {
    165         return !this._items.length;
    166     },
    167 
    168     _buildDescriptor: function()
    169     {
    170         var result = { type: "subMenu", label: this._label, enabled: !this._disabled, subItems: [] };
    171         for (var i = 0; i < this._items.length; ++i)
    172             result.subItems.push(this._items[i]._buildDescriptor());
    173         return result;
    174     },
    175 
    176     __proto__: WebInspector.ContextMenuItem.prototype
    177 }
    178 
    179 /**
    180  * @constructor
    181  * @extends {WebInspector.ContextSubMenuItem}
    182  */
    183 WebInspector.ContextMenu = function(event) {
    184     WebInspector.ContextSubMenuItem.call(this, this, "");
    185     this._event = event;
    186     this._handlers = {};
    187     this._id = 0;
    188 }
    189 
    190 /**
    191  * @param {boolean} useSoftMenu
    192  */
    193 WebInspector.ContextMenu.setUseSoftMenu = function(useSoftMenu)
    194 {
    195     WebInspector.ContextMenu._useSoftMenu = useSoftMenu;
    196 }
    197 
    198 WebInspector.ContextMenu.prototype = {
    199     nextId: function()
    200     {
    201         return this._id++;
    202     },
    203 
    204     show: function()
    205     {
    206         var menuObject = this._buildDescriptor();
    207 
    208         if (menuObject.length) {
    209             WebInspector._contextMenu = this;
    210             if (WebInspector.ContextMenu._useSoftMenu) {
    211                 var softMenu = new WebInspector.SoftContextMenu(menuObject);
    212                 softMenu.show(this._event);
    213             } else {
    214                 InspectorFrontendHost.showContextMenu(this._event, menuObject);
    215             }
    216             this._event.consume();
    217         }
    218     },
    219 
    220     _setHandler: function(id, handler)
    221     {
    222         if (handler)
    223             this._handlers[id] = handler;
    224     },
    225 
    226     _buildDescriptor: function()
    227     {
    228         var result = [];
    229         for (var i = 0; i < this._items.length; ++i)
    230             result.push(this._items[i]._buildDescriptor());
    231         return result;
    232     },
    233 
    234     _itemSelected: function(id)
    235     {
    236         if (this._handlers[id])
    237             this._handlers[id].call(this);
    238     },
    239 
    240     /**
    241      * @param {!Object} target
    242      */
    243     appendApplicableItems: function(target)
    244     {
    245         for (var i = 0; i < WebInspector.ContextMenu._providers.length; ++i) {
    246             var provider = WebInspector.ContextMenu._providers[i];
    247             this.appendSeparator();
    248             provider.appendApplicableItems(this._event, this, target);
    249             this.appendSeparator();
    250         }
    251     },
    252 
    253     __proto__: WebInspector.ContextSubMenuItem.prototype
    254 }
    255 
    256 /**
    257  * @interface
    258  */
    259 WebInspector.ContextMenu.Provider = function() {
    260 }
    261 
    262 WebInspector.ContextMenu.Provider.prototype = {
    263     /**
    264      * @param {!WebInspector.ContextMenu} contextMenu
    265      * @param {!Object} target
    266      */
    267     appendApplicableItems: function(event, contextMenu, target) { }
    268 }
    269 
    270 /**
    271  * @param {!WebInspector.ContextMenu.Provider} provider
    272  */
    273 WebInspector.ContextMenu.registerProvider = function(provider)
    274 {
    275     WebInspector.ContextMenu._providers.push(provider);
    276 }
    277 
    278 WebInspector.ContextMenu._providers = [];
    279 
    280 WebInspector.contextMenuItemSelected = function(id)
    281 {
    282     if (WebInspector._contextMenu)
    283         WebInspector._contextMenu._itemSelected(id);
    284 }
    285 
    286 WebInspector.contextMenuCleared = function()
    287 {
    288     // FIXME: Unfortunately, contextMenuCleared is invoked between show and item selected
    289     // so we can't delete last menu object from WebInspector. Fix the contract.
    290 }
    291