001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 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.internal;
011:
012: import org.eclipse.core.runtime.CoreException;
013: import org.eclipse.core.runtime.IConfigurationElement;
014: import org.eclipse.core.runtime.IStatus;
015: import org.eclipse.jface.action.Action;
016: import org.eclipse.jface.action.IAction;
017: import org.eclipse.jface.dialogs.MessageDialog;
018: import org.eclipse.jface.viewers.ISelection;
019: import org.eclipse.jface.viewers.ISelectionChangedListener;
020: import org.eclipse.jface.viewers.SelectionChangedEvent;
021: import org.eclipse.jface.viewers.StructuredSelection;
022: import org.eclipse.swt.widgets.Event;
023: import org.eclipse.ui.IActionDelegate;
024: import org.eclipse.ui.IActionDelegate2;
025: import org.eclipse.ui.IActionDelegateWithEvent;
026: import org.eclipse.ui.INullSelectionListener;
027: import org.eclipse.ui.IPluginContribution;
028: import org.eclipse.ui.ISelectionListener;
029: import org.eclipse.ui.IWorkbenchPart;
030: import org.eclipse.ui.IWorkbenchWindowActionDelegate;
031: import org.eclipse.ui.SelectionEnabler;
032: import org.eclipse.ui.WorkbenchException;
033: import org.eclipse.ui.internal.misc.StatusUtil;
034: import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
035: import org.eclipse.ui.internal.util.BundleUtility;
036: import org.eclipse.ui.internal.util.Util;
037:
038: /**
039: * A PluginAction is a proxy for an action extension.
040: *
041: * At startup we read the registry and create a PluginAction for each action extension.
042: * This plugin action looks like the real action ( label, icon, etc ) and acts as
043: * a proxy for the action until invoked. At that point the proxy will instantiate
044: * the real action and delegate the run method to the real action.
045: * This makes it possible to load the action extension lazily.
046: *
047: * Occasionally the class will ask if it is OK to
048: * load the delegate (on selection changes). If the plugin containing
049: * the action extension has been loaded then the action extension itself
050: * will be instantiated.
051: */
052:
053: public abstract class PluginAction extends Action implements
054: ISelectionListener, ISelectionChangedListener,
055: INullSelectionListener, IPluginContribution {
056: private IActionDelegate delegate;
057:
058: private SelectionEnabler enabler;
059:
060: private ISelection selection;
061:
062: private IConfigurationElement configElement;
063:
064: private String pluginId;
065:
066: private String runAttribute = IWorkbenchRegistryConstants.ATT_CLASS;
067:
068: private static int actionCount = 0;
069:
070: /**
071: * PluginAction constructor.
072: *
073: * @param actionElement the element
074: * @param id the identifier
075: * @param style the style bits
076: */
077: public PluginAction(IConfigurationElement actionElement, String id,
078: int style) {
079: super (null, style);
080:
081: this .configElement = actionElement;
082:
083: if (id != null) {
084: setId(id);
085: } else {
086: // Create unique action id.
087: setId("PluginAction." + Integer.toString(actionCount)); //$NON-NLS-1$
088: ++actionCount;
089: }
090:
091: String defId = actionElement
092: .getAttribute(IWorkbenchRegistryConstants.ATT_DEFINITION_ID);
093: setActionDefinitionId(defId);
094:
095: pluginId = configElement.getNamespace();
096:
097: // Read enablement declaration.
098: if (configElement
099: .getAttribute(IWorkbenchRegistryConstants.ATT_ENABLES_FOR) != null) {
100: enabler = new SelectionEnabler(configElement);
101: } else {
102: IConfigurationElement[] kids = configElement
103: .getChildren(IWorkbenchRegistryConstants.TAG_ENABLEMENT);
104: IConfigurationElement[] kids2 = configElement
105: .getChildren(IWorkbenchRegistryConstants.TAG_SELECTION);
106: if (kids.length > 0 || kids2.length > 0) {
107: enabler = new SelectionEnabler(configElement);
108: }
109: }
110:
111: // Give enabler or delegate a chance to adjust enable state
112: selectionChanged(StructuredSelection.EMPTY);
113: }
114:
115: /**
116: * Creates the delegate and refreshes its enablement.
117: */
118: protected final void createDelegate() {
119: // The runAttribute is null if delegate creation failed previously...
120: if (delegate == null && runAttribute != null) {
121: try {
122: Object obj = WorkbenchPlugin.createExtension(
123: configElement, runAttribute);
124: delegate = validateDelegate(obj);
125: initDelegate();
126: refreshEnablement();
127: } catch (Throwable e) {
128: runAttribute = null;
129: IStatus status = null;
130: if (e instanceof CoreException) {
131: status = ((CoreException) e).getStatus();
132: } else {
133: status = StatusUtil
134: .newStatus(
135: IStatus.ERROR,
136: "Internal plug-in action delegate error on creation.", e); //$NON-NLS-1$
137: }
138: String id = configElement
139: .getAttribute(IWorkbenchRegistryConstants.ATT_ID);
140: WorkbenchPlugin
141: .log(
142: "Could not create action delegate for id: " + id, status); //$NON-NLS-1$
143: return;
144: }
145: }
146: }
147:
148: /**
149: * Validates the object is a delegate of the expected type. Subclasses can
150: * override to check for specific delegate types.
151: * <p>
152: * <b>Note:</b> Calls to the object are not allowed during this method.
153: * </p>
154: *
155: * @param obj a possible action delegate implementation
156: * @return the <code>IActionDelegate</code> implementation for the object
157: * @throws WorkbenchException if not of the expected delegate type
158: */
159: protected IActionDelegate validateDelegate(Object obj)
160: throws WorkbenchException {
161: if (obj instanceof IActionDelegate) {
162: return (IActionDelegate) obj;
163: }
164:
165: throw new WorkbenchException(
166: "Action must implement IActionDelegate"); //$NON-NLS-1$
167: }
168:
169: /**
170: * Initialize the action delegate by calling its lifecycle method.
171: * Subclasses may override but must call this implementation first.
172: */
173: protected void initDelegate() {
174: if (delegate instanceof IActionDelegate2) {
175: ((IActionDelegate2) delegate).init(this );
176: }
177: }
178:
179: /**
180: * Returns the action delegate if created. Can be <code>null</code>
181: * if the delegate is not created yet or if previous delegate
182: * creation failed.
183: */
184: protected IActionDelegate getDelegate() {
185: return delegate;
186: }
187:
188: /**
189: * Returns true if the declaring plugin has been loaded
190: * and there is no need to delay creating the delegate
191: * any more.
192: */
193: protected boolean isOkToCreateDelegate() {
194: if (getStyle() == IAction.AS_DROP_DOWN_MENU
195: && !WWinPluginPulldown.class.isInstance(this )) {
196: return true;
197: }
198:
199: // test if the plugin has loaded
200: String bundleId = configElement.getNamespace();
201: return BundleUtility.isActive(bundleId);
202: }
203:
204: /**
205: * Refresh the action enablement.
206: */
207: protected void refreshEnablement() {
208: if (enabler != null) {
209: setEnabled(enabler.isEnabledForSelection(selection));
210: }
211: if (delegate != null) {
212: delegate.selectionChanged(this , selection);
213: }
214: }
215:
216: /* (non-Javadoc)
217: * Method declared on IAction.
218: */
219: public void run() {
220: runWithEvent(null);
221: }
222:
223: /* (non-Javadoc)
224: * Method declared on IAction.
225: */
226: public void runWithEvent(Event event) {
227: // this message dialog is problematic.
228: if (delegate == null) {
229: createDelegate();
230: if (delegate == null) {
231: MessageDialog
232: .openInformation(
233: Util.getShellToParentOn(),
234: WorkbenchMessages.Information,
235: WorkbenchMessages.PluginAction_operationNotAvailableMessage);
236: return;
237: }
238: if (!isEnabled()) {
239: MessageDialog.openInformation(
240: Util.getShellToParentOn(),
241: WorkbenchMessages.Information,
242: WorkbenchMessages.PluginAction_disabledMessage);
243: return;
244: }
245: }
246:
247: if (event != null) {
248: if (delegate instanceof IActionDelegate2) {
249: ((IActionDelegate2) delegate).runWithEvent(this , event);
250: return;
251: }
252: // Keep for backward compatibility with R2.0
253: if (delegate instanceof IActionDelegateWithEvent) {
254: ((IActionDelegateWithEvent) delegate).runWithEvent(
255: this , event);
256: return;
257: }
258: }
259:
260: delegate.run(this );
261: }
262:
263: /**
264: * Handles selection change. If rule-based enabled is
265: * defined, it will be first to call it. If the delegate
266: * is loaded, it will also be given a chance.
267: *
268: * @param newSelection the new selection
269: */
270: public void selectionChanged(ISelection newSelection) {
271: // Update selection.
272: selection = newSelection;
273: if (selection == null) {
274: selection = StructuredSelection.EMPTY;
275: }
276:
277: // The selection is passed to the delegate as-is without
278: // modification. If the selection needs to be modified
279: // the action contributors should do so.
280:
281: // If the delegate can be loaded, do so.
282: // Otherwise, just update the enablement.
283: if (delegate == null && isOkToCreateDelegate()) {
284: createDelegate();
285: } else {
286: refreshEnablement();
287: }
288: }
289:
290: /**
291: * The <code>SelectionChangedEventAction</code> implementation of this
292: * <code>ISelectionChangedListener</code> method calls
293: * <code>selectionChanged(IStructuredSelection)</code> when the selection is
294: * a structured one.
295: */
296: public void selectionChanged(SelectionChangedEvent event) {
297: ISelection sel = event.getSelection();
298: selectionChanged(sel);
299: }
300:
301: /**
302: * The <code>SelectionChangedEventAction</code> implementation of this
303: * <code>ISelectionListener</code> method calls
304: * <code>selectionChanged(IStructuredSelection)</code> when the selection is
305: * a structured one. Subclasses may extend this method to react to the change.
306: */
307: public void selectionChanged(IWorkbenchPart part, ISelection sel) {
308: selectionChanged(sel);
309: }
310:
311: /**
312: * For testing purposes only.
313: *
314: * @return the selection
315: * @since 3.1
316: */
317: public ISelection getSelection() {
318: return selection;
319: }
320:
321: /**
322: * Returns the action identifier this action overrides.
323: * Default implementation returns <code>null</code>.
324: *
325: * @return the action identifier to override or <code>null</code>
326: */
327: public String getOverrideActionId() {
328: return null;
329: }
330:
331: /**
332: * @return the IConfigurationElement used to create this PluginAction.
333: *
334: * @since 3.0
335: */
336: protected IConfigurationElement getConfigElement() {
337: return configElement;
338: }
339:
340: /* (non-Javadoc)
341: * @see org.eclipse.ui.IPluginContribution#getLocalId()
342: */
343: public String getLocalId() {
344: return getId();
345: }
346:
347: /* (non-Javadoc)
348: * @see org.eclipse.ui.IPluginContribution#getPluginId()
349: */
350: public String getPluginId() {
351: return pluginId;
352: }
353:
354: /**
355: * Disposes the delegate, if created.
356: *
357: * @since 3.1
358: */
359: public void disposeDelegate() {
360: // avoid calling dispose() twice if the delegate implements
361: // both IActionDelegate2 and IWorkbenchWindowActionDelegate
362: if (getDelegate() instanceof IActionDelegate2) {
363: ((IActionDelegate2) getDelegate()).dispose();
364: } else if (getDelegate() instanceof IWorkbenchWindowActionDelegate) {
365: ((IWorkbenchWindowActionDelegate) getDelegate()).dispose();
366: }
367: delegate = null;
368: }
369:
370: /**
371: * Disposes this plugin action.
372: *
373: * @since 3.1
374: */
375: public void dispose() {
376: disposeDelegate();
377: selection = null;
378: }
379: }
|