org.eclipse.ui.SubActionBars Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Lars Vogel - Bug 440810
*******************************************************************************/
package org.eclipse.ui;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.commands.common.EventManager;
import org.eclipse.core.expressions.EvaluationResult;
import org.eclipse.core.expressions.Expression;
import org.eclipse.core.expressions.ExpressionInfo;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.SubMenuManager;
import org.eclipse.jface.action.SubStatusLineManager;
import org.eclipse.jface.action.SubToolBarManager;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.ui.handlers.IHandlerActivation;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.internal.EditorActionBars;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.actions.CommandAction;
import org.eclipse.ui.internal.handlers.CommandLegacyActionWrapper;
import org.eclipse.ui.internal.handlers.IActionCommandMappingService;
import org.eclipse.ui.internal.services.SourcePriorityNameMapping;
import org.eclipse.ui.services.IServiceLocator;
/**
* Generic implementation of the IActionBars
interface.
*/
public class SubActionBars extends EventManager implements IActionBars {
/**
* The expression to use when contributing handlers through
* {@link #setGlobalActionHandler(String, IAction)}}. This ensures that handlers
* contributed through {@link SubActionBars} are given priority over handlers
* contributed to the {@link IHandlerService}.
*/
private static final Expression EXPRESSION = new Expression() {
@Override
public final EvaluationResult evaluate(final IEvaluationContext context) {
return EvaluationResult.TRUE;
}
@Override
public final void collectExpressionInfo(final ExpressionInfo info) {
info.addVariableNameAccess(SourcePriorityNameMapping.LEGACY_LEGACY_NAME);
}
};
/**
* Property constant for changes to action handlers.
*/
public static final String P_ACTION_HANDLERS = "org.eclipse.ui.internal.actionHandlers"; //$NON-NLS-1$
private Map actionHandlers;
private boolean actionHandlersChanged;
/**
* A map of handler activations ({@link IHandlerActivation} indexed by action id
* ({@link String}) indexed by service locator ({@link IServiceLocator}). This
* value is null
if there are no activations.
*/
private Map> activationsByActionIdByServiceLocator;
private boolean active = false;
private SubMenuManager menuMgr;
private IActionBars parent;
/**
* A service locator appropriate for this action bar. This value is never
* null
. It must be capable of providing a {@link IHandlerService}.
*/
private IServiceLocator serviceLocator;
private SubStatusLineManager statusLineMgr;
private SubToolBarManager toolBarMgr;
private Map actionIdByCommandId = new HashMap<>();
/**
* Construct a new SubActionBars
object. The service locator will
* simply be the service locator of the parent.
*
* @param parent The parent of this action bar; must not be null
.
*/
public SubActionBars(final IActionBars parent) {
this(parent, null);
}
/**
* Constructs a new instance of SubActionBars
.
*
* @param parent The parent of this action bar; must not be
* null
.
* @param serviceLocator The service locator for this action bar; should not be
* null
.
*
* @since 3.2
*/
public SubActionBars(final IActionBars parent, final IServiceLocator serviceLocator) {
if (parent == null) {
throw new NullPointerException("The parent cannot be null"); //$NON-NLS-1$
}
this.parent = parent;
this.serviceLocator = serviceLocator;
}
/**
* Activate the contributions.
*/
public void activate() {
activate(true);
}
/**
* Activate the contributions.
*
* Workaround for toolbar layout flashing when editors contribute large amounts
* of items. In this case we want to force the items to be visible/hidden only
* when required, otherwise just change the enablement state.
*
*
* @param forceVisibility (is not used)
*/
public void activate(boolean forceVisibility) {
setActive(true);
}
/**
* Adds a property change listener. Has no effect if an identical listener is
* already registered.
*
* @param listener a property change listener
*/
public void addPropertyChangeListener(IPropertyChangeListener listener) {
addListenerObject(listener);
}
/**
* Sets the active flag. Clients should not call this method directly unless
* they are overriding the setActive() method.
*/
protected final void basicSetActive(boolean active) {
this.active = active;
}
/**
* Clear the global action handlers.
*/
@Override
public void clearGlobalActionHandlers() {
if (actionHandlers != null) {
actionHandlers.clear();
actionHandlersChanged = true;
}
if (activationsByActionIdByServiceLocator != null) {
// Clean up the activations.
final Iterator>> activationItr = activationsByActionIdByServiceLocator
.entrySet().iterator();
while (activationItr.hasNext()) {
final Map.Entry> value = activationItr.next();
final IServiceLocator locator = value.getKey();
final IHandlerService service = locator.getService(IHandlerService.class);
final Map activationsByActionId = value.getValue();
Collection activations = activationsByActionId.values();
if (service != null) {
service.deactivateHandlers(activations);
}
for (Object activation : activations) {
((IHandlerActivation) activation).getHandler().dispose();
}
}
activationsByActionIdByServiceLocator.clear();
}
}
/**
* Returns a new sub menu manager.
*
* @param parent the parent menu manager
* @return the menu manager
*/
protected SubMenuManager createSubMenuManager(IMenuManager parent) {
return new SubMenuManager(parent);
}
/**
* Returns a new sub toolbar manager.
*
* @param parent the parent toolbar manager
* @return the tool bar manager
*/
protected SubToolBarManager createSubToolBarManager(IToolBarManager parent) {
return new SubToolBarManager(parent);
}
/**
* Deactivate the contributions.
*/
public void deactivate() {
deactivate(true);
}
/**
* Deactivate the contributions.
*
* Workaround for menubar/toolbar layout flashing when editors have many
* contributions. In this case we want to force the contributions to be
* visible/hidden only when required, otherwise just change the enablement
* state.
*
*
* @param forceHide (not used)
*/
public void deactivate(boolean forceHide) {
setActive(false);
}
/**
* Dispose the contributions.
*/
public void dispose() {
clearGlobalActionHandlers();
if (menuMgr != null) {
menuMgr.dispose();
menuMgr.disposeManager();
}
if (statusLineMgr != null) {
statusLineMgr.disposeManager();
}
if (toolBarMgr != null) {
toolBarMgr.disposeManager();
}
updateActionBars(); // Notify listeners and parent
clearListeners();
}
/**
* Notifies any property change listeners if the global action handlers have
* changed
*/
protected void fireActionHandlersChanged() {
if (actionHandlersChanged) {
// Doesn't actually pass the old and new values
firePropertyChange(new PropertyChangeEvent(this, P_ACTION_HANDLERS, null, null));
actionHandlersChanged = false;
}
}
/**
* Notifies any property change listeners that a property has changed. Only
* listeners registered at the time this method is called are notified.
*
* @param event the property change event
*
* @see IPropertyChangeListener#propertyChange
*/
protected void firePropertyChange(PropertyChangeEvent event) {
Object[] listeners = getListeners();
for (Object listener : listeners) {
((IPropertyChangeListener) listener).propertyChange(event);
}
}
/**
* Return whether the manager is currently active or not.
*/
protected final boolean getActive() {
return active;
}
/**
* Get the handler for a window action.
*
* @param actionID an action ID declared in the registry
* @return an action handler which implements the action ID, or
* null
if none is registered.
*/
@Override
public IAction getGlobalActionHandler(String actionID) {
if (actionHandlers == null) {
return null;
}
return actionHandlers.get(actionID);
}
/**
* Returns the complete list of active global action handlers. If there are no
* global action handlers registered return null.
*
* @return a map with global action handlers
*/
public Map getGlobalActionHandlers() {
return actionHandlers;
}
/**
* Returns the abstract menu manager. If items are added or removed from the
* manager be sure to call updateActionBars
.
*
* @return the menu manager
*/
@Override
public IMenuManager getMenuManager() {
if (menuMgr == null) {
menuMgr = createSubMenuManager(parent.getMenuManager());
menuMgr.setVisible(active);
}
return menuMgr;
}
/**
* Return the parent action bar manager.
*/
protected final IActionBars getParent() {
return parent;
}
/**
* Answer the service locator for this action bar.
*
* @return an IServiceLocater
or the parents if the receiver does
* not have one
*
* @since 3.2
*/
@Override
public final IServiceLocator getServiceLocator() {
if (serviceLocator != null) {
return serviceLocator;
}
return parent.getServiceLocator();
}
/**
* Returns the status line manager. If items are added or removed from the
* manager be sure to call updateActionBars
.
*
* @return the status line manager
*/
@Override
public IStatusLineManager getStatusLineManager() {
if (statusLineMgr == null) {
statusLineMgr = new SubStatusLineManager(parent.getStatusLineManager());
statusLineMgr.setVisible(active);
}
return statusLineMgr;
}
/**
* Returns the tool bar manager. If items are added or removed from the manager
* be sure to call updateActionBars
.
*
* @return the tool bar manager
*/
@Override
public IToolBarManager getToolBarManager() {
if (toolBarMgr == null) {
toolBarMgr = createSubToolBarManager(parent.getToolBarManager());
toolBarMgr.setVisible(active);
}
return toolBarMgr;
}
/**
* Return whether the sub menu manager has been created yet.
*/
protected final boolean isSubMenuManagerCreated() {
return menuMgr != null;
}
/**
* Return whether the sub status line manager has been created yet.
*/
protected final boolean isSubStatusLineManagerCreated() {
return statusLineMgr != null;
}
/**
* Return whether the sub toolbar manager has been created yet.
*/
protected final boolean isSubToolBarManagerCreated() {
return toolBarMgr != null;
}
/**
* Notification that the target part for the action bars has changed. This
* implementation does nothing.
*
* @param part the part to be notified.
*/
public void partChanged(IWorkbenchPart part) {
}
/**
* Removes the given property change listener. Has no effect if an identical
* listener is not registered.
*
* @param listener a property change listener
*/
public void removePropertyChangeListener(IPropertyChangeListener listener) {
removeListenerObject(listener);
}
/**
* Activate / deactivate the contributions.
*/
protected void setActive(boolean set) {
active = set;
if (menuMgr != null) {
menuMgr.setVisible(set);
}
if (statusLineMgr != null) {
statusLineMgr.setVisible(set);
}
if (toolBarMgr != null) {
toolBarMgr.setVisible(set);
}
}
/**
* Add a handler for a window action.
*
* @param actionID an action ID declared in the registry
* @param handler an action which implements the action ID. null
* may be passed to deregister a handler.
*/
@Override
public void setGlobalActionHandler(String actionID, IAction handler) {
if (actionID == null) {
/*
* Bug 124061. It used to be invalid to pass null as an action id, but some
* people still did it. Handle this case by trapping the exception and logging
* it.
*/
WorkbenchPlugin.log("Cannot set the global action handler for a null action id"); //$NON-NLS-1$
return;
}
if (handler instanceof CommandLegacyActionWrapper) {
// this is a registration of a fake action for an already
// registered handler
WorkbenchPlugin.log("Cannot feed a CommandLegacyActionWrapper back into the system"); //$NON-NLS-1$
return;
}
if (handler instanceof CommandAction) {
// we unfortunately had to allow these out into the wild, but they
// still must not feed back into the system
return;
}
if (handler != null) {
// Update the action handlers.
if (actionHandlers == null) {
actionHandlers = new HashMap<>(11);
}
actionHandlers.put(actionID, handler);
// Add a mapping from this action id to the command id.
if (serviceLocator != null) {
String commandId = null;
final IActionCommandMappingService mappingService = serviceLocator
.getService(IActionCommandMappingService.class);
if (mappingService != null) {
commandId = mappingService.getCommandId(actionID);
}
if (commandId == null) {
commandId = handler.getActionDefinitionId();
}
// Update the handler activations.
final IHandlerService service = serviceLocator.getService(IHandlerService.class);
Map activationsByActionId = null;
if (activationsByActionIdByServiceLocator == null) {
activationsByActionIdByServiceLocator = new WeakHashMap<>();
activationsByActionId = new HashMap<>();
activationsByActionIdByServiceLocator.put(serviceLocator, activationsByActionId);
} else {
activationsByActionId = activationsByActionIdByServiceLocator.get(serviceLocator);
if (activationsByActionId == null) {
activationsByActionId = new HashMap<>();
activationsByActionIdByServiceLocator.put(serviceLocator, activationsByActionId);
} else if (activationsByActionId.containsKey(actionID)) {
final Object value = activationsByActionId.remove(actionID);
if (value instanceof IHandlerActivation) {
final IHandlerActivation activation = (IHandlerActivation) value;
actionIdByCommandId.remove(activation.getCommandId());
if (service != null) {
service.deactivateHandler(activation);
}
activation.getHandler().dispose();
}
} else if (commandId != null && actionIdByCommandId.containsKey(commandId)) {
final Object value = activationsByActionId.remove(actionIdByCommandId.remove(commandId));
if (value instanceof IHandlerActivation) {
final IHandlerActivation activation = (IHandlerActivation) value;
if (service != null) {
service.deactivateHandler(activation);
}
activation.getHandler().dispose();
}
}
}
if (commandId != null) {
actionIdByCommandId.put(commandId, actionID);
// Register this as a handler with the given definition id.
// the expression gives the setGlobalActionHandler() a
// priority.
final IHandler actionHandler = new ActionHandler(handler);
Expression handlerExpression = EXPRESSION;
// XXX add new API in next release to avoid down-casting (bug 137091)
if (this instanceof EditorActionBars) {
handlerExpression = ((EditorActionBars) this).getHandlerExpression();
}
if (service != null) {
final IHandlerActivation activation = service.activateHandler(commandId, actionHandler,
handlerExpression);
activationsByActionId.put(actionID, activation);
}
}
}
} else {
if (actionHandlers != null) {
actionHandlers.remove(actionID);
}
// Remove the handler activation.
if (serviceLocator != null) {
final IHandlerService service = serviceLocator.getService(IHandlerService.class);
if (activationsByActionIdByServiceLocator != null) {
final Map activationsByActionId = activationsByActionIdByServiceLocator
.get(serviceLocator);
if ((activationsByActionId != null) && (activationsByActionId.containsKey(actionID))) {
final Object value = activationsByActionId.remove(actionID);
if (value instanceof IHandlerActivation) {
final IHandlerActivation activation = (IHandlerActivation) value;
actionIdByCommandId.remove(activation.getCommandId());
service.deactivateHandler(activation);
activation.getHandler().dispose();
}
}
}
}
}
actionHandlersChanged = true;
}
/**
* Sets the service locator for this action bar.
*
* @param locator The new locator; must not be null
.
*
* @since 3.2
*/
protected final void setServiceLocator(final IServiceLocator locator) {
if (locator == null) {
throw new NullPointerException("The service locator cannot be null"); //$NON-NLS-1$
}
this.serviceLocator = locator;
}
/**
* Commits all UI changes. This should be called after additions or subtractions
* have been made to a menu, status line, or toolbar.
*/
@Override
public void updateActionBars() {
parent.updateActionBars();
fireActionHandlersChanged();
}
}