| /******************************************************************************* |
| * Copyright (c) 2000, 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 43573 [Contributions] Support icon in <menu> |
| *******************************************************************************/ |
| package org.eclipse.ui.internal; |
| |
| import java.util.ArrayList; |
| |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.jface.action.AbstractGroupMarker; |
| import org.eclipse.jface.action.GroupMarker; |
| import org.eclipse.jface.action.IContributionItem; |
| import org.eclipse.jface.action.IContributionManager; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.IToolBarManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.ui.IWorkbenchActionConstants; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants; |
| import org.eclipse.ui.internal.registry.RegistryReader; |
| import org.eclipse.ui.plugin.AbstractUIPlugin; |
| |
| /** |
| * This class contains shared functionality for reading action contributions |
| * from plugins into workbench parts (both editors and views). |
| */ |
| public abstract class PluginActionBuilder extends RegistryReader { |
| protected String targetID; |
| |
| protected String targetContributionTag; |
| |
| protected BasicContribution currentContribution; |
| |
| protected ArrayList cache; |
| |
| /** |
| * The default constructor. |
| */ |
| public PluginActionBuilder() { |
| } |
| |
| /** |
| * Contributes submenus and/or actions into the provided menu and tool bar |
| * managers. |
| * |
| * @param menu the menu to contribute to |
| * @param toolbar the toolbar to contribute to |
| * @param appendIfMissing append containers if missing |
| */ |
| public final void contribute(IMenuManager menu, IToolBarManager toolbar, |
| boolean appendIfMissing) { |
| if (cache == null) { |
| return; |
| } |
| |
| for (int i = 0; i < cache.size(); i++) { |
| BasicContribution contribution = (BasicContribution) cache.get(i); |
| contribution.contribute(menu, appendIfMissing, toolbar, |
| appendIfMissing); |
| } |
| } |
| |
| /** |
| * This factory method returns a new ActionDescriptor for the |
| * configuration element. It should be implemented by subclasses. |
| */ |
| protected abstract ActionDescriptor createActionDescriptor( |
| IConfigurationElement element); |
| |
| /** |
| * Factory method to create the helper contribution class that will hold |
| * onto the menus and actions contributed. |
| */ |
| protected BasicContribution createContribution() { |
| return new BasicContribution(); |
| } |
| |
| /** |
| * Returns the name of the part ID attribute that is expected |
| * in the target extension. |
| */ |
| protected String getTargetID(IConfigurationElement element) { |
| String value = element.getAttribute(IWorkbenchRegistryConstants.ATT_TARGET_ID); |
| return value != null ? value : "???"; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns the id of this contributions. |
| */ |
| protected String getID(IConfigurationElement element) { |
| String value = element.getAttribute(IWorkbenchRegistryConstants.ATT_ID); |
| return value != null ? value : "???"; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Reads the contributions from the registry for the provided workbench |
| * part and the provided extension point ID. |
| */ |
| protected void readContributions(String id, String tag, |
| String extensionPoint) { |
| cache = null; |
| currentContribution = null; |
| targetID = id; |
| targetContributionTag = tag; |
| // RAP [bm]: |
| // readRegistry(Platform.getExtensionRegistry(), PlatformUI.PLUGIN_ID, |
| // extensionPoint); |
| readRegistry(Platform.getExtensionRegistry(), PlatformUI.PLUGIN_EXTENSION_NAME_SPACE, |
| extensionPoint); |
| // RAPEND: [bm] |
| } |
| |
| /** |
| * Implements abstract method to handle the provided XML element |
| * in the registry. |
| */ |
| protected boolean readElement(IConfigurationElement element) { |
| String tag = element.getName(); |
| |
| // Ignore all object contributions element as these |
| // are handled by the ObjectActionContributorReader. |
| if (tag.equals(IWorkbenchRegistryConstants.TAG_OBJECT_CONTRIBUTION)) { |
| return true; |
| } |
| |
| // Found top level contribution element |
| if (tag.equals(targetContributionTag)) { |
| if (targetID != null) { |
| // Ignore contributions not matching target id |
| String id = getTargetID(element); |
| if (id == null || !id.equals(targetID)) { |
| return true; |
| } |
| } |
| |
| // Read its sub-elements |
| currentContribution = createContribution(); |
| readElementChildren(element); |
| if (cache == null) { |
| cache = new ArrayList(4); |
| } |
| cache.add(currentContribution); |
| currentContribution = null; |
| return true; |
| } |
| |
| // Found menu contribution sub-element |
| if (tag.equals(IWorkbenchRegistryConstants.TAG_MENU)) { |
| currentContribution.addMenu(element); |
| return true; |
| } |
| |
| // Found action contribution sub-element |
| if (tag.equals(IWorkbenchRegistryConstants.TAG_ACTION)) { |
| currentContribution.addAction(createActionDescriptor(element)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Helper class to collect the menus and actions defined within a |
| * contribution element. |
| */ |
| protected static class BasicContribution { |
| protected ArrayList menus; |
| |
| protected ArrayList actions; |
| |
| /** |
| * Add a menu. |
| * |
| * @param element the element to base the menu on |
| */ |
| public void addMenu(IConfigurationElement element) { |
| if (menus == null) { |
| menus = new ArrayList(1); |
| } |
| menus.add(element); |
| } |
| |
| /** |
| * Add an action. |
| * |
| * @param desc the descriptor |
| */ |
| public void addAction(ActionDescriptor desc) { |
| if (actions == null) { |
| actions = new ArrayList(3); |
| } |
| actions.add(desc); |
| } |
| |
| /** |
| * Contributes submenus and/or actions into the provided menu and tool bar |
| * managers. |
| * |
| * The elements added are filtered based on activity enablement. |
| * @param menu the menu to contribute to |
| * @param menuAppendIfMissing whether to append missing groups to menus |
| * @param toolbar the toolbar to contribute to |
| * @param toolAppendIfMissing whether to append missing groups to toolbars |
| */ |
| public void contribute(IMenuManager menu, boolean menuAppendIfMissing, |
| IToolBarManager toolbar, boolean toolAppendIfMissing) { |
| if (menus != null && menu != null) { |
| for (int i = 0; i < menus.size(); i++) { |
| IConfigurationElement menuElement = (IConfigurationElement) menus |
| .get(i); |
| contributeMenu(menuElement, menu, menuAppendIfMissing); |
| } |
| } |
| |
| if (actions != null) { |
| for (int i = 0; i < actions.size(); i++) { |
| ActionDescriptor ad = (ActionDescriptor) actions.get(i); |
| if (menu != null) { |
| contributeMenuAction(ad, menu, menuAppendIfMissing); |
| } |
| if (toolbar != null) { |
| contributeToolbarAction(ad, toolbar, |
| toolAppendIfMissing); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Creates a menu from the information in the menu configuration element and |
| * adds it into the provided menu manager. If 'appendIfMissing' is true, and |
| * menu path slot is not found, it will be created and menu will be added |
| * into it. Otherwise, add operation will fail. |
| */ |
| protected void contributeMenu(IConfigurationElement menuElement, |
| IMenuManager mng, boolean appendIfMissing) { |
| // Get config data. |
| String id = menuElement.getAttribute(IWorkbenchRegistryConstants.ATT_ID); |
| String label = menuElement.getAttribute(IWorkbenchRegistryConstants.ATT_LABEL); |
| String path = menuElement.getAttribute(IWorkbenchRegistryConstants.ATT_PATH); |
| String icon = menuElement.getAttribute(IWorkbenchRegistryConstants.ATT_ICON); |
| ImageDescriptor image = null; |
| if (icon != null) { |
| String extendingPluginId = menuElement.getDeclaringExtension() |
| .getContributor().getName(); |
| image = AbstractUIPlugin.imageDescriptorFromPlugin( |
| extendingPluginId, icon); |
| } |
| if (label == null) { |
| WorkbenchPlugin.log("Plugin \'" //$NON-NLS-1$ |
| + menuElement.getContributor().getName() |
| + "\' invalid Menu Extension (label == null): " + id); //$NON-NLS-1$ |
| return; |
| } |
| |
| // Calculate menu path and group. |
| String group = null; |
| if (path != null) { |
| int loc = path.lastIndexOf('/'); |
| if (loc != -1) { |
| group = path.substring(loc + 1); |
| path = path.substring(0, loc); |
| } else { |
| // assume that path represents a slot |
| // so actual path portion should be null |
| group = path; |
| path = null; |
| } |
| } |
| |
| // Find parent menu. |
| IMenuManager parent = mng; |
| if (path != null) { |
| parent = mng.findMenuUsingPath(path); |
| if (parent == null) { |
| ideLog("Plugin \'" //$NON-NLS-1$ |
| + menuElement.getContributor().getName() |
| + "\' invalid Menu Extension (Path \'" //$NON-NLS-1$ |
| + path + "\' is invalid): " + id); //$NON-NLS-1$ |
| return; |
| } |
| } |
| |
| // Find reference group. |
| if (group == null) { |
| group = IWorkbenchActionConstants.MB_ADDITIONS; |
| } |
| IContributionItem sep = parent.find(group); |
| if (sep == null) { |
| if (appendIfMissing) { |
| addGroup(parent, group); |
| } else { |
| WorkbenchPlugin |
| .log("Plugin \'" //$NON-NLS-1$ |
| + menuElement.getContributor().getName() |
| + "\' invalid Menu Extension (Group \'" //$NON-NLS-1$ |
| + group + "\' is invalid): " + id); //$NON-NLS-1$ |
| return; |
| } |
| } |
| |
| // If the menu does not exist create it. |
| IMenuManager newMenu = parent.findMenuUsingPath(id); |
| if (newMenu == null) { |
| newMenu = new MenuManager(label, image, id); |
| } |
| |
| // Add the menu |
| try { |
| insertAfter(parent, group, newMenu); |
| } catch (IllegalArgumentException e) { |
| WorkbenchPlugin |
| .log("Plugin \'" //$NON-NLS-1$ |
| + menuElement.getContributor().getName() |
| + "\' invalid Menu Extension (Group \'" //$NON-NLS-1$ |
| + group + "\' is missing): " + id); //$NON-NLS-1$ |
| } |
| |
| // Get the menu again as it may be wrapped, otherwise adding |
| // the separators and group markers below will not be wrapped |
| // properly if the menu was just created. |
| newMenu = parent.findMenuUsingPath(id); |
| if (newMenu == null) { |
| WorkbenchPlugin.log("Could not find new menu: " + id); //$NON-NLS-1$ |
| } |
| |
| // Create separators. |
| IConfigurationElement[] children = menuElement.getChildren(); |
| for (int i = 0; i < children.length; i++) { |
| String childName = children[i].getName(); |
| if (childName.equals(IWorkbenchRegistryConstants.TAG_SEPARATOR)) { |
| contributeSeparator(newMenu, children[i]); |
| } else if (childName.equals(IWorkbenchRegistryConstants.TAG_GROUP_MARKER)) { |
| contributeGroupMarker(newMenu, children[i]); |
| } |
| } |
| } |
| |
| /** |
| * Contributes action from action descriptor into the provided menu manager. |
| */ |
| protected void contributeMenuAction(ActionDescriptor ad, |
| IMenuManager menu, boolean appendIfMissing) { |
| // Get config data. |
| String mpath = ad.getMenuPath(); |
| String mgroup = ad.getMenuGroup(); |
| if (mpath == null && mgroup == null) { |
| return; |
| } |
| // Find parent menu. |
| IMenuManager parent = menu; |
| if (mpath != null) { |
| parent = parent.findMenuUsingPath(mpath); |
| if (parent == null) { |
| ideLog("Plug-in '" + ad.getPluginId() + "' contributed an invalid Menu Extension (Path: '" + mpath + "' is invalid): " + ad.getId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return; |
| } |
| } |
| |
| // Find reference group. |
| if (mgroup == null) { |
| mgroup = IWorkbenchActionConstants.MB_ADDITIONS; |
| } |
| IContributionItem sep = parent.find(mgroup); |
| if (sep == null) { |
| if (appendIfMissing) { |
| addGroup(parent, mgroup); |
| } else { |
| WorkbenchPlugin |
| .log("Plug-in '" + ad.getPluginId() + "' contributed an invalid Menu Extension (Group: '" + mgroup + "' is invalid): " + ad.getId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return; |
| } |
| } |
| |
| // Add action. |
| try { |
| insertAfter(parent, mgroup, ad); |
| } catch (IllegalArgumentException e) { |
| WorkbenchPlugin |
| .log("Plug-in '" + ad.getPluginId() + "' contributed an invalid Menu Extension (Group: '" + mgroup + "' is missing): " + ad.getId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| } |
| |
| /** |
| * Creates a named menu separator from the information in the configuration element. |
| * If the separator already exists do not create a second. |
| */ |
| protected void contributeSeparator(IMenuManager menu, |
| IConfigurationElement element) { |
| String id = element.getAttribute(IWorkbenchRegistryConstants.ATT_NAME); |
| if (id == null || id.length() <= 0) { |
| return; |
| } |
| IContributionItem sep = menu.find(id); |
| if (sep != null) { |
| return; |
| } |
| insertMenuGroup(menu, new Separator(id)); |
| } |
| |
| /** |
| * Creates a named menu group marker from the information in the configuration element. |
| * If the marker already exists do not create a second. |
| */ |
| protected void contributeGroupMarker(IMenuManager menu, |
| IConfigurationElement element) { |
| String id = element.getAttribute(IWorkbenchRegistryConstants.ATT_NAME); |
| if (id == null || id.length() <= 0) { |
| return; |
| } |
| IContributionItem marker = menu.find(id); |
| if (marker != null) { |
| return; |
| } |
| insertMenuGroup(menu, new GroupMarker(id)); |
| } |
| |
| /** |
| * Contributes action from the action descriptor into the provided tool bar manager. |
| */ |
| protected void contributeToolbarAction(ActionDescriptor ad, |
| IToolBarManager toolbar, boolean appendIfMissing) { |
| // Get config data. |
| String tId = ad.getToolbarId(); |
| String tgroup = ad.getToolbarGroupId(); |
| if (tId == null && tgroup == null) { |
| return; |
| } |
| |
| // Find reference group. |
| if (tgroup == null) { |
| tgroup = IWorkbenchActionConstants.MB_ADDITIONS; |
| } |
| IContributionItem sep = null; |
| sep = toolbar.find(tgroup); |
| if (sep == null) { |
| if (appendIfMissing) { |
| addGroup(toolbar, tgroup); |
| } else { |
| WorkbenchPlugin |
| .log("Plug-in '" + ad.getPluginId() //$NON-NLS-1$ |
| + "' invalid Toolbar Extension (Group \'" //$NON-NLS-1$ |
| + tgroup + "\' is invalid): " + ad.getId()); //$NON-NLS-1$ |
| return; |
| } |
| } |
| // Add action to tool bar. |
| try { |
| insertAfter(toolbar, tgroup, ad); |
| } catch (IllegalArgumentException e) { |
| WorkbenchPlugin |
| .log("Plug-in '" + ad.getPluginId() //$NON-NLS-1$ |
| + "' invalid Toolbar Extension (Group \'" //$NON-NLS-1$ |
| + tgroup + "\' is missing): " + ad.getId()); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Inserts the separator or group marker into the menu. Subclasses may override. |
| */ |
| protected void insertMenuGroup(IMenuManager menu, |
| AbstractGroupMarker marker) { |
| menu.add(marker); |
| } |
| |
| /** |
| * Inserts an action after another named contribution item. |
| * Subclasses may override. |
| */ |
| protected void insertAfter(IContributionManager mgr, String refId, |
| ActionDescriptor desc) { |
| final PluginActionContributionItem item = new PluginActionContributionItem(desc.getAction()); |
| item.setMode(desc.getMode()); |
| insertAfter(mgr, refId, item); |
| } |
| |
| /** |
| * Inserts a contribution item after another named contribution item. |
| * Subclasses may override. |
| */ |
| protected void insertAfter(IContributionManager mgr, String refId, |
| IContributionItem item) { |
| mgr.insertAfter(refId, item); |
| } |
| |
| /** |
| * Adds a group to a contribution manager. |
| * Subclasses may override. |
| */ |
| protected void addGroup(IContributionManager mgr, String name) { |
| mgr.add(new Separator(name)); |
| } |
| |
| /** |
| * Disposes this contribution. |
| * |
| * @since 3.1 |
| */ |
| public void dispose() { |
| // do nothing |
| } |
| |
| /** |
| * Disposes the actions. |
| * |
| * @since 3.1 |
| */ |
| protected void disposeActions() { |
| if (actions != null) { |
| for (int i = 0; i < actions.size(); i++) { |
| PluginAction proxy = ((ActionDescriptor) actions.get(i)) |
| .getAction(); |
| proxy.dispose(); |
| } |
| actions = null; |
| } |
| } |
| } |
| |
| private static boolean allowIdeLogging = false; |
| |
| /** |
| * If set to <code>false</code>, some of the logs that can be caused by |
| * use IDE plugins from an RCP app will be ignored. |
| * |
| * @param b |
| * Log the errors or not. |
| * @since 3.3 |
| */ |
| public static void setAllowIdeLogging(boolean b) { |
| allowIdeLogging = b; |
| } |
| |
| /** |
| * These are log messages that should be ignored by RCP apps when using the |
| * IDE plugins. |
| * |
| * @param msg |
| * @since 3.3 |
| */ |
| private static void ideLog(String msg) { |
| if (allowIdeLogging) { |
| WorkbenchPlugin.log(msg); |
| } |
| } |
| } |