001: /*******************************************************************************
002: * Copyright (c) 2005, 2006 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.handlers;
011:
012: import org.eclipse.core.commands.Command;
013: import org.eclipse.core.commands.CommandEvent;
014: import org.eclipse.core.commands.ExecutionEvent;
015: import org.eclipse.core.commands.ExecutionException;
016: import org.eclipse.core.commands.ICommandListener;
017: import org.eclipse.core.commands.INamedHandleStateIds;
018: import org.eclipse.core.commands.NotHandledException;
019: import org.eclipse.core.commands.ParameterizedCommand;
020: import org.eclipse.core.commands.State;
021: import org.eclipse.core.commands.common.NotDefinedException;
022: import org.eclipse.jface.action.AbstractAction;
023: import org.eclipse.jface.action.IAction;
024: import org.eclipse.jface.action.IMenuCreator;
025: import org.eclipse.jface.bindings.TriggerSequence;
026: import org.eclipse.jface.bindings.keys.KeySequence;
027: import org.eclipse.jface.bindings.keys.KeyStroke;
028: import org.eclipse.jface.commands.RadioState;
029: import org.eclipse.jface.commands.ToggleState;
030: import org.eclipse.jface.menus.IMenuStateIds;
031: import org.eclipse.jface.menus.TextState;
032: import org.eclipse.jface.resource.ImageDescriptor;
033: import org.eclipse.jface.util.Util;
034: import org.eclipse.swt.events.HelpListener;
035: import org.eclipse.swt.widgets.Event;
036: import org.eclipse.ui.commands.ICommandService;
037: import org.eclipse.ui.internal.commands.CommandImageManager;
038: import org.eclipse.ui.internal.commands.ICommandImageService;
039: import org.eclipse.ui.keys.IBindingService;
040: import org.eclipse.ui.services.IServiceLocator;
041:
042: /**
043: * <p>
044: * A wrapper around the new command infrastructure that imitates the old
045: * <code>IAction</code> interface.
046: * </p>
047: * <p>
048: * Clients may instantiate this class, but must not extend.
049: * </p>
050: * <p>
051: * <strong>PROVISIONAL</strong>. This class or interface has been added as
052: * part of a work in progress. There is a guarantee neither that this API will
053: * work nor that it will remain the same. Please do not use this API without
054: * consulting with the Platform/UI team.
055: * </p>
056: * <p>
057: * This class is eventually intended to exist in
058: * <code>org.eclipse.ui.handlers</code>.
059: * </p>
060: *
061: * @since 3.2
062: */
063: public final class CommandLegacyActionWrapper extends AbstractAction {
064:
065: /**
066: * Listens to changes to one or more commands, and forwards them out through
067: * the property change event mechanism.
068: */
069: private final class CommandListener implements ICommandListener {
070: public final void commandChanged(final CommandEvent commandEvent) {
071: final Command baseCommand = commandEvent.getCommand();
072:
073: // Check if the name changed.
074: if (commandEvent.isNameChanged()) {
075: String newName = null;
076: if (baseCommand.isDefined()) {
077: try {
078: newName = baseCommand.getName();
079: } catch (final NotDefinedException e) {
080: // Not defined, so leave as null.
081: }
082: }
083: firePropertyChange(IAction.TEXT, null, newName);
084: }
085:
086: // Check if the description changed.
087: if (commandEvent.isDescriptionChanged()) {
088: String newDescription = null;
089: if (baseCommand.isDefined()) {
090: try {
091: newDescription = baseCommand.getDescription();
092: } catch (final NotDefinedException e) {
093: // Not defined, so leave as null.
094: }
095: }
096: firePropertyChange(IAction.DESCRIPTION, null,
097: newDescription);
098: firePropertyChange(IAction.TOOL_TIP_TEXT, null,
099: newDescription);
100: }
101:
102: // Check if the handled property changed.
103: if (commandEvent.isHandledChanged()) {
104: if (baseCommand.isHandled()) {
105: firePropertyChange(IAction.HANDLED, Boolean.FALSE,
106: Boolean.TRUE);
107: } else {
108: firePropertyChange(IAction.HANDLED, Boolean.TRUE,
109: Boolean.FALSE);
110: }
111: }
112: }
113:
114: }
115:
116: /**
117: * The command with which this action is associated; never <code>null</code>.
118: */
119: private ParameterizedCommand command;
120:
121: /**
122: * Listens to changes in a command, and forwards them out through the
123: * property change event mechanism.
124: */
125: private final ICommandListener commandListener = new CommandListener();
126:
127: /**
128: * Whether this action has been marked as enabled.
129: */
130: private boolean enabled = true;
131:
132: /**
133: * The identifier for the action. This may be <code>null</code>.
134: */
135: private String id;
136:
137: /**
138: * A service locator that can be used for retrieving command-based services.
139: * This value is never <code>null</code>.
140: */
141: private final IServiceLocator serviceLocator;
142:
143: /**
144: * The image style to use for this action. This value may be
145: * <code>null</code>.
146: */
147: private final String style;
148:
149: /**
150: * Constructs a new instance of <code>ActionProxy</code>.
151: *
152: * @param id
153: * The initial action identifier; may be <code>null</code>.
154: * @param command
155: * The command with which this action is associated; must not be
156: * <code>null</code>.
157: * @param style
158: * The image style to use for this action, may be
159: * <code>null</code>.
160: * @param serviceLocator
161: * A service locator that can be used to find various
162: * command-based services; must not be <code>null</code>.
163: */
164: public CommandLegacyActionWrapper(final String id,
165: final ParameterizedCommand command, final String style,
166: final IServiceLocator serviceLocator) {
167: if (command == null) {
168: throw new NullPointerException(
169: "An action proxy can't be created without a command"); //$NON-NLS-1$
170: }
171:
172: if (serviceLocator == null) {
173: throw new NullPointerException(
174: "An action proxy can't be created without a service locator"); //$NON-NLS-1$
175: }
176:
177: this .command = command;
178: this .id = id;
179: this .style = style;
180: this .serviceLocator = serviceLocator;
181:
182: // TODO Needs to listen to command, state, binding and image changes.
183: command.getCommand().addCommandListener(commandListener);
184: }
185:
186: public final int getAccelerator() {
187: final String commandId = getActionDefinitionId();
188: final IBindingService bindingService = (IBindingService) serviceLocator
189: .getService(IBindingService.class);
190: final TriggerSequence triggerSequence = bindingService
191: .getBestActiveBindingFor(commandId);
192: if (triggerSequence instanceof KeySequence) {
193: final KeySequence keySequence = (KeySequence) triggerSequence;
194: final KeyStroke[] keyStrokes = keySequence.getKeyStrokes();
195: if (keyStrokes.length == 1) {
196: final KeyStroke keyStroke = keyStrokes[0];
197: return keyStroke.getModifierKeys()
198: | keyStroke.getNaturalKey();
199: }
200: }
201:
202: return 0;
203: }
204:
205: public final String getActionDefinitionId() {
206: return command.getId();
207: }
208:
209: public final String getDescription() {
210: try {
211: return command.getCommand().getDescription();
212: } catch (final NotDefinedException e) {
213: return null;
214: }
215: }
216:
217: public final ImageDescriptor getDisabledImageDescriptor() {
218: final String commandId = getActionDefinitionId();
219: final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
220: .getService(ICommandImageService.class);
221: return commandImageService.getImageDescriptor(commandId,
222: CommandImageManager.TYPE_DISABLED, style);
223: }
224:
225: public final HelpListener getHelpListener() {
226: // TODO Help. Addressing help on commands.
227: return null;
228: }
229:
230: public final ImageDescriptor getHoverImageDescriptor() {
231: final String commandId = getActionDefinitionId();
232: final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
233: .getService(ICommandImageService.class);
234: return commandImageService.getImageDescriptor(commandId,
235: CommandImageManager.TYPE_HOVER, style);
236: }
237:
238: public final String getId() {
239: return id;
240: }
241:
242: public final ImageDescriptor getImageDescriptor() {
243: final String commandId = getActionDefinitionId();
244: final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
245: .getService(ICommandImageService.class);
246: return commandImageService.getImageDescriptor(commandId, style);
247: }
248:
249: public final IMenuCreator getMenuCreator() {
250: // TODO Pulldown. What kind of callback is needed here?
251: return null;
252: }
253:
254: public final int getStyle() {
255: // TODO Pulldown. This does not currently support the pulldown style.
256: final State state = command.getCommand().getState(
257: IMenuStateIds.STYLE);
258: if (state instanceof RadioState) {
259: return IAction.AS_RADIO_BUTTON;
260: } else if (state instanceof ToggleState) {
261: return IAction.AS_CHECK_BOX;
262: }
263:
264: return IAction.AS_PUSH_BUTTON;
265: }
266:
267: public final String getText() {
268: try {
269: return command.getName();
270: } catch (final NotDefinedException e) {
271: return null;
272: }
273: }
274:
275: public final String getToolTipText() {
276: return getDescription();
277: }
278:
279: public final boolean isChecked() {
280: final State state = command.getCommand().getState(
281: IMenuStateIds.STYLE);
282: if (state instanceof ToggleState) {
283: final Boolean currentValue = (Boolean) state.getValue();
284: return currentValue.booleanValue();
285: }
286:
287: return false;
288: }
289:
290: public final boolean isEnabled() {
291: final Command baseCommand = command.getCommand();
292: return baseCommand.isEnabled() && enabled;
293: }
294:
295: /**
296: * Whether this action's local <code>enabled</code> property is set. This
297: * can be used by handlers that are trying to check if
298: * {@link #setEnabled(boolean)} has been called. This is typically used by
299: * legacy action proxies who are trying to avoid a <a
300: * href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=117496">stack
301: * overflow</a>.
302: *
303: * @return <code>false</code> if someone has called
304: * {@link #setEnabled(boolean)} with <code>false</code>;
305: * <code>true</code> otherwise.
306: */
307: public final boolean isEnabledDisregardingCommand() {
308: return enabled;
309: }
310:
311: public final boolean isHandled() {
312: final Command baseCommand = command.getCommand();
313: return baseCommand.isHandled();
314: }
315:
316: public final void run() {
317: runWithEvent(null);
318: }
319:
320: public final void runWithEvent(final Event event) {
321: final Command baseCommand = command.getCommand();
322: final ExecutionEvent executionEvent = new ExecutionEvent(
323: command.getCommand(), command.getParameterMap(), event,
324: null);
325: try {
326: baseCommand.execute(executionEvent);
327: firePropertyChange(IAction.RESULT, null, Boolean.TRUE);
328:
329: } catch (final NotHandledException e) {
330: firePropertyChange(IAction.RESULT, null, Boolean.FALSE);
331:
332: } catch (final ExecutionException e) {
333: firePropertyChange(IAction.RESULT, null, Boolean.FALSE);
334: // TODO Should this be logged?
335:
336: }
337: }
338:
339: public final void setAccelerator(final int keycode) {
340: // TODO Binding. This is hopefully not essential.
341: }
342:
343: public final void setActionDefinitionId(final String id) {
344: // Get the old values.
345: final boolean oldChecked = isChecked();
346: final String oldDescription = getDescription();
347: final boolean oldEnabled = isEnabled();
348: final boolean oldHandled = isHandled();
349: final ImageDescriptor oldDefaultImage = getImageDescriptor();
350: final ImageDescriptor oldDisabledImage = getDisabledImageDescriptor();
351: final ImageDescriptor oldHoverImage = getHoverImageDescriptor();
352: final String oldText = getText();
353:
354: // Update the command.
355: final Command oldBaseCommand = command.getCommand();
356: oldBaseCommand.removeCommandListener(commandListener);
357: final ICommandService commandService = (ICommandService) serviceLocator
358: .getService(ICommandService.class);
359: final Command newBaseCommand = commandService.getCommand(id);
360: command = new ParameterizedCommand(newBaseCommand, null);
361: newBaseCommand.addCommandListener(commandListener);
362:
363: // Get the new values.
364: final boolean newChecked = isChecked();
365: final String newDescription = getDescription();
366: final boolean newEnabled = isEnabled();
367: final boolean newHandled = isHandled();
368: final ImageDescriptor newDefaultImage = getImageDescriptor();
369: final ImageDescriptor newDisabledImage = getDisabledImageDescriptor();
370: final ImageDescriptor newHoverImage = getHoverImageDescriptor();
371: final String newText = getText();
372:
373: // Fire property change events, as necessary.
374: if (newChecked != oldChecked) {
375: if (oldChecked) {
376: firePropertyChange(IAction.CHECKED, Boolean.TRUE,
377: Boolean.FALSE);
378: } else {
379: firePropertyChange(IAction.CHECKED, Boolean.FALSE,
380: Boolean.TRUE);
381: }
382: }
383:
384: if (!Util.equals(oldDescription, newDescription)) {
385: firePropertyChange(IAction.DESCRIPTION, oldDescription,
386: newDescription);
387: firePropertyChange(IAction.TOOL_TIP_TEXT, oldDescription,
388: newDescription);
389: }
390:
391: if (newEnabled != oldEnabled) {
392: if (oldEnabled) {
393: firePropertyChange(IAction.ENABLED, Boolean.TRUE,
394: Boolean.FALSE);
395: } else {
396: firePropertyChange(IAction.ENABLED, Boolean.FALSE,
397: Boolean.TRUE);
398: }
399: }
400:
401: if (newHandled != oldHandled) {
402: if (oldHandled) {
403: firePropertyChange(IAction.HANDLED, Boolean.TRUE,
404: Boolean.FALSE);
405: } else {
406: firePropertyChange(IAction.HANDLED, Boolean.FALSE,
407: Boolean.TRUE);
408: }
409: }
410:
411: if (!Util.equals(oldDefaultImage, newDefaultImage)) {
412: firePropertyChange(IAction.IMAGE, oldDefaultImage,
413: newDefaultImage);
414: }
415:
416: if (!Util.equals(oldDisabledImage, newDisabledImage)) {
417: firePropertyChange(IAction.IMAGE, oldDisabledImage,
418: newDisabledImage);
419: }
420:
421: if (!Util.equals(oldHoverImage, newHoverImage)) {
422: firePropertyChange(IAction.IMAGE, oldHoverImage,
423: newHoverImage);
424: }
425:
426: if (!Util.equals(oldText, newText)) {
427: firePropertyChange(IAction.TEXT, oldText, newText);
428: }
429: }
430:
431: public final void setChecked(final boolean checked) {
432: final State state = command.getCommand().getState(
433: IMenuStateIds.STYLE);
434: if (state instanceof ToggleState) {
435: final Boolean currentValue = (Boolean) state.getValue();
436: if (checked != currentValue.booleanValue()) {
437: if (checked) {
438: state.setValue(Boolean.TRUE);
439: } else {
440: state.setValue(Boolean.FALSE);
441: }
442: }
443: }
444: }
445:
446: public final void setDescription(final String text) {
447: final State state = command.getCommand().getState(
448: INamedHandleStateIds.DESCRIPTION);
449: if (state instanceof TextState) {
450: final String currentValue = (String) state.getValue();
451: if (!Util.equals(text, currentValue)) {
452: state.setValue(text);
453: }
454: }
455: }
456:
457: public final void setDisabledImageDescriptor(
458: final ImageDescriptor newImage) {
459: final String commandId = getActionDefinitionId();
460: final int type = CommandImageManager.TYPE_DISABLED;
461: final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
462: .getService(ICommandImageService.class);
463: commandImageService.bind(commandId, type, style, newImage);
464: }
465:
466: public final void setEnabled(final boolean enabled) {
467: if (enabled != this .enabled) {
468: final Boolean oldValue = this .enabled ? Boolean.TRUE
469: : Boolean.FALSE;
470: final Boolean newValue = enabled ? Boolean.TRUE
471: : Boolean.FALSE;
472: this .enabled = enabled;
473: firePropertyChange(ENABLED, oldValue, newValue);
474: }
475: }
476:
477: public final void setHelpListener(final HelpListener listener) {
478: // TODO Help Haven't even started to look at help yet.
479:
480: }
481:
482: public final void setHoverImageDescriptor(
483: final ImageDescriptor newImage) {
484: final String commandId = getActionDefinitionId();
485: final int type = CommandImageManager.TYPE_HOVER;
486: final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
487: .getService(ICommandImageService.class);
488: commandImageService.bind(commandId, type, style, newImage);
489: }
490:
491: public final void setId(final String id) {
492: this .id = id;
493: }
494:
495: public final void setImageDescriptor(final ImageDescriptor newImage) {
496: final String commandId = getActionDefinitionId();
497: final int type = CommandImageManager.TYPE_DEFAULT;
498: final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
499: .getService(ICommandImageService.class);
500: commandImageService.bind(commandId, type, style, newImage);
501: }
502:
503: public final void setMenuCreator(final IMenuCreator creator) {
504: // TODO Pulldown. This is complicated
505: }
506:
507: public final void setText(final String text) {
508: final State state = command.getCommand().getState(
509: INamedHandleStateIds.NAME);
510: if (state instanceof TextState) {
511: final String currentValue = (String) state.getValue();
512: if (!Util.equals(text, currentValue)) {
513: state.setValue(text);
514: }
515: }
516: }
517:
518: public final void setToolTipText(final String text) {
519: setDescription(text);
520: }
521:
522: }
|