001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.menus;
011:
012: import java.util.HashMap;
013: import java.util.HashSet;
014: import java.util.Map;
015: import java.util.Set;
016:
017: import org.eclipse.core.expressions.Expression;
018: import org.eclipse.core.expressions.ExpressionConverter;
019: import org.eclipse.core.runtime.CoreException;
020: import org.eclipse.core.runtime.IConfigurationElement;
021: import org.eclipse.core.runtime.InvalidRegistryObjectException;
022: import org.eclipse.jface.action.GroupMarker;
023: import org.eclipse.jface.action.IContributionItem;
024: import org.eclipse.jface.action.MenuManager;
025: import org.eclipse.jface.action.Separator;
026: import org.eclipse.jface.action.ToolBarContributionItem;
027: import org.eclipse.jface.action.ToolBarManager;
028: import org.eclipse.jface.resource.ImageDescriptor;
029: import org.eclipse.ui.IWorkbenchWindow;
030: import org.eclipse.ui.actions.CompoundContributionItem;
031: import org.eclipse.ui.internal.WorkbenchWindow;
032: import org.eclipse.ui.internal.provisional.presentations.IActionBarPresentationFactory;
033: import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
034: import org.eclipse.ui.internal.util.Util;
035: import org.eclipse.ui.menus.AbstractContributionFactory;
036: import org.eclipse.ui.menus.CommandContributionItem;
037: import org.eclipse.ui.menus.IContributionRoot;
038: import org.eclipse.ui.menus.IMenuService;
039: import org.eclipse.ui.menus.WorkbenchWindowControlContribution;
040: import org.eclipse.ui.plugin.AbstractUIPlugin;
041: import org.eclipse.ui.services.IServiceLocator;
042:
043: /**
044: * @since 3.3
045: *
046: */
047: public class MenuAdditionCacheEntry extends AbstractContributionFactory {
048: private IConfigurationElement additionElement;
049:
050: // Caches
051:
052: /**
053: * Maps an IContributionItem to its corresponding IConfigurationElement
054: */
055: Map iciToConfigElementMap = new HashMap();
056:
057: /**
058: * If an {@link IConfigurationElement} is in the Set then we have already
059: * tried (and failed) to load the associated ExecutableExtension.
060: *
061: * This is used to prevent multiple retries which would spam the Log.
062: */
063: Set failedLoads = new HashSet();
064:
065: /**
066: * Maps an IConfigurationElement to its parsed Expression
067: */
068: private HashMap visWhenMap = new HashMap();
069:
070: /**
071: * The menu service on which to generate all subcaches.
072: */
073: private IMenuService menuService;
074:
075: public MenuAdditionCacheEntry(IMenuService menuService,
076: IConfigurationElement element, String location,
077: String namespace) {
078: super (location, namespace);
079: this .menuService = menuService;
080: this .additionElement = element;
081: generateSubCaches();
082: }
083:
084: /**
085: *
086: */
087: private void generateSubCaches() {
088: IConfigurationElement[] items = additionElement.getChildren();
089: for (int i = 0; i < items.length; i++) {
090: String itemType = items[i].getName();
091: if (IWorkbenchRegistryConstants.TAG_MENU.equals(itemType)
092: || IWorkbenchRegistryConstants.TAG_TOOLBAR
093: .equals(itemType)) {
094: // Menus and toolbars are special...we have to add any sub menu
095: // items into their own cache
096: // If the locationURI is null then this should be a sub menu
097: // addition..create the 'root' URI
098:
099: String location = new MenuLocationURI(getLocation())
100: .getScheme()
101: + ":" + MenuAdditionCacheEntry.getId(items[i]); //$NON-NLS-1$
102:
103: // -ALL- contibuted menus must have an id so create one
104: // if necessary
105: MenuAdditionCacheEntry subMenuEntry = new MenuAdditionCacheEntry(
106: menuService, items[i], location, getNamespace());
107: menuService.addContributionFactory(subMenuEntry);
108: }
109: }
110: }
111:
112: private Expression getVisibleWhenForItem(IContributionItem item) {
113: IConfigurationElement configElement = (IConfigurationElement) iciToConfigElementMap
114: .get(item);
115: if (configElement == null)
116: return null;
117:
118: if (!visWhenMap.containsKey(configElement)) {
119: // Not parsed yet
120: try {
121: IConfigurationElement[] visibleConfig = configElement
122: .getChildren(IWorkbenchRegistryConstants.TAG_VISIBLE_WHEN);
123: if (visibleConfig.length > 0
124: && visibleConfig.length < 2) {
125: IConfigurationElement[] visibleChild = visibleConfig[0]
126: .getChildren();
127: if (visibleChild.length > 0) {
128: Expression visWhen = ExpressionConverter
129: .getDefault().perform(visibleChild[0]);
130: visWhenMap.put(configElement, visWhen);
131: }
132: }
133: } catch (InvalidRegistryObjectException e) {
134: visWhenMap.put(configElement, null);
135: // TODO Auto-generated catch block
136: e.printStackTrace();
137: } catch (CoreException e) {
138: visWhenMap.put(configElement, null);
139: // TODO Auto-generated catch block
140: e.printStackTrace();
141: }
142: }
143:
144: return (Expression) visWhenMap.get(configElement);
145: }
146:
147: /*
148: * (non-Javadoc)
149: *
150: * @see org.eclipse.ui.internal.menus.AbstractContributionFactory#createContributionItems(org.eclipse.ui.internal.menus.IMenuService,
151: * java.util.List)
152: */
153: public void createContributionItems(IServiceLocator serviceLocator,
154: IContributionRoot additions) {
155: IActionBarPresentationFactory actionBarPresentationFactory = null;
156:
157: WorkbenchWindow window = (WorkbenchWindow) serviceLocator
158: .getService(IWorkbenchWindow.class);
159: if (window != null) {
160: actionBarPresentationFactory = window
161: .getActionBarPresentationFactory();
162: }
163:
164: IConfigurationElement[] items = additionElement.getChildren();
165: for (int i = 0; i < items.length; i++) {
166: String itemType = items[i].getName();
167: IContributionItem newItem = null;
168:
169: if (IWorkbenchRegistryConstants.TAG_COMMAND
170: .equals(itemType)) {
171: newItem = createCommandAdditionContribution(
172: serviceLocator, items[i]);
173: } else if (IWorkbenchRegistryConstants.TAG_DYNAMIC
174: .equals(itemType)) {
175: newItem = createDynamicAdditionContribution(items[i]);
176: } else if (IWorkbenchRegistryConstants.TAG_CONTROL
177: .equals(itemType)) {
178: newItem = createControlAdditionContribution(items[i]);
179: } else if (IWorkbenchRegistryConstants.TAG_SEPARATOR
180: .equals(itemType)) {
181: newItem = createSeparatorAdditionContribution(items[i]);
182: } else if (IWorkbenchRegistryConstants.TAG_MENU
183: .equals(itemType)) {
184: newItem = createMenuAdditionContribution(items[i]);
185: } else if (IWorkbenchRegistryConstants.TAG_TOOLBAR
186: .equals(itemType)) {
187: newItem = createToolBarAdditionContribution(
188: actionBarPresentationFactory, items[i]);
189: }
190:
191: // Cache the relationship between the ICI and the
192: // registry element used to back it
193: if (newItem != null) {
194: iciToConfigElementMap.put(newItem, items[i]);
195: additions.addContributionItem(newItem,
196: getVisibleWhenForItem(newItem));
197: }
198: }
199: }
200:
201: /**
202: * @param configurationElement
203: * @return
204: */
205: private IContributionItem createToolBarAdditionContribution(
206: IActionBarPresentationFactory actionBarPresentationFactory,
207: IConfigurationElement configurationElement) {
208: if (!getLocation().startsWith("toolbar")) { //$NON-NLS-1$
209: return null;
210: }
211: if (actionBarPresentationFactory != null) {
212: return actionBarPresentationFactory
213: .createToolBarContributionItem(
214: actionBarPresentationFactory
215: .createToolBarManager(),
216: getId(configurationElement));
217: }
218: return new ToolBarContributionItem(new ToolBarManager(),
219: getId(configurationElement));
220: }
221:
222: /**
223: * @param configurationElement
224: * @return the menu manager
225: */
226: private IContributionItem createMenuAdditionContribution(
227: final IConfigurationElement menuAddition) {
228: // Is this for a menu or a ToolBar ? We can't create
229: // a menu directly under a Toolbar; we have to add an
230: // item of style 'pulldown'
231: if (getLocation().startsWith("toolbar")) { //$NON-NLS-1$
232: return null;
233: }
234:
235: String text = getLabel(menuAddition);
236: String mnemonic = getMnemonic(menuAddition);
237: if (text != null && mnemonic != null) {
238: int idx = text.indexOf(mnemonic);
239: if (idx != -1) {
240: text = text.substring(0, idx) + '&'
241: + text.substring(idx);
242: }
243: }
244: return new MenuManager(text, getId(menuAddition));
245: }
246:
247: /**
248: * @param configurationElement
249: * @return
250: */
251: private IContributionItem createSeparatorAdditionContribution(
252: final IConfigurationElement sepAddition) {
253: if (isSeparatorVisible(sepAddition)) {
254: return new Separator(getName(sepAddition));
255: }
256: return new GroupMarker(getName(sepAddition));
257: }
258:
259: /**
260: * @return
261: */
262: private IContributionItem createDynamicAdditionContribution(
263: final IConfigurationElement dynamicAddition) {
264: // If we've already tried (and failed) to load the
265: // executable extension then skip this addition.
266: if (failedLoads.contains(dynamicAddition))
267: return null;
268:
269: // Attempt to load the addition's EE (creates a new instance)
270: final CompoundContributionItem loadedDynamicContribution = (CompoundContributionItem) Util
271: .safeLoadExecutableExtension(dynamicAddition,
272: IWorkbenchRegistryConstants.ATT_CLASS,
273: CompoundContributionItem.class);
274:
275: // Cache failures
276: if (loadedDynamicContribution == null) {
277: failedLoads.add(loadedDynamicContribution);
278: return null;
279: }
280:
281: // TODO provide a proxy IContributionItem that defers instantiation
282: // adding contribution items in a menu instantiates this object ...
283: // we need to defer loading until fill(*) is called.
284: return loadedDynamicContribution;
285: }
286:
287: /**
288: * @return
289: */
290: private IContributionItem createControlAdditionContribution(
291: final IConfigurationElement widgetAddition) {
292: if (!getLocation().startsWith("toolbar")) { //$NON-NLS-1$
293: return null;
294: }
295: // If we've already tried (and failed) to load the
296: // executable extension then skip this addirion.
297: if (failedLoads.contains(widgetAddition))
298: return null;
299:
300: // Attempt to load the addition's EE (creates a new instance)
301: final WorkbenchWindowControlContribution loadedWidget = (WorkbenchWindowControlContribution) Util
302: .safeLoadExecutableExtension(widgetAddition,
303: IWorkbenchRegistryConstants.ATT_CLASS,
304: WorkbenchWindowControlContribution.class);
305:
306: // Cache failures
307: if (loadedWidget == null) {
308: failedLoads.add(widgetAddition);
309: return null;
310: }
311:
312: // explicitly set the id
313: ((InternalControlContribution) loadedWidget)
314: .setId(getId(widgetAddition));
315:
316: return loadedWidget;
317: }
318:
319: /**
320: * @param configurationElement
321: * @return
322: */
323: private IContributionItem createCommandAdditionContribution(
324: IServiceLocator locator,
325: final IConfigurationElement commandAddition) {
326: return new CommandContributionItem(locator,
327: getId(commandAddition), getCommandId(commandAddition),
328: getParameters(commandAddition),
329: getIconDescriptor(commandAddition),
330: getDisabledIconDescriptor(commandAddition),
331: getHoverIconDescriptor(commandAddition),
332: getLabel(commandAddition),
333: getMnemonic(commandAddition),
334: getTooltip(commandAddition), getStyle(commandAddition));
335: }
336:
337: /*
338: * Support Utilities
339: */
340: public static String getId(IConfigurationElement element) {
341: String id = element
342: .getAttribute(IWorkbenchRegistryConstants.ATT_ID);
343:
344: // For sub-menu management -all- items must be id'd so enforce this
345: // here (we could optimize by checking the 'name' of the config
346: // element == "menu"
347: if (id == null || id.length() == 0)
348: id = element.toString();
349:
350: return id;
351: }
352:
353: static String getName(IConfigurationElement element) {
354: return element
355: .getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
356: }
357:
358: static String getLabel(IConfigurationElement element) {
359: return element
360: .getAttribute(IWorkbenchRegistryConstants.ATT_LABEL);
361: }
362:
363: static String getMnemonic(IConfigurationElement element) {
364: return element
365: .getAttribute(IWorkbenchRegistryConstants.ATT_MNEMONIC);
366: }
367:
368: static String getTooltip(IConfigurationElement element) {
369: return element
370: .getAttribute(IWorkbenchRegistryConstants.ATT_TOOLTIP);
371: }
372:
373: static String getIconPath(IConfigurationElement element) {
374: return element
375: .getAttribute(IWorkbenchRegistryConstants.ATT_ICON);
376: }
377:
378: static String getDisabledIconPath(IConfigurationElement element) {
379: return element
380: .getAttribute(IWorkbenchRegistryConstants.ATT_DISABLEDICON);
381: }
382:
383: static String getHoverIconPath(IConfigurationElement element) {
384: return element
385: .getAttribute(IWorkbenchRegistryConstants.ATT_HOVERICON);
386: }
387:
388: static ImageDescriptor getIconDescriptor(
389: IConfigurationElement element) {
390: String extendingPluginId = element.getDeclaringExtension()
391: .getContributor().getName();
392:
393: String iconPath = getIconPath(element);
394: if (iconPath != null) {
395: return AbstractUIPlugin.imageDescriptorFromPlugin(
396: extendingPluginId, iconPath);
397: }
398: return null;
399: }
400:
401: static ImageDescriptor getDisabledIconDescriptor(
402: IConfigurationElement element) {
403: String extendingPluginId = element.getDeclaringExtension()
404: .getContributor().getName();
405:
406: String iconPath = getDisabledIconPath(element);
407: if (iconPath != null) {
408: return AbstractUIPlugin.imageDescriptorFromPlugin(
409: extendingPluginId, iconPath);
410: }
411: return null;
412: }
413:
414: static ImageDescriptor getHoverIconDescriptor(
415: IConfigurationElement element) {
416: String extendingPluginId = element.getDeclaringExtension()
417: .getContributor().getName();
418:
419: String iconPath = getHoverIconPath(element);
420: if (iconPath != null) {
421: return AbstractUIPlugin.imageDescriptorFromPlugin(
422: extendingPluginId, iconPath);
423: }
424: return null;
425: }
426:
427: public static boolean isSeparatorVisible(
428: IConfigurationElement element) {
429: String val = element
430: .getAttribute(IWorkbenchRegistryConstants.ATT_VISIBLE);
431: return Boolean.valueOf(val).booleanValue();
432: }
433:
434: public static String getClassSpec(IConfigurationElement element) {
435: return element
436: .getAttribute(IWorkbenchRegistryConstants.ATT_CLASS);
437: }
438:
439: public static String getCommandId(IConfigurationElement element) {
440: return element
441: .getAttribute(IWorkbenchRegistryConstants.ATT_COMMAND_ID);
442: }
443:
444: private int getStyle(IConfigurationElement element) {
445: String style = element
446: .getAttribute(IWorkbenchRegistryConstants.ATT_STYLE);
447: if (style == null || style.length() == 0) {
448: return CommandContributionItem.STYLE_PUSH;
449: }
450: if (IWorkbenchRegistryConstants.STYLE_TOGGLE.equals(style)) {
451: return CommandContributionItem.STYLE_CHECK;
452: }
453: if (IWorkbenchRegistryConstants.STYLE_RADIO.equals(style)) {
454: return CommandContributionItem.STYLE_RADIO;
455: }
456: if (IWorkbenchRegistryConstants.STYLE_PULLDOWN.equals(style)) {
457: return CommandContributionItem.STYLE_PULLDOWN;
458: }
459: return CommandContributionItem.STYLE_PUSH;
460: }
461:
462: /**
463: * @param element
464: * @return A map of parameters names to parameter values. All Strings. The
465: * map may be empty.
466: */
467: public static Map getParameters(IConfigurationElement element) {
468: HashMap map = new HashMap();
469: IConfigurationElement[] parameters = element
470: .getChildren(IWorkbenchRegistryConstants.TAG_PARAMETER);
471: for (int i = 0; i < parameters.length; i++) {
472: String name = parameters[i]
473: .getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
474: String value = parameters[i]
475: .getAttribute(IWorkbenchRegistryConstants.ATT_VALUE);
476: if (name != null && value != null) {
477: map.put(name, value);
478: }
479: }
480: return map;
481: }
482: }
|