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