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