blob: a361b1e69a4c6fac3e95c702255ca490c30243ea [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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
* Dan Rubel (dan_rubel@instantiations.com) - accessor to get menu id
*******************************************************************************/
package org.eclipse.ui.internal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionDelta;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IRegistryChangeEvent;
import org.eclipse.core.runtime.IRegistryChangeListener;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener2;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.SubMenuManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
/**
* This class extends a single popup menu
*/
public class PopupMenuExtender implements IMenuListener2,
IRegistryChangeListener {
/**
* The bit in <code>bitSet</code> that stores whether the static actions
* have been read from the registry.
*/
private static final int STATIC_ACTION_READ = 1;
/**
* The bit in <code>bitSet</code> that stores whether the editor input
* should be included for the sake of object contributions.
*/
private static final int INCLUDE_EDITOR_INPUT = 1 << 1;
private final MenuManager menu;
private SubMenuManager menuWrapper;
private final ISelectionProvider selProvider;
private final IWorkbenchPart part;
private Map staticActionBuilders = null;
/**
* The boolean properties maintained by this extender. A bit set is used to
* save memory.
*/
private int bitSet = 0;
/**
* Construct a new menu extender.
*
* @param id
* the menu id
* @param menu
* the menu to extend
* @param prov
* the selection provider
* @param part
* the part to extend
*/
public PopupMenuExtender(String id, MenuManager menu,
ISelectionProvider prov, IWorkbenchPart part) {
this(id, menu, prov, part, true);
}
/**
* Construct a new menu extender.
*
* @param id
* the menu id
* @param menu
* the menu to extend
* @param prov
* the selection provider
* @param part
* the part to extend
* @param includeEditorInput
* Whether the editor input should be included when adding object
* contributions to this context menu.
*/
public PopupMenuExtender(final String id, final MenuManager menu,
final ISelectionProvider prov, final IWorkbenchPart part,
final boolean includeEditorInput) {
super();
this.menu = menu;
this.selProvider = prov;
this.part = part;
if (includeEditorInput) {
bitSet |= INCLUDE_EDITOR_INPUT;
}
menu.addMenuListener(this);
if (!menu.getRemoveAllWhenShown()) {
menuWrapper = new SubMenuManager(menu);
menuWrapper.setVisible(true);
}
readStaticActionsFor(id);
Platform.getExtensionRegistry().addRegistryChangeListener(this);
}
// getMenuId() added by Dan Rubel (dan_rubel@instantiations.com)
/**
* Return the menu identifiers for this extender.
*
* @return The set of all identifiers that represent this extender.
*/
public Set getMenuIds() {
if (staticActionBuilders == null) {
return Collections.EMPTY_SET;
}
return staticActionBuilders.keySet();
}
/**
* <p>
* Adds another menu identifier to this extender. An extender can represent
* many menu identifiers. These identifiers should represent the same menu
* manager, selection provider and part. Duplicate identifiers are
* automatically ignored.
* </p>
* <p>
* For example, it is necessary to filter out duplicate identifiers for
* <code>CompilationUnitEditor</code> instances, as these define both
* <code>"#CompilationUnitEditorContext"</code> and
* <code>"org.eclipse.jdt.ui.CompilationUnitEditor.EditorContext"</code>
* as menu identifier for the same pop-up menu. We don't want to contribute
* duplicate items in this case.
* </p>
*
* @param menuId
* The menu identifier to add to this extender; should not be
* <code>null</code>.
*/
public final void addMenuId(final String menuId) {
bitSet &= ~STATIC_ACTION_READ;
readStaticActionsFor(menuId);
}
/**
* Determines whether this extender would be the same as another extender
* created with the given values. Two extenders are equivalent if they have
* the same menu manager, selection provider and part (i.e., if the menu
* they represent is about to show, they would populate it with duplicate
* values).
*
* @param menuManager
* The menu manager with which to compare; may be
* <code>null</code>.
* @param selectionProvider
* The selection provider with which to compare; may be
* <code>null</code>.
* @param part
* The part with which to compare; may be <code>null</code>.
* @return <code>true</code> if the menu manager, selection provider and
* part are all the same.
*/
public final boolean matches(final MenuManager menuManager,
final ISelectionProvider selectionProvider,
final IWorkbenchPart part) {
return (this.menu == menuManager)
&& (this.selProvider == selectionProvider)
&& (this.part == part);
}
/**
* Contributes items registered for the currently active editor.
*/
private void addEditorActions(IMenuManager mgr) {
ISelectionProvider activeEditor = new ISelectionProvider() {
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
*/
public void addSelectionChangedListener(
ISelectionChangedListener listener) {
throw new UnsupportedOperationException(
"This ISelectionProvider is static, and cannot be modified."); //$NON-NLS-1$
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
*/
public ISelection getSelection() {
if (part instanceof IEditorPart) {
final IEditorPart editorPart = (IEditorPart) part;
return new StructuredSelection(new Object[] { editorPart
.getEditorInput() });
}
return new StructuredSelection(new Object[0]);
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
*/
public void removeSelectionChangedListener(
ISelectionChangedListener listener) {
throw new UnsupportedOperationException(
"This ISelectionProvider is static, and cannot be modified."); //$NON-NLS-1$
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
*/
public void setSelection(ISelection selection) {
throw new UnsupportedOperationException(
"This ISelectionProvider is static, and cannot be modified."); //$NON-NLS-1$
}
};
if (ObjectActionContributorManager.getManager()
.contributeObjectActions(part, mgr, activeEditor)) {
mgr.add(new Separator());
}
}
/**
* Contributes items registered for the object type(s) in
* the current selection.
*/
private void addObjectActions(IMenuManager mgr) {
if (selProvider != null) {
if (ObjectActionContributorManager.getManager()
.contributeObjectActions(part, mgr, selProvider)) {
mgr.add(new Separator());
}
}
}
/**
* Disposes all of the static actions.
*/
private final void clearStaticActions() {
bitSet &= ~STATIC_ACTION_READ;
if (staticActionBuilders != null) {
final Iterator staticActionBuilderItr = staticActionBuilders
.values().iterator();
while (staticActionBuilderItr.hasNext()) {
final Object staticActionBuilder = staticActionBuilderItr
.next();
if (staticActionBuilder instanceof ViewerActionBuilder) {
((ViewerActionBuilder) staticActionBuilder).dispose();
}
}
}
}
/**
* Adds static items to the context menu.
*/
private void addStaticActions(IMenuManager mgr) {
if (staticActionBuilders != null) {
final Iterator staticActionBuilderItr = staticActionBuilders
.values().iterator();
while (staticActionBuilderItr.hasNext()) {
final ViewerActionBuilder staticActionBuilder = (ViewerActionBuilder) staticActionBuilderItr
.next();
staticActionBuilder.contribute(mgr, null, true);
}
}
}
/**
* Notifies the listener that the menu is about to be shown.
*/
public void menuAboutToShow(IMenuManager mgr) {
// Add this menu as a visible menu.
final IWorkbenchPartSite site = part.getSite();
if (site != null) {
final IWorkbench workbench = site.getWorkbenchWindow().getWorkbench();
if (workbench instanceof Workbench) {
final Workbench realWorkbench = (Workbench) workbench;
realWorkbench.addShowingMenus(getMenuIds());
}
}
readStaticActions();
testForAdditions();
if (menuWrapper != null) {
mgr = menuWrapper;
menuWrapper.removeAll();
}
if ((bitSet & INCLUDE_EDITOR_INPUT) != 0) {
addEditorActions(mgr);
}
addObjectActions(mgr);
addStaticActions(mgr);
}
/**
* Notifies the listener that the menu is about to be hidden.
*/
public final void menuAboutToHide(final IMenuManager mgr) {
// Remove this menu as a visible menu.
final IWorkbenchPartSite site = part.getSite();
if (site != null) {
final IWorkbench workbench = site.getWorkbenchWindow().getWorkbench();
if (workbench instanceof Workbench) {
final Workbench realWorkbench = (Workbench) workbench;
realWorkbench.removeShowingMenus(getMenuIds());
}
}
}
/**
* Read all of the static items for the content menu.
*/
private final void readStaticActions() {
if (staticActionBuilders != null) {
final Iterator menuIdItr = staticActionBuilders.keySet().iterator();
while (menuIdItr.hasNext()) {
final String menuId = (String) menuIdItr.next();
readStaticActionsFor(menuId);
}
}
}
/**
* Read static items for a particular menu id, into the context menu.
*/
private void readStaticActionsFor(final String menuId) {
if ((bitSet & STATIC_ACTION_READ) != 0) {
return;
}
bitSet |= STATIC_ACTION_READ;
// If no menu id provided, then there is no contributions
// to add. Fix for bug #33140.
if ((menuId == null) || (menuId.length() < 1)) {
return;
}
if (staticActionBuilders == null) {
staticActionBuilders = new HashMap();
}
Object object = staticActionBuilders.get(menuId);
if (!(object instanceof ViewerActionBuilder)) {
object = new ViewerActionBuilder();
staticActionBuilders.put(menuId, object);
}
final ViewerActionBuilder staticActionBuilder = (ViewerActionBuilder) object;
staticActionBuilder.readViewerContributions(menuId, selProvider, part);
}
/**
* Checks for the existance of an MB_ADDITIONS group.
*/
private void testForAdditions() {
IContributionItem item = menu
.find(IWorkbenchActionConstants.MB_ADDITIONS);
if (item == null) {
WorkbenchPlugin
.log("Context menu missing standard group 'org.eclipse.ui.IWorkbenchActionConstants.MB_ADDITIONS'. (menu ids = " //$NON-NLS-1$
+ getMenuIds().toString() + ") part id = " //$NON-NLS-1$
+ (part == null ? "???" : part.getSite().getId()) //$NON-NLS-1$
+ ")"); //$NON-NLS-1$
}
}
/**
* Dispose of the menu extender. Should only be called when the part
* is disposed.
*/
public void dispose() {
clearStaticActions();
Platform.getExtensionRegistry().removeRegistryChangeListener(this);
menu.removeMenuListener(this);
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent)
*/
public void registryChanged(final IRegistryChangeEvent event) {
Display display = Display.getDefault();
if (part != null) {
display = part.getSite().getPage().getWorkbenchWindow().getWorkbench().getDisplay();
}
//check the delta to see if there are any viewer contribution changes. if so, null our builder to cause reparsing on the next menu show
IExtensionDelta [] deltas = event.getExtensionDeltas();
for (int i = 0; i < deltas.length; i++) {
IExtensionDelta delta = deltas[i];
IExtensionPoint extensionPoint = delta.getExtensionPoint();
if (extensionPoint.getNamespace().equals(
WorkbenchPlugin.PI_WORKBENCH)
&& extensionPoint.getSimpleIdentifier().equals(
IWorkbenchRegistryConstants.PL_POPUP_MENU)) {
boolean clearPopups = false;
IConfigurationElement [] elements = delta.getExtension().getConfigurationElements();
for (int j = 0; j < elements.length; j++) {
IConfigurationElement element = elements[j];
if (element.getName().equals(IWorkbenchRegistryConstants.TAG_VIEWER_CONTRIBUTION)) {
clearPopups = true;
break;
}
}
if (clearPopups) {
display.syncExec(new Runnable() {
public void run() {
clearStaticActions();
}
});
}
}
}
}
}