001: /*******************************************************************************
002: * Copyright (c) 2003, 2006 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.navigator;
011:
012: import java.util.HashMap;
013: import java.util.HashSet;
014: import java.util.Iterator;
015: import java.util.Map;
016: import java.util.Set;
017:
018: import org.eclipse.core.runtime.Assert;
019: import org.eclipse.core.runtime.ISafeRunnable;
020: import org.eclipse.core.runtime.SafeRunner;
021: import org.eclipse.jface.action.GroupMarker;
022: import org.eclipse.jface.action.IContributionItem;
023: import org.eclipse.jface.action.IMenuManager;
024: import org.eclipse.jface.action.MenuManager;
025: import org.eclipse.jface.action.Separator;
026: import org.eclipse.jface.viewers.ISelectionProvider;
027: import org.eclipse.jface.viewers.StructuredSelection;
028: import org.eclipse.jface.viewers.StructuredViewer;
029: import org.eclipse.ui.IActionBars;
030: import org.eclipse.ui.IMemento;
031: import org.eclipse.ui.actions.ActionContext;
032: import org.eclipse.ui.actions.ActionGroup;
033: import org.eclipse.ui.internal.navigator.NavigatorContentService;
034: import org.eclipse.ui.internal.navigator.NavigatorPlugin;
035: import org.eclipse.ui.internal.navigator.actions.CommonActionDescriptorManager;
036: import org.eclipse.ui.internal.navigator.actions.CommonActionProviderDescriptor;
037: import org.eclipse.ui.internal.navigator.extensions.CommonActionExtensionSite;
038: import org.eclipse.ui.internal.navigator.extensions.SkeletonActionProvider;
039:
040: /**
041: * <p>
042: * Provides context menu items and {@link IActionBars} contributions for a particular abstract
043: * viewer. The interface matches that of {@link ActionGroup} and may be used in the same manner.
044: * Clients must call
045: * {@link NavigatorActionService#prepareMenuForPlatformContributions(MenuManager, ISelectionProvider, boolean)}
046: * when using this class to allow object or viewer contributions. The
047: * <b>org.eclipse.ui.navigator.viewer/viewer/popupMenu</b> element may override whether platform
048: * contributions are allowed to the menu with its <i>allowsPlatformContributions</i> attribute.
049: * "Platform Contributions" are menu items that are added through the <b>org.eclipse.ui.popupMenus</b>
050: * extension point.
051: * </p>
052: * <p>
053: * A {@link CommonActionProvider} has opportunities to contribute to the context menu and
054: * {@link org.eclipse.ui.IActionBars} whenever the selection in the viewer changes. Action Providers
055: * are selected based on the enablement expressions of their associated content extension or their
056: * own enablement expression if it is declared as a top-level <actionProvider /> element (of
057: * the <b>org.eclipse.ui.navigator.navigatorContent</b> extension point). See the schema
058: * documentation of <b>org.eclipse.ui.navigator.navigatorContent</b> for more information on how to
059: * specify an Action Provider.
060: * </p>
061: * <p>
062: * Clients that reuse this service outside of an instance of {@link CommonNavigator} must be sure
063: * that {{@link #fillActionBars(IActionBars)} is called whenever the selection changes. The
064: * retargetable actions for each selection could change, based on who contributed the items.
065: *
066: * @since 3.2
067: *
068: */
069: public final class NavigatorActionService extends ActionGroup implements
070: IMementoAware {
071:
072: private static final IContributionItem[] DEFAULT_GROUPS = new IContributionItem[] {
073: new Separator(ICommonMenuConstants.GROUP_NEW),
074: new GroupMarker(ICommonMenuConstants.GROUP_GOTO),
075: new GroupMarker(ICommonMenuConstants.GROUP_OPEN),
076: new GroupMarker(ICommonMenuConstants.GROUP_OPEN_WITH),
077: new Separator(ICommonMenuConstants.GROUP_EDIT),
078: new GroupMarker(ICommonMenuConstants.GROUP_SHOW),
079: new GroupMarker(ICommonMenuConstants.GROUP_REORGANIZE),
080: new GroupMarker(ICommonMenuConstants.GROUP_PORT),
081: new Separator(ICommonMenuConstants.GROUP_GENERATE),
082: new Separator(ICommonMenuConstants.GROUP_SEARCH),
083: new Separator(ICommonMenuConstants.GROUP_BUILD),
084: new Separator(ICommonMenuConstants.GROUP_ADDITIONS),
085: new Separator(ICommonMenuConstants.GROUP_PROPERTIES) };
086:
087: private final ICommonViewerSite commonViewerSite;
088:
089: private final StructuredViewer structuredViewer;
090:
091: private final NavigatorContentService contentService;
092:
093: private final INavigatorViewerDescriptor viewerDescriptor;
094:
095: private final Set actionProviderDescriptors = new HashSet();
096:
097: /*
098: * Map of CommonActionProviderDescriptors to CommonActionProviders
099: */
100: private final Map actionProviderInstances = new HashMap();
101:
102: private IMemento memento;
103:
104: private IContributionItem[] menuGroups;
105:
106: private boolean disposed = false;
107:
108: /**
109: * @param aCommonViewerSite
110: * A site that provides information about the context for extensions.
111: * @param aStructuredViewer
112: * The associated StructuredViewer. Used to initialize extensions. <b>May NOT be
113: * null.</b>
114: * @param aContentService
115: * The associated INavigatorContentService (for extensions that coordinate behavior
116: * with content extensions -- either nested or top-level action providers). <b>May
117: * NOT be null.</b>
118: */
119: public NavigatorActionService(ICommonViewerSite aCommonViewerSite,
120: StructuredViewer aStructuredViewer,
121: INavigatorContentService aContentService) {
122: super ();
123: Assert.isNotNull(aCommonViewerSite);
124: Assert.isNotNull(aStructuredViewer);
125: Assert.isNotNull(aContentService);
126:
127: commonViewerSite = aCommonViewerSite;
128: contentService = (NavigatorContentService) aContentService;
129: structuredViewer = aStructuredViewer;
130: viewerDescriptor = contentService.getViewerDescriptor();
131:
132: }
133:
134: /**
135: * Prepares the menu for object contributions, if the option is set in the extension. The option
136: * is controlled by the &lgt;popupMenu /> element's 'allowPlatformContributions' attribute.
137: * Clients may choose to ignore this setting by supplying a value of <b>true</b> for the
138: * <code>force</code> attribute.
139: *
140: * @param menu
141: * The context menu of the IViewPart
142: * @param aSelectionProvider
143: * The selection provider that will supplement actions with a valid, current
144: * selection.
145: * @param force
146: * A value of 'true' forces the menu to be registered for object/view contributions.
147: * Otherwise, the option from the extension point will be respected. See
148: * <b>org.eclipse.ui.navigator.viewer/viewer</b> for more information.
149: */
150: public void prepareMenuForPlatformContributions(MenuManager menu,
151: ISelectionProvider aSelectionProvider, boolean force) {
152: Assert.isTrue(!disposed);
153:
154: if (commonViewerSite instanceof ICommonViewerWorkbenchSite) {
155: /*
156: * Hooks into the Eclipse framework for Object contributions, and View contributions.
157: */
158: if (force
159: || viewerDescriptor
160: .allowsPlatformContributionsToContextMenu()) {
161: ((ICommonViewerWorkbenchSite) commonViewerSite)
162: .registerContextMenu(
163: contentService.getViewerDescriptor()
164: .getPopupMenuId(), menu,
165: aSelectionProvider);
166: }
167: }
168: }
169:
170: /**
171: * Requests that the service invoke extensions to fill the given menu with Action Providers that
172: * are interested in elements from the given selection.
173: *
174: * <p>
175: * Object contributions (see <b>org.eclipes.ui.popupMenus</b>) may also respected by this
176: * method if <code>toRespectObjectContributions</code> is true.
177: * </p>
178: *
179: * @param aMenu
180: * The menu being presented to the user.
181: * @see ActionGroup#fillContextMenu(IMenuManager)
182: */
183: public void fillContextMenu(IMenuManager aMenu) {
184: Assert.isTrue(!disposed);
185:
186: if (menuGroups == null) {
187: createMenuGroups();
188: }
189:
190: for (int i = 0; i < menuGroups.length; i++) {
191: aMenu.add(menuGroups[i]);
192: }
193:
194: addCommonActionProviderMenu(aMenu);
195:
196: }
197:
198: private void createMenuGroups() {
199: MenuInsertionPoint[] customPoints = viewerDescriptor
200: .getCustomInsertionPoints();
201:
202: if (customPoints == null) {
203: menuGroups = DEFAULT_GROUPS;
204: } else {
205: menuGroups = new IContributionItem[customPoints.length];
206: for (int i = 0; i < customPoints.length; i++) {
207: if (customPoints[i].isSeparator()) {
208: menuGroups[i] = new Separator(customPoints[i]
209: .getName());
210: } else {
211: menuGroups[i] = new GroupMarker(customPoints[i]
212: .getName());
213: }
214: }
215: }
216: }
217:
218: /**
219: * @param aMenu
220: */
221: private void addCommonActionProviderMenu(IMenuManager aMenu) {
222:
223: CommonActionProviderDescriptor[] providerDescriptors = CommonActionDescriptorManager
224: .getInstance().findRelevantActionDescriptors(
225: contentService, getContext());
226: if (providerDescriptors.length > 0) {
227: CommonActionProvider provider = null;
228: for (int i = 0; i < providerDescriptors.length; i++) {
229: try {
230: provider = getActionProviderInstance(providerDescriptors[i]);
231: provider.setContext(getContext());
232: provider.fillContextMenu(aMenu);
233: } catch (Throwable t) {
234: NavigatorPlugin.logError(0, t.getMessage(), t);
235: }
236: }
237: }
238: }
239:
240: /**
241: * Request that the service invoke extensions to fill the given IActionBars with retargetable
242: * actions or view menu contributions from Action Providers that are interested in the given
243: * selection.
244: *
245: * @param theActionBars
246: * The action bars in use by the current view site.
247: * @see ActionGroup#fillActionBars(IActionBars)
248: */
249: public void fillActionBars(IActionBars theActionBars) {
250: Assert.isTrue(!disposed);
251:
252: theActionBars.clearGlobalActionHandlers();
253: ActionContext context = getContext();
254: if (context == null) {
255: context = new ActionContext(StructuredSelection.EMPTY);
256: }
257:
258: CommonActionProviderDescriptor[] providerDescriptors = CommonActionDescriptorManager
259: .getInstance().findRelevantActionDescriptors(
260: contentService, context);
261: if (providerDescriptors.length > 0) {
262: CommonActionProvider provider = null;
263: for (int i = 0; i < providerDescriptors.length; i++) {
264: try {
265: provider = getActionProviderInstance(providerDescriptors[i]);
266: if (provider != null) {
267: provider.setContext(context);
268: provider.fillActionBars(theActionBars);
269: provider.updateActionBars();
270: }
271:
272: } catch (RuntimeException e) {
273: NavigatorPlugin.logError(0, e.getMessage(), e);
274: }
275: }
276: }
277: theActionBars.updateActionBars();
278: theActionBars.getMenuManager().update();
279: }
280:
281: /**
282: * Dispose of any state or resources held by the service.
283: *
284: * @see ActionGroup#dispose()
285: */
286: public void dispose() {
287: synchronized (actionProviderInstances) {
288: for (Iterator iter = actionProviderInstances.values()
289: .iterator(); iter.hasNext();) {
290: CommonActionProvider element = (CommonActionProvider) iter
291: .next();
292: element.dispose();
293: }
294: actionProviderInstances.clear();
295: }
296: actionProviderDescriptors.clear();
297: disposed = false;
298: }
299:
300: /**
301: * Use the given memento to restore the state of each Action Provider as it is initialized.
302: *
303: * @param aMemento
304: * The memento retrieved from the dialog settings
305: */
306: public void restoreState(IMemento aMemento) {
307: Assert.isTrue(!disposed);
308: memento = aMemento;
309:
310: synchronized (actionProviderInstances) {
311: for (Iterator actionProviderIterator = actionProviderInstances
312: .values().iterator(); actionProviderIterator
313: .hasNext();) {
314: final CommonActionProvider provider = (CommonActionProvider) actionProviderIterator
315: .next();
316: ISafeRunnable runnable = new ISafeRunnable() {
317: public void run() throws Exception {
318: provider.restoreState(memento);
319: }
320:
321: public void handleException(Throwable exception) {
322: NavigatorPlugin
323: .logError(
324: 0,
325: "Could not restore state for action provider " + provider.getClass(), exception); //$NON-NLS-1$
326:
327: }
328: };
329: SafeRunner.run(runnable);
330:
331: }
332: }
333: }
334:
335: /**
336: * Request that Action Providers save any state that they find interesting.
337: *
338: * @param aMemento
339: * The memento retrieved from the dialog settings
340: */
341: public void saveState(IMemento aMemento) {
342: Assert.isTrue(!disposed);
343:
344: memento = aMemento;
345: CommonActionProvider provider = null;
346: synchronized (actionProviderInstances) {
347: for (Iterator actionProviderIterator = actionProviderInstances
348: .values().iterator(); actionProviderIterator
349: .hasNext();) {
350: provider = (CommonActionProvider) actionProviderIterator
351: .next();
352: provider.saveState(memento);
353: }
354: }
355: }
356:
357: private CommonActionProvider getActionProviderInstance(
358: CommonActionProviderDescriptor aProviderDescriptor) {
359: CommonActionProvider provider = null;
360: try {
361: provider = (CommonActionProvider) actionProviderInstances
362: .get(aProviderDescriptor);
363: if (provider != null) {
364: return provider;
365: }
366: synchronized (actionProviderInstances) {
367: provider = (CommonActionProvider) actionProviderInstances
368: .get(aProviderDescriptor);
369: if (provider == null) {
370: provider = aProviderDescriptor
371: .createActionProvider();
372: if (provider != null) {
373: actionProviderInstances.put(
374: aProviderDescriptor, provider);
375: initialize(aProviderDescriptor.getId(),
376: provider);
377: } else {
378: actionProviderInstances
379: .put(
380: aProviderDescriptor,
381: (provider = SkeletonActionProvider.INSTANCE));
382: }
383: }
384: }
385: } catch (Throwable t) {
386: NavigatorPlugin.logError(0, t.getMessage(), t);
387: }
388: return provider;
389: }
390:
391: private void initialize(String id,
392: CommonActionProvider anActionProvider) {
393: if (anActionProvider != null
394: && anActionProvider != SkeletonActionProvider.INSTANCE) {
395: ICommonActionExtensionSite configuration = new CommonActionExtensionSite(
396: id, commonViewerSite, contentService,
397: structuredViewer);
398: anActionProvider.init(configuration);
399: anActionProvider.restoreState(memento);
400: anActionProvider.setContext(new ActionContext(
401: StructuredSelection.EMPTY));
402: if (commonViewerSite instanceof ICommonViewerWorkbenchSite) {
403: anActionProvider
404: .fillActionBars(((ICommonViewerWorkbenchSite) commonViewerSite)
405: .getActionBars());
406: }
407: }
408: }
409: }
|