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.jface.action;
011:
012: import org.eclipse.jface.resource.ImageDescriptor;
013: import org.eclipse.swt.events.HelpListener;
014: import org.eclipse.swt.widgets.Control;
015: import org.eclipse.swt.widgets.Event;
016: import org.eclipse.swt.widgets.Menu;
017:
018: /**
019: * The standard abstract implementation of an action.
020: * <p>
021: * Subclasses must implement the <code>IAction.run</code> method to carry out
022: * the action's semantics.
023: * </p>
024: */
025: public abstract class Action extends AbstractAction implements IAction {
026:
027: private static final IMenuCreator VAL_DROP_DOWN_MENU = new IMenuCreator() {
028: public void dispose() {
029: // do nothing
030: }
031:
032: public Menu getMenu(Control parent) {
033: // do nothing
034: return null;
035: }
036:
037: public Menu getMenu(Menu parent) {
038: // do nothing
039: return null;
040: }
041: };
042:
043: /*
044: * The list of default values the action can have. These values will
045: * determine the style of the action.
046: */
047: private static final String VAL_PUSH_BTN = "PUSH_BTN"; //$NON-NLS-1$
048:
049: private static final Integer VAL_RADIO_BTN_OFF = new Integer(0);
050:
051: private static final Integer VAL_RADIO_BTN_ON = new Integer(1);
052:
053: private static final Boolean VAL_TOGGLE_BTN_OFF = Boolean.FALSE;
054:
055: private static final Boolean VAL_TOGGLE_BTN_ON = Boolean.TRUE;
056:
057: /**
058: * Converts an accelerator key code to a string representation.
059: *
060: * @param keyCode
061: * the key code to be translated
062: * @return a string representation of the key code
063: */
064: public static String convertAccelerator(int keyCode) {
065: return LegacyActionTools.convertAccelerator(keyCode);
066: }
067:
068: /**
069: * Parses the given accelerator text, and converts it to an accelerator key
070: * code.
071: *
072: * @param acceleratorText
073: * the accelerator text
074: * @return the SWT key code, or 0 if there is no accelerator
075: */
076: public static int convertAccelerator(String acceleratorText) {
077: return LegacyActionTools.convertAccelerator(acceleratorText);
078: }
079:
080: /**
081: * Maps a standard keyboard key name to an SWT key code. Key names are
082: * converted to upper case before comparison. If the key name is a single
083: * letter, for example "S", its character code is returned.
084: * <p>
085: * The following key names are known (case is ignored):
086: * <ul>
087: * <li><code>"BACKSPACE"</code></li>
088: * <li><code>"TAB"</code></li>
089: * <li><code>"RETURN"</code></li>
090: * <li><code>"ENTER"</code></li>
091: * <li><code>"ESC"</code></li>
092: * <li><code>"ESCAPE"</code></li>
093: * <li><code>"DELETE"</code></li>
094: * <li><code>"SPACE"</code></li>
095: * <li><code>"ARROW_UP"</code>, <code>"ARROW_DOWN"</code>,
096: * <code>"ARROW_LEFT"</code>, and <code>"ARROW_RIGHT"</code></li>
097: * <li><code>"PAGE_UP"</code> and <code>"PAGE_DOWN"</code></li>
098: * <li><code>"HOME"</code></li>
099: * <li><code>"END"</code></li>
100: * <li><code>"INSERT"</code></li>
101: * <li><code>"F1"</code>, <code>"F2"</code> through <code>"F12"</code></li>
102: * </ul>
103: * </p>
104: *
105: * @param token
106: * the key name
107: * @return the SWT key code, <code>-1</code> if no match was found
108: * @see org.eclipse.swt.SWT
109: */
110: public static int findKeyCode(String token) {
111: return LegacyActionTools.findKeyCode(token);
112: }
113:
114: /**
115: * Maps an SWT key code to a standard keyboard key name. The key code is
116: * stripped of modifiers (SWT.CTRL, SWT.ALT, SWT.SHIFT, and SWT.COMMAND). If
117: * the key code is not an SWT code (for example if it a key code for the key
118: * 'S'), a string containing a character representation of the key code is
119: * returned.
120: *
121: * @param keyCode
122: * the key code to be translated
123: * @return the string representation of the key code
124: * @see org.eclipse.swt.SWT
125: * @since 2.0
126: */
127: public static String findKeyString(int keyCode) {
128: return LegacyActionTools.findKeyString(keyCode);
129: }
130:
131: /**
132: * Maps standard keyboard modifier key names to the corresponding SWT
133: * modifier bit. The following modifier key names are recognized (case is
134: * ignored): <code>"CTRL"</code>, <code>"SHIFT"</code>,
135: * <code>"ALT"</code>, and <code>"COMMAND"</code>. The given modifier
136: * key name is converted to upper case before comparison.
137: *
138: * @param token
139: * the modifier key name
140: * @return the SWT modifier bit, or <code>0</code> if no match was found
141: * @see org.eclipse.swt.SWT
142: */
143: public static int findModifier(String token) {
144: return LegacyActionTools.findModifier(token);
145: }
146:
147: /**
148: * Returns a string representation of an SWT modifier bit (SWT.CTRL,
149: * SWT.ALT, SWT.SHIFT, and SWT.COMMAND). Returns <code>null</code> if the
150: * key code is not an SWT modifier bit.
151: *
152: * @param keyCode
153: * the SWT modifier bit to be translated
154: * @return the string representation of the SWT modifier bit, or
155: * <code>null</code> if the key code was not an SWT modifier bit
156: * @see org.eclipse.swt.SWT
157: * @since 2.0
158: */
159: public static String findModifierString(int keyCode) {
160: return LegacyActionTools.findModifierString(keyCode);
161: }
162:
163: /**
164: * Convenience method for removing any optional accelerator text from the
165: * given string. The accelerator text appears at the end of the text, and is
166: * separated from the main part by a single tab character <code>'\t'</code>.
167: *
168: * @param text
169: * the text
170: * @return the text sans accelerator
171: */
172: public static String removeAcceleratorText(String text) {
173: return LegacyActionTools.removeAcceleratorText(text);
174: }
175:
176: /**
177: * Convenience method for removing any mnemonics from the given string. For
178: * example, <code>removeMnemonics("&Open")</code> will return
179: * <code>"Open"</code>.
180: *
181: * @param text
182: * the text
183: * @return the text sans mnemonics
184: *
185: * @since 3.0
186: */
187: public static String removeMnemonics(String text) {
188: return LegacyActionTools.removeMnemonics(text);
189: }
190:
191: /**
192: * This action's accelerator; <code>0</code> means none.
193: */
194: private int accelerator = 0;
195:
196: /**
197: * This action's action definition id, or <code>null</code> if none.
198: */
199: private String actionDefinitionId;
200:
201: /**
202: * This action's description, or <code>null</code> if none.
203: */
204: private String description;
205:
206: /**
207: * This action's disabled image, or <code>null</code> if none.
208: */
209: private ImageDescriptor disabledImage;
210:
211: /**
212: * Indicates this action is enabled.
213: */
214: private boolean enabled = true;
215:
216: /**
217: * An action's help listener, or <code>null</code> if none.
218: */
219: private HelpListener helpListener;
220:
221: /**
222: * This action's hover image, or <code>null</code> if none.
223: */
224: private ImageDescriptor hoverImage;
225:
226: /**
227: * This action's id, or <code>null</code> if none.
228: */
229: private String id;
230:
231: /**
232: * This action's image, or <code>null</code> if none.
233: */
234: private ImageDescriptor image;
235:
236: /**
237: * This action's text, or <code>null</code> if none.
238: */
239: private String text;
240:
241: /**
242: * This action's tool tip text, or <code>null</code> if none.
243: */
244: private String toolTipText;
245:
246: /**
247: * Holds the action's menu creator (an IMenuCreator) or checked state (a
248: * Boolean for toggle button, or an Integer for radio button), or
249: * <code>null</code> if neither have been set.
250: * <p>
251: * The value of this field affects the value of <code>getStyle()</code>.
252: * </p>
253: */
254: private Object value = null;
255:
256: /**
257: * Creates a new action with no text and no image.
258: * <p>
259: * Configure the action later using the set methods.
260: * </p>
261: */
262: protected Action() {
263: // do nothing
264: }
265:
266: /**
267: * Creates a new action with the given text and no image. Calls the zero-arg
268: * constructor, then <code>setText</code>.
269: *
270: * @param text
271: * the string used as the text for the action, or
272: * <code>null</code> if there is no text
273: * @see #setText
274: */
275: protected Action(String text) {
276: this ();
277: setText(text);
278: }
279:
280: /**
281: * Creates a new action with the given text and image. Calls the zero-arg
282: * constructor, then <code>setText</code> and
283: * <code>setImageDescriptor</code>.
284: *
285: * @param text
286: * the action's text, or <code>null</code> if there is no text
287: * @param image
288: * the action's image, or <code>null</code> if there is no
289: * image
290: * @see #setText
291: * @see #setImageDescriptor
292: */
293: protected Action(String text, ImageDescriptor image) {
294: this (text);
295: setImageDescriptor(image);
296: }
297:
298: /**
299: * Creates a new action with the given text and style.
300: *
301: * @param text
302: * the action's text, or <code>null</code> if there is no text
303: * @param style
304: * one of <code>AS_PUSH_BUTTON</code>,
305: * <code>AS_CHECK_BOX</code>, <code>AS_DROP_DOWN_MENU</code>,
306: * <code>AS_RADIO_BUTTON</code>, and
307: * <code>AS_UNSPECIFIED</code>.
308: */
309: protected Action(String text, int style) {
310: this (text);
311: switch (style) {
312: case AS_PUSH_BUTTON:
313: value = VAL_PUSH_BTN;
314: break;
315: case AS_CHECK_BOX:
316: value = VAL_TOGGLE_BTN_OFF;
317: break;
318: case AS_DROP_DOWN_MENU:
319: value = VAL_DROP_DOWN_MENU;
320: break;
321: case AS_RADIO_BUTTON:
322: value = VAL_RADIO_BTN_OFF;
323: break;
324: }
325: }
326:
327: /*
328: * (non-Javadoc) Method declared on IAction.
329: */
330: public int getAccelerator() {
331: return accelerator;
332: }
333:
334: /*
335: * (non-Javadoc) Method declared on IAction.
336: *
337: */
338: public String getActionDefinitionId() {
339: return actionDefinitionId;
340: }
341:
342: /*
343: * (non-Javadoc) Method declared on IAction.
344: */
345: public String getDescription() {
346: if (description != null) {
347: return description;
348: }
349: return getToolTipText();
350: }
351:
352: /*
353: * (non-Javadoc) Method declared on IAction.
354: */
355: public ImageDescriptor getDisabledImageDescriptor() {
356: return disabledImage;
357: }
358:
359: /*
360: * (non-Javadoc) Method declared on IAction.
361: */
362: public HelpListener getHelpListener() {
363: return helpListener;
364: }
365:
366: /*
367: * (non-Javadoc) Method declared on IAction.
368: */
369: public ImageDescriptor getHoverImageDescriptor() {
370: return hoverImage;
371: }
372:
373: /*
374: * (non-Javadoc) Method declared on IAction.
375: */
376: public String getId() {
377: return id;
378: }
379:
380: /*
381: * (non-Javadoc) Method declared on IAction.
382: */
383: public ImageDescriptor getImageDescriptor() {
384: return image;
385: }
386:
387: /*
388: * (non-Javadoc) Method declared on IAction.
389: */
390: public IMenuCreator getMenuCreator() {
391: // The default drop down menu value is only used
392: // to mark this action requested style. So do not
393: // return it. For backward compatibility reasons.
394: if (value == VAL_DROP_DOWN_MENU) {
395: return null;
396: }
397: if (value instanceof IMenuCreator) {
398: return (IMenuCreator) value;
399: }
400: return null;
401: }
402:
403: /*
404: * (non-Javadoc) Method declared on IAction.
405: */
406: public int getStyle() {
407: // Infer the style from the value field.
408: if (value == VAL_PUSH_BTN || value == null) {
409: return AS_PUSH_BUTTON;
410: }
411: if (value == VAL_TOGGLE_BTN_ON || value == VAL_TOGGLE_BTN_OFF) {
412: return AS_CHECK_BOX;
413: }
414: if (value == VAL_RADIO_BTN_ON || value == VAL_RADIO_BTN_OFF) {
415: return AS_RADIO_BUTTON;
416: }
417: if (value instanceof IMenuCreator) {
418: return AS_DROP_DOWN_MENU;
419: }
420:
421: // We should never get to this line...
422: return AS_PUSH_BUTTON;
423: }
424:
425: /*
426: * (non-Javadoc) Method declared on IAction.
427: */
428: public String getText() {
429: return text;
430: }
431:
432: /*
433: * (non-Javadoc) Method declared on IAction.
434: */
435: public String getToolTipText() {
436: return toolTipText;
437: }
438:
439: /*
440: * (non-Javadoc) Method declared on IAction.
441: */
442: public boolean isChecked() {
443: return value == VAL_TOGGLE_BTN_ON || value == VAL_RADIO_BTN_ON;
444: }
445:
446: /*
447: * (non-Javadoc) Method declared on IAction.
448: */
449: public boolean isEnabled() {
450: return enabled;
451: }
452:
453: /*
454: * (non-Javadoc) Method declared on IAction.
455: */
456: public boolean isHandled() {
457: return true;
458: }
459:
460: /**
461: * Reports the outcome of the running of this action via the
462: * {@link IAction#RESULT} property.
463: *
464: * @param success
465: * <code>true</code> if the action succeeded and
466: * <code>false</code> if the action failed or was not completed
467: * @see IAction#RESULT
468: * @since 3.0
469: */
470: public final void notifyResult(boolean success) {
471: // avoid Boolean.valueOf(boolean) to allow compilation against JCL
472: // Foundation (bug 80059)
473: firePropertyChange(RESULT, null, success ? Boolean.TRUE
474: : Boolean.FALSE);
475: }
476:
477: /**
478: * The default implementation of this <code>IAction</code> method does
479: * nothing. Subclasses should override this method if they do not need
480: * information from the triggering event, or override
481: * <code>runWithEvent(Event)</code> if they do.
482: */
483: public void run() {
484: // do nothing
485: }
486:
487: /**
488: * The default implementation of this <code>IAction</code> method ignores
489: * the event argument, and simply calls <code>run()</code>. Subclasses
490: * should override this method if they need information from the triggering
491: * event, or override <code>run()</code> if not.
492: *
493: * @param event
494: * the SWT event which triggered this action being run
495: * @since 2.0
496: */
497: public void runWithEvent(Event event) {
498: run();
499: }
500:
501: /*
502: * @see IAction#setAccelerator(int)
503: */
504: public void setAccelerator(int keycode) {
505: this .accelerator = keycode;
506: }
507:
508: /*
509: * (non-Javadoc) Method declared on IAction.
510: */
511: public void setActionDefinitionId(String id) {
512: actionDefinitionId = id;
513: }
514:
515: /*
516: * (non-Javadoc) Method declared on IAction.
517: */
518: public void setChecked(boolean checked) {
519: Object newValue = null;
520:
521: // For backward compatibility, if the style is not
522: // set yet, then convert it to a toggle button.
523: if (value == null || value == VAL_TOGGLE_BTN_ON
524: || value == VAL_TOGGLE_BTN_OFF) {
525: newValue = checked ? VAL_TOGGLE_BTN_ON : VAL_TOGGLE_BTN_OFF;
526: } else if (value == VAL_RADIO_BTN_ON
527: || value == VAL_RADIO_BTN_OFF) {
528: newValue = checked ? VAL_RADIO_BTN_ON : VAL_RADIO_BTN_OFF;
529: } else {
530: // Some other style already, so do nothing.
531: return;
532: }
533:
534: if (newValue != value) {
535: value = newValue;
536: if (checked) {
537: firePropertyChange(CHECKED, Boolean.FALSE, Boolean.TRUE);
538: } else {
539: firePropertyChange(CHECKED, Boolean.TRUE, Boolean.FALSE);
540: }
541: }
542: }
543:
544: /*
545: * (non-Javadoc) Method declared on IAction.
546: */
547: public void setDescription(String text) {
548:
549: if ((description == null && text != null)
550: || (description != null && text == null)
551: || (description != null && text != null && !text
552: .equals(description))) {
553: String oldDescription = description;
554: description = text;
555: firePropertyChange(DESCRIPTION, oldDescription, description);
556: }
557: }
558:
559: /*
560: * (non-Javadoc) Method declared on IAction.
561: */
562: public void setDisabledImageDescriptor(ImageDescriptor newImage) {
563: if (disabledImage != newImage) {
564: ImageDescriptor oldImage = disabledImage;
565: disabledImage = newImage;
566: firePropertyChange(IMAGE, oldImage, newImage);
567: }
568: }
569:
570: /*
571: * (non-Javadoc) Method declared on IAction.
572: */
573: public void setEnabled(boolean enabled) {
574: if (enabled != this .enabled) {
575: Boolean oldVal = this .enabled ? Boolean.TRUE
576: : Boolean.FALSE;
577: Boolean newVal = enabled ? Boolean.TRUE : Boolean.FALSE;
578: this .enabled = enabled;
579: firePropertyChange(ENABLED, oldVal, newVal);
580: }
581: }
582:
583: /*
584: * (non-Javadoc) Method declared on IAction.
585: */
586: public void setHelpListener(HelpListener listener) {
587: helpListener = listener;
588: }
589:
590: /*
591: * (non-Javadoc) Method declared on IAction.
592: */
593: public void setHoverImageDescriptor(ImageDescriptor newImage) {
594: if (hoverImage != newImage) {
595: ImageDescriptor oldImage = hoverImage;
596: hoverImage = newImage;
597: firePropertyChange(IMAGE, oldImage, newImage);
598: }
599: }
600:
601: /*
602: * (non-Javadoc) Method declared on IAction.
603: */
604: public void setId(String id) {
605: this .id = id;
606: }
607:
608: /*
609: * (non-Javadoc) Method declared on IAction.
610: */
611: public void setImageDescriptor(ImageDescriptor newImage) {
612: if (image != newImage) {
613: ImageDescriptor oldImage = image;
614: image = newImage;
615: firePropertyChange(IMAGE, oldImage, newImage);
616: }
617: }
618:
619: /**
620: * Sets the menu creator for this action.
621: * <p>
622: * Note that if this method is called, it overrides the check status.
623: * </p>
624: *
625: * @param creator
626: * the menu creator, or <code>null</code> if none
627: */
628: public void setMenuCreator(IMenuCreator creator) {
629: // For backward compatibility, if the style is not
630: // set yet, then convert it to a drop down menu.
631: if (value == null) {
632: value = creator;
633: return;
634: }
635:
636: if (value instanceof IMenuCreator) {
637: value = creator == null ? VAL_DROP_DOWN_MENU : creator;
638: }
639: }
640:
641: /**
642: * Sets the text for this action.
643: * <p>
644: * Fires a property change event for the <code>TEXT</code> property if the
645: * text actually changes as a consequence.
646: * </p>
647: * <p>
648: * The accelerator is identified by the last index of a tab character. If
649: * there are no tab characters, then it is identified by the last index of a
650: * '@' character. If neither, then there is no accelerator text. Note that
651: * if you want to insert a '@' character into the text (but no accelerator,
652: * you can simply insert a '@' or a tab at the end of the text.
653: * </p>
654: *
655: * @param text
656: * the text, or <code>null</code> if none
657: */
658: public void setText(String text) {
659: String oldText = this .text;
660: int oldAccel = this .accelerator;
661: this .text = text;
662: if (text != null) {
663: String acceleratorText = LegacyActionTools
664: .extractAcceleratorText(text);
665: if (acceleratorText != null) {
666: int newAccelerator = LegacyActionTools
667: .convertLocalizedAccelerator(acceleratorText);
668: // Be sure to not wipe out the accelerator if nothing found
669: if (newAccelerator > 0) {
670: setAccelerator(newAccelerator);
671: }
672: }
673: }
674: if (!(this .accelerator == oldAccel && (oldText == null ? this .text == null
675: : oldText.equals(this .text)))) {
676: firePropertyChange(TEXT, oldText, this .text);
677: }
678: }
679:
680: /**
681: * Sets the tool tip text for this action.
682: * <p>
683: * Fires a property change event for the <code>TOOL_TIP_TEXT</code>
684: * property if the tool tip text actually changes as a consequence.
685: * </p>
686: *
687: * @param toolTipText
688: * the tool tip text, or <code>null</code> if none
689: */
690: public void setToolTipText(String toolTipText) {
691: String oldToolTipText = this.toolTipText;
692: if (!(oldToolTipText == null ? toolTipText == null
693: : oldToolTipText.equals(toolTipText))) {
694: this.toolTipText = toolTipText;
695: firePropertyChange(TOOL_TIP_TEXT, oldToolTipText,
696: toolTipText);
697: }
698: }
699:
700: }
|