/******************************************************************************* * Copyright (c) 2003, 2015 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 *******************************************************************************/ package org.eclipse.ui.navigator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPluginContribution; import org.eclipse.ui.actions.ActionContext; import org.eclipse.ui.actions.ActionGroup; import org.eclipse.ui.activities.WorkbenchActivityHelper; import org.eclipse.ui.internal.navigator.NavigatorContentService; import org.eclipse.ui.internal.navigator.NavigatorSafeRunnable; import org.eclipse.ui.internal.navigator.actions.CommonActionDescriptorManager; import org.eclipse.ui.internal.navigator.actions.CommonActionProviderDescriptor; import org.eclipse.ui.internal.navigator.extensions.CommonActionExtensionSite; import org.eclipse.ui.internal.navigator.extensions.SkeletonActionProvider; /** * <p> * Provides context menu items and {@link IActionBars} contributions for a particular abstract * viewer. The interface matches that of {@link ActionGroup} and may be used in the same manner. * Clients must call * {@link NavigatorActionService#prepareMenuForPlatformContributions(MenuManager, ISelectionProvider, boolean)} * when using this class to allow object or viewer contributions. The * <b>org.eclipse.ui.navigator.viewer/viewer/popupMenu</b> element may override whether platform * contributions are allowed to the menu with its <i>allowsPlatformContributions</i> attribute. * "Platform Contributions" are menu items that are added through the <b>org.eclipse.ui.popupMenus</b> * extension point. * </p> * <p> * A {@link CommonActionProvider} has opportunities to contribute to the context menu and * {@link org.eclipse.ui.IActionBars} whenever the selection in the viewer changes. Action Providers * are selected based on the enablement expressions of their associated content extension or their * own enablement expression if it is declared as a top-level <actionProvider /> element (of * the <b>org.eclipse.ui.navigator.navigatorContent</b> extension point). See the schema * documentation of <b>org.eclipse.ui.navigator.navigatorContent</b> for more information on how to * specify an Action Provider. * </p> * <p> * Clients that reuse this service outside of an instance of {@link CommonNavigator} must be sure * that {{@link #fillActionBars(IActionBars)} is called whenever the selection changes. The * retargetable actions for each selection could change, based on who contributed the items. * * @since 3.2 * */ public final class NavigatorActionService extends ActionGroup implements IMementoAware { private static final IContributionItem[] DEFAULT_GROUPS = new IContributionItem[]{new Separator(ICommonMenuConstants.GROUP_NEW), new GroupMarker(ICommonMenuConstants.GROUP_GOTO), new GroupMarker(ICommonMenuConstants.GROUP_OPEN), new GroupMarker(ICommonMenuConstants.GROUP_OPEN_WITH), new Separator(ICommonMenuConstants.GROUP_EDIT), new GroupMarker(ICommonMenuConstants.GROUP_SHOW), new GroupMarker(ICommonMenuConstants.GROUP_REORGANIZE), new GroupMarker(ICommonMenuConstants.GROUP_PORT), new Separator(ICommonMenuConstants.GROUP_GENERATE), new Separator(ICommonMenuConstants.GROUP_SEARCH), new Separator(ICommonMenuConstants.GROUP_BUILD), new Separator(ICommonMenuConstants.GROUP_ADDITIONS), new Separator(ICommonMenuConstants.GROUP_PROPERTIES)}; private final ICommonViewerSite commonViewerSite; private final StructuredViewer structuredViewer; private final NavigatorContentService contentService; private final INavigatorViewerDescriptor viewerDescriptor; private final Set actionProviderDescriptors = new HashSet(); /* * Map of CommonActionProviderDescriptors to CommonActionProviders */ private final Map actionProviderInstances = new HashMap(); private IMemento memento; private IContributionItem[] menuGroups; private boolean disposed = false; /** * @param aCommonViewerSite * A site that provides information about the context for extensions. * @param aStructuredViewer * The associated StructuredViewer. Used to initialize extensions. <b>May NOT be * null.</b> * @param aContentService * The associated INavigatorContentService (for extensions that coordinate behavior * with content extensions -- either nested or top-level action providers). <b>May * NOT be null.</b> */ public NavigatorActionService(ICommonViewerSite aCommonViewerSite, StructuredViewer aStructuredViewer, INavigatorContentService aContentService) { super(); Assert.isNotNull(aCommonViewerSite); Assert.isNotNull(aStructuredViewer); Assert.isNotNull(aContentService); commonViewerSite = aCommonViewerSite; contentService = (NavigatorContentService) aContentService; structuredViewer = aStructuredViewer; viewerDescriptor = contentService.getViewerDescriptor(); } /** * Prepares the menu for object contributions, if the option is set in the extension. The option * is controlled by the &lgt;popupMenu /> element's 'allowPlatformContributions' attribute. * Clients may choose to ignore this setting by supplying a value of <b>true</b> for the * <code>force</code> attribute. * * @param menu * The context menu of the IViewPart * @param aSelectionProvider * The selection provider that will supplement actions with a valid, current * selection. * @param force * A value of 'true' forces the menu to be registered for object/view contributions. * Otherwise, the option from the extension point will be respected. See * <b>org.eclipse.ui.navigator.viewer/viewer</b> for more information. */ public void prepareMenuForPlatformContributions(MenuManager menu, ISelectionProvider aSelectionProvider, boolean force) { Assert.isTrue(!disposed); if (commonViewerSite instanceof ICommonViewerWorkbenchSite) { /* * Hooks into the Eclipse framework for Object contributions, and View contributions. */ if (force || viewerDescriptor.allowsPlatformContributionsToContextMenu()) { ((ICommonViewerWorkbenchSite) commonViewerSite).registerContextMenu(contentService.getViewerDescriptor().getPopupMenuId(), menu, aSelectionProvider); } } } /** * Requests that the service invoke extensions to fill the given menu with Action Providers that * are interested in elements from the given selection. * * <p> * Object contributions (see <b>org.eclipes.ui.popupMenus</b>) may also respected by this * method if <code>toRespectObjectContributions</code> is true. * </p> * * @param aMenu * The menu being presented to the user. * @see ActionGroup#fillContextMenu(IMenuManager) */ @Override public void fillContextMenu(IMenuManager aMenu) { Assert.isTrue(!disposed); if (menuGroups == null) { createMenuGroups(); } for (IContributionItem menuGroup : menuGroups) { aMenu.add(menuGroup); } addCommonActionProviderMenu(aMenu); } private void createMenuGroups() { MenuInsertionPoint[] customPoints = viewerDescriptor.getCustomInsertionPoints(); if (customPoints == null) { menuGroups = DEFAULT_GROUPS; } else { menuGroups = new IContributionItem[customPoints.length]; for (int i = 0; i < customPoints.length; i++) { if (customPoints[i].isSeparator()) { menuGroups[i] = new Separator(customPoints[i].getName()); } else { menuGroups[i] = new GroupMarker(customPoints[i].getName()); } } } } private boolean filterActionProvider(final CommonActionProviderDescriptor providerDesc) { IPluginContribution piCont = new IPluginContribution() { @Override public String getLocalId() { return providerDesc.getId(); } @Override public String getPluginId() { return providerDesc.getPluginId(); } }; return WorkbenchActivityHelper.filterItem(piCont); } /** * @param aMenu */ private void addCommonActionProviderMenu(final IMenuManager aMenu) { final CommonActionProviderDescriptor[] providerDescriptors = CommonActionDescriptorManager .getInstance().findRelevantActionDescriptors(contentService, getContext()); if (providerDescriptors.length > 0) { for (CommonActionProviderDescriptor providerDescriptor : providerDescriptors) { final CommonActionProviderDescriptor providerDescriptorLocal = providerDescriptor; SafeRunner.run(new NavigatorSafeRunnable() { @Override public void run() throws Exception { if (!filterActionProvider(providerDescriptorLocal)) { CommonActionProvider provider = getActionProviderInstance(providerDescriptorLocal); provider.setContext(getContext()); provider.fillContextMenu(aMenu); } } }); } } } /** * Request that the service invoke extensions to fill the given IActionBars with retargetable * actions or view menu contributions from Action Providers that are interested in the given * selection. * * @param theActionBars * The action bars in use by the current view site. * @see ActionGroup#fillActionBars(IActionBars) */ @Override public void fillActionBars(final IActionBars theActionBars) { Assert.isTrue(!disposed); theActionBars.clearGlobalActionHandlers(); ActionContext context = getContext(); if (context == null) { context = new ActionContext(StructuredSelection.EMPTY); } final CommonActionProviderDescriptor[] providerDescriptors = CommonActionDescriptorManager .getInstance().findRelevantActionDescriptors(contentService, context); if (providerDescriptors.length > 0) { for (CommonActionProviderDescriptor providerDescriptor : providerDescriptors) { final CommonActionProviderDescriptor providerDesciptorLocal = providerDescriptor; final ActionContext actionContextLocal = context; SafeRunner.run(new NavigatorSafeRunnable() { @Override public void run() throws Exception { if (!filterActionProvider(providerDesciptorLocal)) { CommonActionProvider provider = null; provider = getActionProviderInstance(providerDesciptorLocal); provider.setContext(actionContextLocal); provider.fillActionBars(theActionBars); provider.updateActionBars(); } } }); } } theActionBars.updateActionBars(); theActionBars.getMenuManager().update(); } /** * Dispose of any state or resources held by the service. * * @see ActionGroup#dispose() */ @Override public void dispose() { synchronized (actionProviderInstances) { for (Iterator iter = actionProviderInstances.values().iterator(); iter.hasNext();) { CommonActionProvider element = (CommonActionProvider) iter.next(); element.dispose(); } actionProviderInstances.clear(); } actionProviderDescriptors.clear(); disposed = false; } /** * Use the given memento to restore the state of each Action Provider as it is initialized. * * @param aMemento * The memento retrieved from the dialog settings */ @Override public void restoreState(IMemento aMemento) { Assert.isTrue(!disposed); memento = aMemento; synchronized (actionProviderInstances) { for (Iterator actionProviderIterator = actionProviderInstances.values().iterator(); actionProviderIterator .hasNext();) { final CommonActionProvider provider = (CommonActionProvider) actionProviderIterator .next(); SafeRunner.run(new NavigatorSafeRunnable() { @Override public void run() throws Exception { provider.restoreState(memento); } }); } } } /** * Request that Action Providers save any state that they find interesting. * * @param aMemento * The memento retrieved from the dialog settings */ @Override public void saveState(IMemento aMemento) { Assert.isTrue(!disposed); memento = aMemento; CommonActionProvider provider = null; synchronized (actionProviderInstances) { for (Iterator actionProviderIterator = actionProviderInstances.values().iterator(); actionProviderIterator.hasNext();) { provider = (CommonActionProvider) actionProviderIterator.next(); provider.saveState(memento); } } } /** * * @param aProviderDescriptor * @return a CommonActionProvider * @noreference This method is not intended to be referenced by clients. */ public CommonActionProvider getActionProviderInstance( final CommonActionProviderDescriptor aProviderDescriptor) { CommonActionProvider provider = null; provider = (CommonActionProvider) actionProviderInstances.get(aProviderDescriptor); if (provider != null) { return provider; } synchronized (actionProviderInstances) { provider = (CommonActionProvider) actionProviderInstances.get(aProviderDescriptor); if (provider == null) { final CommonActionProvider[] retProvider = new CommonActionProvider[1]; SafeRunner.run(new NavigatorSafeRunnable() { @Override public void run() throws Exception { retProvider[0] = aProviderDescriptor.createActionProvider(); if (retProvider[0] != null) { initialize(aProviderDescriptor.getId(), aProviderDescriptor .getPluginId(), retProvider[0]); } } }); // This could happen in the exception case if (retProvider[0] == null) retProvider[0] = SkeletonActionProvider.INSTANCE; actionProviderInstances.put(aProviderDescriptor, retProvider[0]); provider = retProvider[0]; } } return provider; } private void initialize(final String id, final String pluginId, final CommonActionProvider anActionProvider) { if (anActionProvider != null && anActionProvider != SkeletonActionProvider.INSTANCE) { SafeRunner.run(new NavigatorSafeRunnable() { @Override public void run() throws Exception { ICommonActionExtensionSite configuration = new CommonActionExtensionSite(id, pluginId, commonViewerSite, contentService, structuredViewer); anActionProvider.init(configuration); anActionProvider.restoreState(memento); anActionProvider.setContext(new ActionContext(StructuredSelection.EMPTY)); } }); } } }