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