/*******************************************************************************
* Copyright (c) 2006, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Remy Chi Jian Suen <remy.suen@gmail.com> - Bug 221662 [Contributions] Extension point org.eclipse.ui.menus: sub menu contribution does not have icon even if specified
*******************************************************************************/
package org.eclipse.ui.internal.menus;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.expressions.Expression;
import org.eclipse.core.expressions.ExpressionConverter;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.InvalidRegistryObjectException;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarContributionItem;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.commands.ICommandImageService;
import org.eclipse.ui.internal.WorkbenchWindow;
import org.eclipse.ui.internal.provisional.presentations.IActionBarPresentationFactory;
import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
import org.eclipse.ui.internal.services.IWorkbenchLocationService;
import org.eclipse.ui.internal.util.Util;
import org.eclipse.ui.menus.AbstractContributionFactory;
import org.eclipse.ui.menus.CommandContributionItem;
import org.eclipse.ui.menus.CommandContributionItemParameter;
import org.eclipse.ui.menus.IContributionRoot;
import org.eclipse.ui.menus.IMenuService;
import org.eclipse.ui.menus.IWorkbenchContribution;
import org.eclipse.ui.menus.WorkbenchWindowControlContribution;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.services.IServiceLocator;
/**
* @since 3.3
*
*/
public class MenuAdditionCacheEntry extends AbstractContributionFactory {
private IConfigurationElement additionElement;
// Caches
/**
* If an {@link IConfigurationElement} is in the Set then we have already
* tried (and failed) to load the associated ExecutableExtension.
*
* This is used to prevent multiple retries which would spam the Log.
*/
Set failedLoads = new HashSet();
/**
* Maps an IConfigurationElement to its parsed Expression
*/
private HashMap visWhenMap = new HashMap();
/**
* The menu service on which to generate all subcaches.
*/
private IMenuService menuService;
/**
* List of caches created while processing this one. Used to clean up
* stale cache entries during removal
*/
private List subCaches;
private boolean hasAdditions = false;
public MenuAdditionCacheEntry(IMenuService menuService,
IConfigurationElement element) {
this(menuService, element, element.getAttribute(IWorkbenchRegistryConstants.TAG_LOCATION_URI),
element.getNamespaceIdentifier());
}
public MenuAdditionCacheEntry(IMenuService menuService,
IConfigurationElement element, String location, String namespace) {
super(location, namespace);
this.menuService = menuService;
this.additionElement = element;
findAdditions();
generateSubCaches();
}
/**
*
*/
private void generateSubCaches() {
IConfigurationElement[] items = additionElement.getChildren();
for (int i = 0; i < items.length; i++) {
String itemType = items[i].getName();
if (IWorkbenchRegistryConstants.TAG_MENU.equals(itemType)
|| IWorkbenchRegistryConstants.TAG_TOOLBAR.equals(itemType)) {
// Menus and toolbars are special...we have to add any sub menu
// items into their own cache
// If the locationURI is null then this should be a sub menu
// addition..create the 'root' URI
String location = new MenuLocationURI(getLocation())
.getScheme()
+ ":" + MenuAdditionCacheEntry.getId(items[i]); //$NON-NLS-1$
// -ALL- contibuted menus must have an id so create one
// if necessary
MenuAdditionCacheEntry subMenuEntry = new MenuAdditionCacheEntry(
menuService, items[i], location, getNamespace());
menuService.addContributionFactory(subMenuEntry);
if (subCaches == null)
subCaches = new ArrayList();
subCaches.add(subMenuEntry);
}
}
}
private Expression getVisibleWhenForItem(IContributionItem item, IConfigurationElement configElement) {
if (!visWhenMap.containsKey(configElement)) {
// Not parsed yet
try {
IConfigurationElement[] visibleConfig = configElement
.getChildren(IWorkbenchRegistryConstants.TAG_VISIBLE_WHEN);
if (visibleConfig.length > 0 && visibleConfig.length < 2) {
IConfigurationElement[] visibleChild = visibleConfig[0]
.getChildren();
if (visibleChild.length > 0) {
Expression visWhen = ExpressionConverter.getDefault()
.perform(visibleChild[0]);
visWhenMap.put(configElement, visWhen);
}
}
} catch (InvalidRegistryObjectException e) {
visWhenMap.put(configElement, null);
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CoreException e) {
visWhenMap.put(configElement, null);
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return (Expression) visWhenMap.get(configElement);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.internal.menus.AbstractContributionFactory#createContributionItems(org.eclipse.ui.internal.menus.IMenuService,
* java.util.List)
*/
public void createContributionItems(IServiceLocator serviceLocator,
IContributionRoot additions) {
IActionBarPresentationFactory actionBarPresentationFactory = null;
IWorkbenchLocationService wls = (IWorkbenchLocationService) serviceLocator
.getService(IWorkbenchLocationService.class);
WorkbenchWindow window = (WorkbenchWindow) wls.getWorkbenchWindow();
if (window != null) {
actionBarPresentationFactory = window
.getActionBarPresentationFactory();
}
IConfigurationElement[] items = additionElement.getChildren();
for (int i = 0; i < items.length; i++) {
String itemType = items[i].getName();
IContributionItem newItem = null;
if (IWorkbenchRegistryConstants.TAG_COMMAND.equals(itemType)) {
newItem = createCommandAdditionContribution(serviceLocator,
items[i]);
} else if (IWorkbenchRegistryConstants.TAG_DYNAMIC.equals(itemType)) {
newItem = createDynamicAdditionContribution(serviceLocator,
items[i]);
} else if (IWorkbenchRegistryConstants.TAG_CONTROL.equals(itemType)) {
newItem = createControlAdditionContribution(serviceLocator,
items[i]);
} else if (IWorkbenchRegistryConstants.TAG_SEPARATOR
.equals(itemType)) {
newItem = createSeparatorAdditionContribution(items[i]);
} else if (IWorkbenchRegistryConstants.TAG_MENU.equals(itemType)) {
newItem = createMenuAdditionContribution(items[i]);
} else if (IWorkbenchRegistryConstants.TAG_TOOLBAR.equals(itemType)) {
newItem = createToolBarAdditionContribution(
actionBarPresentationFactory, items[i]);
}
if (newItem instanceof InternalControlContribution) {
((InternalControlContribution) newItem).setWorkbenchWindow(window);
}
// Cache the relationship between the ICI and the
// registry element used to back it
if (newItem != null) {
additions.addContributionItem(newItem,
getVisibleWhenForItem(newItem, items[i]));
}
}
}
/**
* @param configurationElement
* @return the toolbar contribution item
*/
private IContributionItem createToolBarAdditionContribution(
IActionBarPresentationFactory actionBarPresentationFactory,
IConfigurationElement configurationElement) {
if (!inToolbar()) {
return null;
}
if (actionBarPresentationFactory != null) {
return actionBarPresentationFactory.createToolBarContributionItem(
actionBarPresentationFactory.createToolBarManager(),
getId(configurationElement));
}
return new ToolBarContributionItem(new ToolBarManager(),
getId(configurationElement));
}
/**
* @return <code>true</code> if this is a toolbar contribution
*/
private boolean inToolbar() {
return getLocation().startsWith("toolbar"); //$NON-NLS-1$
}
/**
* @param configurationElement
* @return the menu manager
*/
private IContributionItem createMenuAdditionContribution(
final IConfigurationElement menuAddition) {
// Is this for a menu or a ToolBar ? We can't create
// a menu directly under a Toolbar; we have to add an
// item of style 'pulldown'
if (inToolbar()) {
return null;
}
String text = getLabel(menuAddition);
String mnemonic = getMnemonic(menuAddition);
if (text != null && mnemonic != null) {
int idx = text.indexOf(mnemonic);
if (idx != -1) {
text = text.substring(0, idx) + '&' + text.substring(idx);
}
}
MenuManager menuManager = new MenuManager(text, getIconDescriptor(menuAddition), getId(menuAddition));
menuManager.setActionDefinitionId(getCommandId(menuAddition));
return menuManager;
}
/**
* @param configurationElement
* @return
*/
private IContributionItem createSeparatorAdditionContribution(
final IConfigurationElement sepAddition) {
if (isSeparatorVisible(sepAddition)) {
return new Separator(getName(sepAddition));
}
return new GroupMarker(getName(sepAddition));
}
/**
* @return
*/
private IContributionItem createDynamicAdditionContribution(
final IServiceLocator locator,
final IConfigurationElement dynamicAddition) {
// If we've already tried (and failed) to load the
// executable extension then skip this addition.
if (failedLoads.contains(dynamicAddition))
return null;
// Attempt to load the addition's EE (creates a new instance)
final ContributionItem loadedDynamicContribution = (ContributionItem) Util
.safeLoadExecutableExtension(dynamicAddition,
IWorkbenchRegistryConstants.ATT_CLASS,
ContributionItem.class);
// Cache failures
if (loadedDynamicContribution == null) {
failedLoads.add(loadedDynamicContribution);
return null;
}
loadedDynamicContribution.setId(getId(dynamicAddition));
if (loadedDynamicContribution instanceof IWorkbenchContribution) {
((IWorkbenchContribution)loadedDynamicContribution).initialize(locator);
}
// TODO provide a proxy IContributionItem that defers instantiation
// adding contribution items in a menu instantiates this object ...
// we need to defer loading until fill(*) is called.
return loadedDynamicContribution;
}
private IContributionItem createControlAdditionContribution(
final IServiceLocator locator,
final IConfigurationElement widgetAddition) {
if (!inToolbar()) {
return null;
}
// If we've already tried (and failed) to load the
// executable extension then skip this addirion.
if (failedLoads.contains(widgetAddition))
return null;
// Attempt to load the addition's EE (creates a new instance)
final WorkbenchWindowControlContribution loadedWidget = (WorkbenchWindowControlContribution) Util
.safeLoadExecutableExtension(widgetAddition,
IWorkbenchRegistryConstants.ATT_CLASS,
WorkbenchWindowControlContribution.class);
// Cache failures
if (loadedWidget == null) {
failedLoads.add(widgetAddition);
return null;
}
// explicitly set the id
loadedWidget.setId(getId(widgetAddition));
if (loadedWidget instanceof IWorkbenchContribution) {
((IWorkbenchContribution)loadedWidget).initialize(locator);
}
return loadedWidget;
}
private IContributionItem createCommandAdditionContribution(
IServiceLocator locator, final IConfigurationElement commandAddition) {
CommandContributionItemParameter parm = new CommandContributionItemParameter(
locator, getId(commandAddition), getCommandId(commandAddition),
getParameters(commandAddition),
getIconDescriptor(commandAddition),
getDisabledIconDescriptor(commandAddition),
getHoverIconDescriptor(commandAddition),
getLabel(commandAddition), getMnemonic(commandAddition),
getTooltip(commandAddition), getStyle(commandAddition),
getHelpContextId(commandAddition),
getVisibleEnabled(commandAddition));
if (inToolbar()) {
parm.iconStyle = ICommandImageService.IMAGE_STYLE_TOOLBAR;
}
parm.mode = getMode(commandAddition);
return new CommandContributionItem(parm);
}
/**
* @param element the configuration element
* @return <code>true</code> if the checkEnabled is <code>true</code>.
*/
static boolean getVisibleEnabled(IConfigurationElement element) {
IConfigurationElement[] children = element
.getChildren(IWorkbenchRegistryConstants.TAG_VISIBLE_WHEN);
String checkEnabled = null;
if (children.length>0) {
checkEnabled = children[0]
.getAttribute(IWorkbenchRegistryConstants.ATT_CHECK_ENABLED);
}
return checkEnabled != null && checkEnabled.equalsIgnoreCase("true"); //$NON-NLS-1$
}
/*
* Support Utilities
*/
public static String getId(IConfigurationElement element) {
String id = element.getAttribute(IWorkbenchRegistryConstants.ATT_ID);
// For sub-menu management -all- items must be id'd so enforce this
// here (we could optimize by checking the 'name' of the config
// element == "menu"
if (id == null || id.length() == 0) {
id = getCommandId(element);
}
if (id == null || id.length() == 0) {
id = element.toString();
}
return id;
}
static String getName(IConfigurationElement element) {
return element.getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
}
static int getMode(IConfigurationElement element) {
if ("FORCE_TEXT".equals(element.getAttribute(IWorkbenchRegistryConstants.ATT_MODE))) { //$NON-NLS-1$
return CommandContributionItem.MODE_FORCE_TEXT;
}
return 0;
}
static String getLabel(IConfigurationElement element) {
return element.getAttribute(IWorkbenchRegistryConstants.ATT_LABEL);
}
static String getMnemonic(IConfigurationElement element) {
return element.getAttribute(IWorkbenchRegistryConstants.ATT_MNEMONIC);
}
static String getTooltip(IConfigurationElement element) {
return element.getAttribute(IWorkbenchRegistryConstants.ATT_TOOLTIP);
}
static String getIconPath(IConfigurationElement element) {
return element.getAttribute(IWorkbenchRegistryConstants.ATT_ICON);
}
static String getDisabledIconPath(IConfigurationElement element) {
return element
.getAttribute(IWorkbenchRegistryConstants.ATT_DISABLEDICON);
}
static String getHoverIconPath(IConfigurationElement element) {
return element.getAttribute(IWorkbenchRegistryConstants.ATT_HOVERICON);
}
static ImageDescriptor getIconDescriptor(IConfigurationElement element) {
String extendingPluginId = element.getDeclaringExtension()
.getContributor().getName();
String iconPath = getIconPath(element);
if (iconPath != null) {
return AbstractUIPlugin.imageDescriptorFromPlugin(
extendingPluginId, iconPath);
}
return null;
}
static ImageDescriptor getDisabledIconDescriptor(
IConfigurationElement element) {
String extendingPluginId = element.getDeclaringExtension()
.getContributor().getName();
String iconPath = getDisabledIconPath(element);
if (iconPath != null) {
return AbstractUIPlugin.imageDescriptorFromPlugin(
extendingPluginId, iconPath);
}
return null;
}
static ImageDescriptor getHoverIconDescriptor(IConfigurationElement element) {
String extendingPluginId = element.getDeclaringExtension()
.getContributor().getName();
String iconPath = getHoverIconPath(element);
if (iconPath != null) {
return AbstractUIPlugin.imageDescriptorFromPlugin(
extendingPluginId, iconPath);
}
return null;
}
static String getHelpContextId(IConfigurationElement element) {
return element
.getAttribute(IWorkbenchRegistryConstants.ATT_HELP_CONTEXT_ID);
}
public static boolean isSeparatorVisible(IConfigurationElement element) {
String val = element
.getAttribute(IWorkbenchRegistryConstants.ATT_VISIBLE);
return Boolean.valueOf(val).booleanValue();
}
public static String getClassSpec(IConfigurationElement element) {
return element.getAttribute(IWorkbenchRegistryConstants.ATT_CLASS);
}
public static String getCommandId(IConfigurationElement element) {
return element.getAttribute(IWorkbenchRegistryConstants.ATT_COMMAND_ID);
}
private int getStyle(IConfigurationElement element) {
String style = element
.getAttribute(IWorkbenchRegistryConstants.ATT_STYLE);
if (style == null || style.length() == 0) {
return CommandContributionItem.STYLE_PUSH;
}
if (IWorkbenchRegistryConstants.STYLE_TOGGLE.equals(style)) {
return CommandContributionItem.STYLE_CHECK;
}
if (IWorkbenchRegistryConstants.STYLE_RADIO.equals(style)) {
return CommandContributionItem.STYLE_RADIO;
}
if (IWorkbenchRegistryConstants.STYLE_PULLDOWN.equals(style)) {
return CommandContributionItem.STYLE_PULLDOWN;
}
return CommandContributionItem.STYLE_PUSH;
}
/**
* @param element
* @return A map of parameters names to parameter values. All Strings. The
* map may be empty.
*/
public static Map getParameters(IConfigurationElement element) {
HashMap map = new HashMap();
IConfigurationElement[] parameters = element
.getChildren(IWorkbenchRegistryConstants.TAG_PARAMETER);
for (int i = 0; i < parameters.length; i++) {
String name = parameters[i]
.getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
String value = parameters[i]
.getAttribute(IWorkbenchRegistryConstants.ATT_VALUE);
if (name != null && value != null) {
map.put(name, value);
}
}
return map;
}
/**
* @return
*/
public IConfigurationElement getConfigElement() {
return additionElement;
}
/**
* @return Returns the subCaches.
*/
public List getSubCaches() {
return subCaches;
}
private void findAdditions() {
IConfigurationElement[] items = additionElement.getChildren();
boolean done = false;
for (int i = 0; i < items.length && !done; i++) {
String itemType = items[i].getName();
if (IWorkbenchRegistryConstants.TAG_SEPARATOR
.equals(itemType)) {
if (IWorkbenchActionConstants.MB_ADDITIONS.equals(getName(items[i]))) {
hasAdditions = true;
done = true;
}
}
}
}
public boolean hasAdditions() {
return hasAdditions;
}
}