/******************************************************************************* * Copyright (c) 2000, 2007 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; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuCreator; import org.eclipse.jface.dialogs.MessageDialog; 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.INullSelectionListener; import org.eclipse.ui.IPluginContribution; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindowActionDelegate; import org.eclipse.ui.SelectionEnabler; import org.eclipse.ui.WorkbenchException; import org.eclipse.ui.internal.misc.StatusUtil; import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants; import org.eclipse.ui.internal.util.BundleUtility; import org.eclipse.ui.internal.util.Util; /** * A PluginAction is a proxy for an action extension. * * At startup we read the registry and create a PluginAction for each action extension. * This plugin action looks like the real action ( label, icon, etc ) and acts as * a proxy for the action until invoked. At that point the proxy will instantiate * the real action and delegate the run method to the real action. * This makes it possible to load the action extension lazily. * * Occasionally the class will ask if it is OK to * load the delegate (on selection changes). If the plugin containing * the action extension has been loaded then the action extension itself * will be instantiated. */ public abstract class PluginAction extends Action implements ISelectionListener, ISelectionChangedListener, INullSelectionListener, IPluginContribution { private IActionDelegate delegate; private SelectionEnabler enabler; private ISelection selection; private IConfigurationElement configElement; private String pluginId; private String runAttribute = IWorkbenchRegistryConstants.ATT_CLASS; private static int actionCount = 0; /** * PluginAction constructor. * * @param actionElement the element * @param id the identifier * @param style the style bits */ public PluginAction(IConfigurationElement actionElement, String id, int style) { super(null, style); this.configElement = actionElement; if (id != null) { setId(id); } else { // Create unique action id. setId("PluginAction." + Integer.toString(actionCount)); //$NON-NLS-1$ ++actionCount; } String defId = actionElement .getAttribute(IWorkbenchRegistryConstants.ATT_DEFINITION_ID); setActionDefinitionId(defId); pluginId = configElement.getNamespace(); // Read enablement declaration. if (configElement.getAttribute(IWorkbenchRegistryConstants.ATT_ENABLES_FOR) != null) { enabler = new SelectionEnabler(configElement); } else { IConfigurationElement[] kids = configElement .getChildren(IWorkbenchRegistryConstants.TAG_ENABLEMENT); IConfigurationElement[] kids2 = configElement .getChildren(IWorkbenchRegistryConstants.TAG_SELECTION); if (kids.length > 0 || kids2.length>0) { enabler = new SelectionEnabler(configElement); } } // Give enabler or delegate a chance to adjust enable state selectionChanged(StructuredSelection.EMPTY); } /** * Creates the delegate and refreshes its enablement. */ protected final void createDelegate() { // The runAttribute is null if delegate creation failed previously... if (delegate == null && runAttribute != null) { try { Object obj = WorkbenchPlugin.createExtension(configElement, runAttribute); delegate = validateDelegate(obj); initDelegate(); refreshEnablement(); } catch (Throwable e) { runAttribute = null; IStatus status = null; if (e instanceof CoreException) { status = ((CoreException) e).getStatus(); } else { status = StatusUtil .newStatus( IStatus.ERROR, "Internal plug-in action delegate error on creation.", e); //$NON-NLS-1$ } String id = configElement.getAttribute(IWorkbenchRegistryConstants.ATT_ID); WorkbenchPlugin .log( "Could not create action delegate for id: " + id, status); //$NON-NLS-1$ return; } } } /** * Validates the object is a delegate of the expected type. Subclasses can * override to check for specific delegate types. *
* Note: Calls to the object are not allowed during this method. *
* * @param obj a possible action delegate implementation * @return theIActionDelegate
implementation for the object
* @throws WorkbenchException if not of the expected delegate type
*/
protected IActionDelegate validateDelegate(Object obj)
throws WorkbenchException {
if (obj instanceof IActionDelegate) {
return (IActionDelegate) obj;
}
throw new WorkbenchException(
"Action must implement IActionDelegate"); //$NON-NLS-1$
}
/**
* Initialize the action delegate by calling its lifecycle method.
* Subclasses may override but must call this implementation first.
*/
protected void initDelegate() {
if (delegate instanceof IActionDelegate2) {
((IActionDelegate2) delegate).init(this);
}
}
/**
* Returns the action delegate if created. Can be null
* if the delegate is not created yet or if previous delegate
* creation failed.
*/
protected IActionDelegate getDelegate() {
return delegate;
}
/**
* Returns true if the declaring plugin has been loaded
* and there is no need to delay creating the delegate
* any more.
*/
protected boolean isOkToCreateDelegate() {
// test if the plugin has loaded
String bundleId = configElement.getContributor().getName();
return BundleUtility.isActive(bundleId);
}
/**
* Refresh the action enablement.
*/
protected void refreshEnablement() {
if (enabler != null) {
setEnabled(enabler.isEnabledForSelection(selection));
}
if (delegate != null) {
delegate.selectionChanged(this, selection);
}
}
@Override
public void run() {
runWithEvent(null);
}
@Override
public void runWithEvent(Event event) {
// this message dialog is problematic.
if (delegate == null) {
createDelegate();
if (delegate == null) {
MessageDialog
.openInformation(
Util.getShellToParentOn(),
WorkbenchMessages.Information,
WorkbenchMessages.PluginAction_operationNotAvailableMessage);
return;
}
if (!isEnabled()) {
MessageDialog.openInformation(Util.getShellToParentOn(), WorkbenchMessages.Information,
WorkbenchMessages.PluginAction_disabledMessage);
return;
}
}
if (event != null) {
if (delegate instanceof IActionDelegate2) {
((IActionDelegate2) delegate).runWithEvent(this, event);
return;
}
// Keep for backward compatibility with R2.0
if (delegate instanceof IActionDelegateWithEvent) {
((IActionDelegateWithEvent) delegate).runWithEvent(this, event);
return;
}
}
delegate.run(this);
}
/**
* Handles selection change. If rule-based enabled is
* defined, it will be first to call it. If the delegate
* is loaded, it will also be given a chance.
*
* @param newSelection the new selection
*/
public void selectionChanged(ISelection newSelection) {
// Update selection.
selection = newSelection;
if (selection == null) {
selection = 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.
if (delegate == null && isOkToCreateDelegate()) {
createDelegate();
} else {
refreshEnablement();
}
}
/**
* The SelectionChangedEventAction
implementation of this
* ISelectionChangedListener
method calls
* selectionChanged(IStructuredSelection)
when the selection is
* a structured one.
*/
@Override
public void selectionChanged(SelectionChangedEvent event) {
ISelection sel = event.getSelection();
selectionChanged(sel);
}
/**
* The SelectionChangedEventAction
implementation of this
* ISelectionListener
method calls
* selectionChanged(IStructuredSelection)
when the selection is
* a structured one. Subclasses may extend this method to react to the change.
*/
@Override
public void selectionChanged(IWorkbenchPart part, ISelection sel) {
selectionChanged(sel);
}
/**
* For testing purposes only.
*
* @return the selection
* @since 3.1
*/
public ISelection getSelection() {
return selection;
}
/**
* Returns the action identifier this action overrides.
* Default implementation returns null
.
*
* @return the action identifier to override or null
*/
public String getOverrideActionId() {
return null;
}
/**
* @return the IConfigurationElement used to create this PluginAction.
*
* @since 3.0
*/
protected IConfigurationElement getConfigElement() {
return configElement;
}
@Override
public String getLocalId() {
return getId();
}
@Override
public String getPluginId() {
return pluginId;
}
/**
* Disposes the delegate, if created.
*
* @since 3.1
*/
public void disposeDelegate() {
// avoid calling dispose() twice if the delegate implements
// both IActionDelegate2 and IWorkbenchWindowActionDelegate
if (getDelegate() instanceof IActionDelegate2) {
((IActionDelegate2) getDelegate()).dispose();
}
else if (getDelegate() instanceof IWorkbenchWindowActionDelegate) {
((IWorkbenchWindowActionDelegate) getDelegate()).dispose();
}
delegate = null;
}
/**
* Disposes this plugin action.
*
* @since 3.1
*/
public void dispose() {
disposeDelegate();
selection = null;
}
@Override
public IMenuCreator getMenuCreator() {
// now that action contribution item defers asking for the menu
// creator until its ready o show the menu, asking for the menu
// creator is time to instantiate the delegate
if (getDelegate()==null) {
createDelegate();
}
return super.getMenuCreator();
}
}