blob: 3a72fdd76975ddd731d655eaba7d609f563775ce [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 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.internal.handlers;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.commands.IHandlerListener;
import org.eclipse.core.commands.IObjectWithState;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.State;
import org.eclipse.core.expressions.EvaluationResult;
import org.eclipse.core.expressions.Expression;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.IActionDelegate;
import org.eclipse.ui.IActionDelegate2;
import org.eclipse.ui.IActionDelegateWithEvent;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.INullSelectionListener;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISources;
import org.eclipse.ui.IViewActionDelegate;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.internal.WorkbenchPlugin;
/**
* <p>
* This proxies an {@link IActionDelegate} so that it can impersonate an
* {@link IHandler}.
* </p>
* <p>
* This class is not intended for use outside of the
* <code>org.eclipse.ui.workbench</code> plug-in.
* </p>
*
* @since 3.2
*/
public final class ActionDelegateHandlerProxy implements ISelectionListener,
ISelectionChangedListener, INullSelectionListener, IHandler,
IObjectWithState {
/**
* The fake action that proxies all of the command-based services. This
* value is never <code>null</code>.
*/
private CommandLegacyActionWrapper action;
/**
* The identifier of the actions to create as a wrapper to the command
* architecture. This value may be <code>null</code>.
*/
private String actionId;
/**
* The command that will back the dummy actions exposed to this delegate.
* This value is never <code>null</code>.
*/
private ParameterizedCommand command;
/**
* This is the current selection, as seen by this proxy.
*/
private ISelection currentSelection;
/**
* The delegate, if it has been created yet.
*/
private IActionDelegate delegate;
/**
* The name of the configuration element attribute which contains the
* information necessary to instantiate the real handler.
*/
private String delegateAttributeName;
/**
* The configuration element from which the handler can be created. This
* value will exist until the element is converted into a real class -- at
* which point this value will be set to <code>null</code>.
*/
private IConfigurationElement element;
/**
* The <code>enabledWhen</code> expression for the handler. Only if this
* expression evaluates to <code>true</code> (or the value is
* <code>null</code>) should we consult the handler.
*/
private final Expression enabledWhenExpression;
/**
* A collection of objects listening to changes to this manager. This
* collection is <code>null</code> if there are no listeners.
*/
private transient ListenerList listenerList = null;
/**
* The image style to use when selecting the images to display for this
* delegate. This value may be <code>null</code>, if the default style
* should be used.
*/
private final String style;
/**
* The identifier of the view with which this delegate must be associated.
* This value is not <code>null</code> iff the delegate is an
* {@link IViewActionDelegate}.
*/
private final String viewId;
/**
* The workbench window in which this delegate is active. This value is
* never <code>null</code>.
*/
private final IWorkbenchWindow window;
/**
* Constructs a new instance of <code>ActionDelegateHandlerProxy</code>
* with all the information it needs to try to avoid loading until it is
* needed.
*
* @param element
* The configuration element from which the real class can be
* loaded at run-time; must not be <code>null</code>.
* @param delegateAttributeName
* The name of the attibute or element containing the action
* delegate; must not be <code>null</code>.
* @param actionId
* The identifier of the underlying action; may be
* <code>null</code>.
* @param command
* The command with which the action delegate will be associated;
* must not be <code>null</code>.
* @param window
* The workbench window in which this delegate will be active;
* must not be <code>null</code>.
* @param style
* The image style with which the icons are associated; may be
* <code>null</code>.
* @param enabledWhenExpression
* The name of the element containing the enabledWhen expression.
* This should be a child of the
* <code>configurationElement</code>. If this value is
* <code>null</code>, then there is no enablement expression
* (i.e., enablement will be delegated to the handler when
* possible).
* @param viewId
* The identifier of the view to which this proxy is bound; may
* be <code>null</code> if this proxy is not for an
* {@link IViewActionDelegate}.
*/
public ActionDelegateHandlerProxy(final IConfigurationElement element,
final String delegateAttributeName, final String actionId,
final ParameterizedCommand command, final IWorkbenchWindow window,
final String style, final Expression enabledWhenExpression,
final String viewId) {
if (element == null) {
throw new NullPointerException(
"The configuration element backing a handler proxy cannot be null"); //$NON-NLS-1$
}
if (delegateAttributeName == null) {
throw new NullPointerException(
"The attribute containing the action delegate must be known"); //$NON-NLS-1$
}
if (window == null) {
throw new NullPointerException(
"The workbench window for a delegate must not be null"); //$NON-NLS-1$
}
this.element = element;
this.enabledWhenExpression = enabledWhenExpression;
this.delegateAttributeName = delegateAttributeName;
this.window = window;
this.command = command;
this.actionId = actionId;
this.style = style;
this.viewId = viewId;
}
public final void addHandlerListener(final IHandlerListener handlerListener) {
if (listenerList == null) {
listenerList = new ListenerList(ListenerList.IDENTITY);
}
listenerList.add(handlerListener);
}
public void addState(String id, State state) {
// TODO Auto-generated method stub
}
public final void dispose() {
final IActionDelegate delegate = getDelegate();
if (delegate instanceof IWorkbenchWindowActionDelegate) {
final IWorkbenchWindowActionDelegate workbenchWindowDelegate = (IWorkbenchWindowActionDelegate) delegate;
workbenchWindowDelegate.dispose();
} else if (delegate instanceof IActionDelegate2) {
final IActionDelegate2 delegate2 = (IActionDelegate2) delegate;
delegate2.dispose();
}
}
public final Object execute(final ExecutionEvent event) {
final IAction action = getAction();
if (loadDelegate() && (action != null)) {
final Object trigger = event.getTrigger();
final IActionDelegate delegate = getDelegate();
// Attempt to update the selection.
final Object applicationContext = event.getApplicationContext();
if (applicationContext instanceof IEvaluationContext) {
final IEvaluationContext context = (IEvaluationContext) applicationContext;
final Object selectionObject = context
.getVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME);
if (selectionObject instanceof ISelection) {
currentSelection = (ISelection) selectionObject;
delegate.selectionChanged(action, currentSelection);
}
}
// Decide what type of delegate we have.
if ((delegate instanceof IActionDelegate2)
&& (trigger instanceof Event)) {
// This supports Eclipse 2.1 to Eclipse 3.1.
final IActionDelegate2 delegate2 = (IActionDelegate2) delegate;
final Event triggeringEvent = (Event) trigger;
delegate2.runWithEvent(action, triggeringEvent);
} else if ((delegate instanceof IActionDelegateWithEvent)
&& (trigger instanceof Event)) {
// This supports Eclipse 2.0
final IActionDelegateWithEvent delegateWithEvent = (IActionDelegateWithEvent) delegate;
final Event triggeringEvent = (Event) trigger;
delegateWithEvent.runWithEvent(action, triggeringEvent);
} else {
delegate.run(action);
}
}
return null;
}
/**
* Retrieves the action corresponding to the currently active workbench
* window, if any.
*
* @return The current action; <code>null</code> if there is no currently
* active workbench window.
*/
private final CommandLegacyActionWrapper getAction() {
if (action == null) {
action = new CommandLegacyActionWrapper(actionId, command, style,
window);
action.addPropertyChangeListener(new IPropertyChangeListener() {
public final void propertyChange(final PropertyChangeEvent event) {
// TODO Update the state somehow.
}
});
}
return action;
}
/**
* Retrieves the delegate corresponding to the currently active workbench
* window, if any. This does not trigger loading of the delegate.
*
* @return The current delegate; or <code>null</code> if none.
*/
private final IActionDelegate getDelegate() {
return delegate;
}
public State getState(String stateId) {
// TODO Auto-generated method stub
return null;
}
public String[] getStateIds() {
// TODO Auto-generated method stub
return null;
}
public final void handleStateChange(final State state, final Object oldValue) {
// TODO What should we do here?
}
/**
* Initialize the action delegate by calling its lifecycle method.
*/
private final boolean initDelegate() {
final IWorkbenchPage page = window.getActivePage();
final IWorkbenchPart activePart;
final IEditorPart activeEditor;
if (page == null) {
activePart = null;
activeEditor = null;
} else {
activePart = page.getActivePart();
activeEditor = page.getActiveEditor();
}
final IActionDelegate delegate = getDelegate();
final IAction action = getAction();
// Check to see if the view delegate should be initialized.
if ((viewId != null) && (page != null)
&& (delegate instanceof IViewActionDelegate)) {
final IViewPart viewPart = page.findView(viewId);
if (viewPart == null) {
return false;
}
}
// Initialize the delegate.
final ISafeRunnable runnable = new ISafeRunnable() {
public final void handleException(final Throwable exception) {
// Do nothing.
}
public final void run() {
// Handle IActionDelegate2
if (delegate instanceof IActionDelegate2) {
final IActionDelegate2 delegate2 = (IActionDelegate2) delegate;
delegate2.init(action);
}
// Handle IObjectActionDelegates
if ((delegate instanceof IObjectActionDelegate)
&& (activePart != null)) {
final IObjectActionDelegate objectActionDelegate = (IObjectActionDelegate) delegate;
objectActionDelegate.setActivePart(action, activePart);
}
// Handle IEditorActionDelegates
if (delegate instanceof IEditorActionDelegate) {
final IEditorActionDelegate editorActionDelegate = (IEditorActionDelegate) delegate;
editorActionDelegate.setActiveEditor(action, activeEditor);
}
// Handle IViewActionDelegates
if ((viewId != null) && (page != null)
&& (delegate instanceof IViewActionDelegate)) {
final IViewPart viewPart = page.findView(viewId);
final IViewActionDelegate viewActionDelegate = (IViewActionDelegate) delegate;
viewActionDelegate.init(viewPart);
}
// Handle IWorkbenchWindowActionDelegate
if (delegate instanceof IWorkbenchWindowActionDelegate) {
final IWorkbenchWindowActionDelegate workbenchWindowActionDelegate = (IWorkbenchWindowActionDelegate) delegate;
workbenchWindowActionDelegate.init(window);
}
}
};
SafeRunner.run(runnable);
return true;
}
public final boolean isEnabled() {
final CommandLegacyActionWrapper action = getAction();
if (enabledWhenExpression != null) {
try {
final IHandlerService service = (IHandlerService) window
.getService(IHandlerService.class);
final EvaluationResult result = enabledWhenExpression
.evaluate(service.getCurrentState());
if (result == EvaluationResult.TRUE) {
return (action == null)
|| action.isEnabledDisregardingCommand();
}
} catch (final CoreException e) {
// We will just fall through an let it return false.
final StringBuffer message = new StringBuffer(
"An exception occurred while evaluating the enabledWhen expression for "); //$NON-NLS-1$
if (element == null) {
message.append(delegate);
} else {
message.append(element.getAttribute(delegateAttributeName));
}
message.append("' could not be loaded"); //$NON-NLS-1$
final IStatus status = new Status(IStatus.WARNING,
WorkbenchPlugin.PI_WORKBENCH, 0, e.getMessage(), e);
WorkbenchPlugin.log(message.toString(), status);
}
return false;
}
return (action == null) || action.isEnabledDisregardingCommand();
}
public final boolean isHandled() {
return true;
}
/**
* Checks if the declaring plugin has been loaded. This means that there
* will be no need to delay creating the delegate.
*
* @return <code>true</code> if the bundle containing the delegate is
* already loaded -- making it safe to load the delegate.
*/
private final boolean isSafeToLoadDelegate() {
return false;
// TODO This causes problem because some people expect their selections
// to be a particular class.
// final String bundleId = element.getNamespace();
// return BundleUtility.isActive(bundleId);
}
/**
* Loads the delegate, if possible. If the delegate is loaded, then the
* member variables are updated accordingly.
*
* @return <code>true</code> if the delegate is now non-null;
* <code>false</code> otherwise.
*/
private final boolean loadDelegate() {
// Try to load the delegate, if it hasn't been loaded already.
if (delegate == null) {
/*
* If this is an IViewActionDelegate, then check to see if we have a
* view ready yet. If not, then we'll have to wait.
*/
if (viewId != null) {
final IWorkbenchPage activePage = window.getActivePage();
if (activePage != null) {
final IViewPart part = activePage.findView(viewId);
if (part == null) {
return false;
}
} else {
return false;
}
}
// Load the delegate.
try {
delegate = (IActionDelegate) element
.createExecutableExtension(delegateAttributeName);
if (initDelegate()) {
element = null;
delegateAttributeName = null;
return true;
}
delegate = null;
return false;
} catch (final ClassCastException e) {
final String message = "The proxied delegate was the wrong class"; //$NON-NLS-1$
final IStatus status = new Status(IStatus.ERROR,
WorkbenchPlugin.PI_WORKBENCH, 0, message, e);
WorkbenchPlugin.log(message, status);
return false;
} catch (final CoreException e) {
final String message = "The proxied delegate for '" //$NON-NLS-1$
+ element.getAttribute(delegateAttributeName)
+ "' could not be loaded"; //$NON-NLS-1$
IStatus status = new Status(IStatus.ERROR,
WorkbenchPlugin.PI_WORKBENCH, 0, message, e);
WorkbenchPlugin.log(message, status);
return false;
}
}
return true;
}
/**
* Refresh the action enablement.
*/
private final void refreshEnablement() {
final IActionDelegate delegate = getDelegate();
final IAction action = getAction();
if ((delegate != null) && (action != null)) {
delegate.selectionChanged(action, currentSelection);
}
}
public void removeHandlerListener(IHandlerListener handlerListener) {
if (listenerList != null) {
listenerList.remove(handlerListener);
if (listenerList.isEmpty()) {
listenerList = null;
}
}
}
public void removeState(String stateId) {
// TODO Auto-generated method stub
}
private final void selectionChanged(final ISelection selection) {
// Update selection.
currentSelection = selection;
if (currentSelection == null) {
currentSelection = StructuredSelection.EMPTY;
}
// The selection is passed to the delegate as-is without
// modification. If the selection needs to be modified
// the action contributors should do so.
// If the delegate can be loaded, do so.
// Otherwise, just update the enablement.
final IActionDelegate delegate = getDelegate();
if (delegate == null && isSafeToLoadDelegate()) {
loadDelegate();
}
refreshEnablement();
}
public final void selectionChanged(final IWorkbenchPart part,
final ISelection selection) {
selectionChanged(selection);
}
public final void selectionChanged(final SelectionChangedEvent event) {
final ISelection selection = event.getSelection();
selectionChanged(selection);
}
public final String toString() {
final StringBuffer buffer = new StringBuffer();
buffer.append("ActionDelegateHandlerProxy("); //$NON-NLS-1$
buffer.append(getDelegate());
if (element != null) {
buffer.append(',');
final String className = element
.getAttribute(delegateAttributeName);
buffer.append(className);
}
buffer.append(')');
return buffer.toString();
}
}