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 WebInspector.ContextMenu.prototype = { 191 nextId: function() 192 { 193 return this._id++; 194 }, 195 196 show: function() 197 { 198 var menuObject = this._buildDescriptor(); 199 200 if (menuObject.length) { 201 WebInspector._contextMenu = this; 202 InspectorFrontendHost.showContextMenu(this._event, menuObject); 203 this._event.consume(); 204 } 205 }, 206 207 showSoftMenu: function() 208 { 209 var menuObject = this._buildDescriptor(); 210 211 if (menuObject.length) { 212 WebInspector._contextMenu = this; 213 var softMenu = new WebInspector.SoftContextMenu(menuObject); 214 softMenu.show(this._event, true); 215 } 216 this._event.consume(); 217 }, 218 219 _setHandler: function(id, handler) 220 { 221 if (handler) 222 this._handlers[id] = handler; 223 }, 224 225 _buildDescriptor: function() 226 { 227 var result = []; 228 for (var i = 0; i < this._items.length; ++i) 229 result.push(this._items[i]._buildDescriptor()); 230 return result; 231 }, 232 233 _itemSelected: function(id) 234 { 235 if (this._handlers[id]) 236 this._handlers[id].call(this); 237 }, 238 239 /** 240 * @param {Object} target 241 */ 242 appendApplicableItems: function(target) 243 { 244 for (var i = 0; i < WebInspector.ContextMenu._providers.length; ++i) { 245 var provider = WebInspector.ContextMenu._providers[i]; 246 this.appendSeparator(); 247 provider.appendApplicableItems(this._event, this, target); 248 this.appendSeparator(); 249 } 250 }, 251 252 __proto__: WebInspector.ContextSubMenuItem.prototype 253 } 254 255 /** 256 * @interface 257 */ 258 WebInspector.ContextMenu.Provider = function() { 259 } 260 261 WebInspector.ContextMenu.Provider.prototype = { 262 /** 263 * @param {WebInspector.ContextMenu} contextMenu 264 * @param {Object} target 265 */ 266 appendApplicableItems: function(event, contextMenu, target) { } 267 } 268 269 /** 270 * @param {WebInspector.ContextMenu.Provider} provider 271 */ 272 WebInspector.ContextMenu.registerProvider = function(provider) 273 { 274 WebInspector.ContextMenu._providers.push(provider); 275 } 276 277 WebInspector.ContextMenu._providers = []; 278 279 WebInspector.contextMenuItemSelected = function(id) 280 { 281 if (WebInspector._contextMenu) 282 WebInspector._contextMenu._itemSelected(id); 283 } 284 285 WebInspector.contextMenuCleared = function() 286 { 287 // FIXME: Unfortunately, contextMenuCleared is invoked between show and item selected 288 // so we can't delete last menu object from WebInspector. Fix the contract. 289 } 290