blob: 4ebfccc3e54290c33cf2191caf76a3fd321b2d2e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2006 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.ISafeRunnable;
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.actions.ActionContext;
import org.eclipse.ui.actions.ActionGroup;
import org.eclipse.ui.internal.navigator.NavigatorContentService;
import org.eclipse.ui.internal.navigator.NavigatorPlugin;
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 &lt;actionProvider /&gt; 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 /&gt; 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)
*/
public void fillContextMenu(IMenuManager aMenu) {
Assert.isTrue(!disposed);
if (menuGroups == null) {
createMenuGroups();
}
for (int i = 0; i < menuGroups.length; i++) {
aMenu.add(menuGroups[i]);
}
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());
}
}
}
}
/**
* @param aMenu
*/
private void addCommonActionProviderMenu(IMenuManager aMenu) {
CommonActionProviderDescriptor[] providerDescriptors = CommonActionDescriptorManager.getInstance().findRelevantActionDescriptors(contentService, getContext());
if (providerDescriptors.length > 0) {
CommonActionProvider provider = null;
for (int i = 0; i < providerDescriptors.length; i++) {
try {
provider = getActionProviderInstance(providerDescriptors[i]);
provider.setContext(getContext());
provider.fillContextMenu(aMenu);
} catch (Throwable t) {
NavigatorPlugin.logError(0, t.getMessage(), t);
}
}
}
}
/**
* 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)
*/
public void fillActionBars(IActionBars theActionBars) {
Assert.isTrue(!disposed);
theActionBars.clearGlobalActionHandlers();
ActionContext context = getContext();
if (context == null) {
context = new ActionContext(StructuredSelection.EMPTY);
}
CommonActionProviderDescriptor[] providerDescriptors = CommonActionDescriptorManager.getInstance().findRelevantActionDescriptors(contentService, context);
if (providerDescriptors.length > 0) {
CommonActionProvider provider = null;
for (int i = 0; i < providerDescriptors.length; i++) {
try {
provider = getActionProviderInstance(providerDescriptors[i]);
if(provider != null) {
provider.setContext(context);
provider.fillActionBars(theActionBars);
provider.updateActionBars();
}
} catch (RuntimeException e) {
NavigatorPlugin.logError(0, e.getMessage(), e);
}
}
}
theActionBars.updateActionBars();
theActionBars.getMenuManager().update();
}
/**
* Dispose of any state or resources held by the service.
*
* @see ActionGroup#dispose()
*/
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
*/
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();
ISafeRunnable runnable = new ISafeRunnable() {
public void run() throws Exception {
provider.restoreState(memento);
}
public void handleException(Throwable exception) {
NavigatorPlugin.logError(0, "Could not restore state for action provider " + provider.getClass(), exception); //$NON-NLS-1$
}
};
SafeRunner.run(runnable);
}
}
}
/**
* Request that Action Providers save any state that they find interesting.
*
* @param aMemento
* The memento retrieved from the dialog settings
*/
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);
}
}
}
private CommonActionProvider getActionProviderInstance(
CommonActionProviderDescriptor aProviderDescriptor) {
CommonActionProvider provider = null;
try {
provider = (CommonActionProvider) actionProviderInstances
.get(aProviderDescriptor);
if (provider != null) {
return provider;
}
synchronized (actionProviderInstances) {
provider = (CommonActionProvider) actionProviderInstances
.get(aProviderDescriptor);
if (provider == null) {
provider = aProviderDescriptor.createActionProvider();
if (provider != null) {
initialize(aProviderDescriptor.getId(), provider);
actionProviderInstances.put(aProviderDescriptor, provider);
} else {
actionProviderInstances.put(aProviderDescriptor,
(provider = SkeletonActionProvider.INSTANCE));
}
}
}
} catch(Throwable t) {
NavigatorPlugin.logError(0, t.getMessage(), t);
}
return provider;
}
private void initialize(String id, CommonActionProvider anActionProvider) {
if (anActionProvider != null && anActionProvider != SkeletonActionProvider.INSTANCE) {
ICommonActionExtensionSite configuration = new CommonActionExtensionSite(id, commonViewerSite, contentService, structuredViewer);
anActionProvider.init(configuration);
anActionProvider.restoreState(memento);
anActionProvider.setContext(new ActionContext(StructuredSelection.EMPTY));
if (commonViewerSite instanceof ICommonViewerWorkbenchSite) {
anActionProvider.fillActionBars(((ICommonViewerWorkbenchSite) commonViewerSite).getActionBars());
}
}
}
}