Source code: org/eclipse/jface/action/Action.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jface.action;
12
13
14 import java.util.HashMap;
15 import java.util.Map;
16 import java.util.StringTokenizer;
17
18 import org.eclipse.jface.resource.ImageDescriptor;
19 import org.eclipse.jface.resource.JFaceResources;
20 import org.eclipse.jface.util.IPropertyChangeListener;
21 import org.eclipse.jface.util.ListenerList;
22 import org.eclipse.jface.util.PropertyChangeEvent;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.events.HelpListener;
25 import org.eclipse.swt.widgets.Control;
26 import org.eclipse.swt.widgets.Event;
27 import org.eclipse.swt.widgets.Menu;
28
29 /**
30 * The standard abstract implementation of an action.
31 * <p>
32 * Subclasses must implement the <code>IAction.run</code> method to carry out
33 * the action's semantics.
34 * </p>
35 */
36 public abstract class Action implements IAction {
37 /*
38 * The list of default values the action can have. These
39 * values will determine the style of the action.
40 */
41 private static final String VAL_PUSH_BTN = "PUSH_BTN"; //$NON-NLS-1$
42 private static final Integer VAL_RADIO_BTN_ON = new Integer(1);
43 private static final Integer VAL_RADIO_BTN_OFF = new Integer(0);
44 private static final Boolean VAL_TOGGLE_BTN_ON = Boolean.TRUE;
45 private static final Boolean VAL_TOGGLE_BTN_OFF = Boolean.FALSE;
46 private static final IMenuCreator VAL_DROP_DOWN_MENU = new IMenuCreator() {
47 public void dispose() {
48 // do nothing
49 }
50 public Menu getMenu(Control parent) {
51 // do nothing
52 return null;
53 }
54 public Menu getMenu(Menu parent) {
55 // do nothing
56 return null;
57 }
58 };
59
60 /**
61 * Table of key codes (key type: <code>String</code>,
62 * value type: <code>Integer</code>); <code>null</code>
63 * if not yet initialized.
64 * @see #findKeyCode
65 */
66 private static Map keyCodes = null;
67
68 /**
69 * Table of key codes (key type: <code>String</code>,
70 * value type: <code>Integer</code>); <code>null</code>
71 * if not yet initialized. The key is the localalized name
72 * of the key as it appears in menus.
73 * @see #findLocalizedKeyCode
74 */
75 private static Map localizedKeyCodes = null;
76
77 /**
78 * The localized uppercase versions of the modifer
79 * keys.
80 */
81 private static String LOCALIZED_CTRL;
82 private static String LOCALIZED_SHIFT;
83 private static String LOCALIZED_ALT;
84 private static String LOCALIZED_COMMAND;
85
86 /**
87 * Table of string representations of keys
88 * (key type: <code>Integer</code>,
89 * value type: <code>String</code>); <code>null</code>>
90 * if not yet initialized.
91 * @see #findKeyString
92 */
93 private static Map keyStrings = null;
94
95 /**
96 * List of registered listeners (element type: <code>IPropertyChangeListener</code>).
97 */
98 private ListenerList listeners = new ListenerList(3);
99
100 /**
101 * This action's text, or <code>null</code> if none.
102 */
103 private String text;
104
105 /**
106 * This action's description, or <code>null</code> if none.
107 */
108 private String description;
109
110 /**
111 * This action's id, or <code>null</code> if none.
112 */
113 private String id;
114
115 /**
116 * This action's action definition id, or <code>null</code> if none.
117 */
118 private String actionDefinitionId;
119
120 /**
121 * This action's tool tip text, or <code>null</code> if none.
122 */
123 private String toolTipText;
124
125 /**
126 * An action's help listener, or <code>null</code> if none.
127 */
128 private HelpListener helpListener;
129
130 /**
131 * This action's image, or <code>null</code> if none.
132 */
133 private ImageDescriptor image;
134
135 /**
136 * This action's hover image, or <code>null</code> if none.
137 */
138 private ImageDescriptor hoverImage;
139
140 /**
141 * This action's disabled image, or <code>null</code> if none.
142 */
143 private ImageDescriptor disabledImage;
144
145 /**
146 * Holds the action's menu creator (an IMenuCreator) or checked state (a
147 * Boolean for toggle button, or an Integer for radio button), or
148 * <code>null</code> if neither have been set.
149 * <p>
150 * The value of this field affects the value of <code>getStyle()</code>.
151 * </p>
152 */
153 private Object value = null;
154
155 /**
156 * This action's accelerator; <code>0</code> means none.
157 */
158 private int accelerator = 0;
159
160 /**
161 * Indicates this action is enabled.
162 */
163 private boolean enabled = true;
164
165 /**
166 * Creates a new action with no text and no image.
167 * <p>
168 * Configure the action later using the set methods.
169 * </p>
170 */
171 protected Action() {
172 // do nothing
173 }
174
175 /**
176 * Creates a new action with the given text and no image.
177 * Calls the zero-arg constructor, then <code>setText</code>.
178 *
179 * @param text the string used as the text for the action,
180 * or <code>null</code> if there is no text
181 * @see #setText
182 */
183 protected Action(String text) {
184 this();
185 setText(text);
186 }
187
188 /**
189 * Creates a new action with the given text and image.
190 * Calls the zero-arg constructor, then <code>setText</code> and <code>setImageDescriptor</code>.
191 *
192 * @param text the action's text, or <code>null</code> if there is no text
193 * @param image the action's image, or <code>null</code> if there is no image
194 * @see #setText
195 * @see #setImageDescriptor
196 */
197 protected Action(String text, ImageDescriptor image) {
198 this(text);
199 setImageDescriptor(image);
200 }
201
202 /**
203 * Creates a new action with the given text and style.
204 *
205 * @param text the action's text, or <code>null</code> if there is no text
206 * @param style one of <code>AS_PUSH_BUTTON</code>, <code>AS_CHECK_BOX</code>,
207 * <code>AS_DROP_DOWN_MENU</code>, <code>AS_RADIO_BUTTON</code>, and
208 * <code>AS_UNSPECIFIED</code>.
209 */
210 protected Action(String text, int style) {
211 this(text);
212 switch (style) {
213 case AS_PUSH_BUTTON :
214 value = VAL_PUSH_BTN;
215 break;
216 case AS_CHECK_BOX :
217 value = VAL_TOGGLE_BTN_OFF;
218 break;
219 case AS_DROP_DOWN_MENU :
220 value = VAL_DROP_DOWN_MENU;
221 break;
222 case AS_RADIO_BUTTON :
223 value = VAL_RADIO_BTN_OFF;
224 break;
225 }
226 }
227
228 /* (non-Javadoc)
229 * Method declared on IAction.
230 */
231 public void addPropertyChangeListener(IPropertyChangeListener listener) {
232 listeners.add(listener);
233 }
234
235 /**
236 * Parses the given accelerator text, and converts it to an accelerator key code.
237 *
238 * Support for localized modifiers is for backwards compatibility
239 * with 1.0. Use setAccelerator(int) to set accelerators programatically
240 * or the <code>accelerator</code> tag in action definitions in
241 * plugin.xml.
242 *
243 * @param acceleratorText the accelerator text localized to the current locale
244 * @return the SWT key code, or 0 if there is no accelerator
245 */
246 private static int convertLocalizedAccelerator(String acceleratorText) {
247 int accelerator = 0;
248 StringTokenizer stok = new StringTokenizer(acceleratorText, "+"); //$NON-NLS-1$
249
250 int keyCode = -1;
251
252 boolean hasMoreTokens = stok.hasMoreTokens();
253 while (hasMoreTokens) {
254 String token = stok.nextToken();
255 hasMoreTokens = stok.hasMoreTokens();
256 // Every token except the last must be one of the modifiers
257 // Ctrl, Shift, Alt, or Command
258 if (hasMoreTokens) {
259 int modifier = findLocalizedModifier(token);
260 if (modifier != 0) {
261 accelerator |= modifier;
262 } else { //Leave if there are none
263 return 0;
264 }
265 } else {
266 keyCode = findLocalizedKeyCode(token);
267 }
268 }
269 if (keyCode != -1) {
270 accelerator |= keyCode;
271 }
272 return accelerator;
273 }
274
275 /**
276 * Parses the given accelerator text, and converts it to an accelerator key code.
277 *
278 * @param acceleratorText the accelerator text
279 * @return the SWT key code, or 0 if there is no accelerator
280 */
281 public static int convertAccelerator(String acceleratorText) {
282 int accelerator = 0;
283 StringTokenizer stok = new StringTokenizer(acceleratorText, "+"); //$NON-NLS-1$
284
285 int keyCode = -1;
286
287 boolean hasMoreTokens = stok.hasMoreTokens();
288 while (hasMoreTokens) {
289 String token = stok.nextToken();
290 hasMoreTokens = stok.hasMoreTokens();
291 // Every token except the last must be one of the modifiers
292 // Ctrl, Shift, Alt, or Command
293 if (hasMoreTokens) {
294 int modifier = findModifier(token);
295 if (modifier != 0) {
296 accelerator |= modifier;
297 } else { //Leave if there are none
298 return 0;
299 }
300 } else {
301 keyCode = findKeyCode(token);
302 }
303 }
304 if (keyCode != -1) {
305 accelerator |= keyCode;
306 }
307 return accelerator;
308 }
309 /**
310 * Converts an accelerator key code to a string representation.
311 *
312 * @param keyCode the key code to be translated
313 * @return a string representation of the key code
314 */
315 public static String convertAccelerator(int keyCode) {
316 String modifier = getModifierString(keyCode);
317 String fullKey;
318 if (modifier.equals("")) { //$NON-NLS-1$
319 fullKey = findKeyString(keyCode);
320 } else {
321 fullKey = modifier + "+" + findKeyString(keyCode); //$NON-NLS-1$
322 }
323 return fullKey;
324 }
325 /*
326 * Returns the string representation of the modifiers (Ctrl, Alt, Shift, Command)
327 * of the key event.
328 */
329 private static String getModifierString(int keyCode) {
330 String modString = ""; //$NON-NLS-1$
331
332 if ((keyCode & SWT.CTRL) != 0) {
333 modString = findModifierString(keyCode & SWT.CTRL);
334 }
335
336 if ((keyCode & SWT.ALT) != 0) {
337 if (modString.equals("")) { //$NON-NLS-1$
338 modString = findModifierString(keyCode & SWT.ALT);
339 } else {
340 modString = modString + "+" + findModifierString(keyCode & SWT.ALT); //$NON-NLS-1$
341 }
342 }
343
344 if ((keyCode & SWT.SHIFT) != 0) {
345 if (modString.equals("")) { //$NON-NLS-1$
346 modString = findModifierString(keyCode & SWT.SHIFT);
347 } else {
348 modString = modString + "+" + findModifierString(keyCode & SWT.SHIFT); //$NON-NLS-1$
349 }
350 }
351
352 if ((keyCode & SWT.COMMAND) != 0) {
353 if (modString.equals("")) { //$NON-NLS-1$
354 modString = findModifierString(keyCode & SWT.COMMAND);
355 } else {
356 modString = modString + "+" + findModifierString(keyCode & SWT.COMMAND); //$NON-NLS-1$
357 }
358 }
359
360 return modString;
361 }
362 /**
363 * Extracts the accelerator text from the given text.
364 * Returns <code>null</code> if there is no accelerator text,
365 * and the empty string if there is no text after the accelerator delimeter (tab or '@').
366 *
367 * @param text the text for the action
368 * @return the accelerator text, or <code>null</code>
369 */
370 private static String extractAcceleratorText(String text) {
371 int index = text.lastIndexOf('\t');
372 if (index == -1)
373 index = text.lastIndexOf('@');
374 if (index >= 0)
375 return text.substring(index + 1);
376 return null;
377 }
378 /**
379 * Maps a standard keyboard key name to an SWT key code.
380 * Key names are converted to upper case before comparison.
381 * If the key name is a single letter, for example "S", its character code is returned.
382 * <p>
383 * The following key names are known (case is ignored):
384 * <ul>
385 * <li><code>"BACKSPACE"</code></li>
386 * <li><code>"TAB"</code></li>
387 * <li><code>"RETURN"</code></li>
388 * <li><code>"ENTER"</code></li>
389 * <li><code>"ESC"</code></li>
390 * <li><code>"ESCAPE"</code></li>
391 * <li><code>"DELETE"</code></li>
392 * <li><code>"SPACE"</code></li>
393 * <li><code>"ARROW_UP"</code>, <code>"ARROW_DOWN"</code>,
394 * <code>"ARROW_LEFT"</code>, and <code>"ARROW_RIGHT"</code></li>
395 * <li><code>"PAGE_UP"</code> and <code>"PAGE_DOWN"</code></li>
396 * <li><code>"HOME"</code></li>
397 * <li><code>"END"</code></li>
398 * <li><code>"INSERT"</code></li>
399 * <li><code>"F1"</code>, <code>"F2"</code> through <code>"F12"</code></li>
400 * </ul>
401 * </p>
402 *
403 * @param token the key name
404 * @return the SWT key code, <code>-1</code> if no match was found
405 * @see SWT
406 */
407 public static int findKeyCode(String token) {
408 if (keyCodes == null)
409 initKeyCodes();
410 token = token.toUpperCase();
411 Integer i = (Integer) keyCodes.get(token);
412 if (i != null)
413 return i.intValue();
414 if (token.length() == 1)
415 return token.charAt(0);
416 return -1;
417 }
418
419 /**
420 * Find the supplied code for a localized key. As
421 * #findKeyCode but localized to the current locale.
422 *
423 * Support for localized modifiers is for backwards compatibility
424 * with 1.0. Use setAccelerator(int) to set accelerators programatically
425 * or the <code>accelerator</code> tag in action definitions in
426 * plugin.xml.
427 *
428 * @param token the localized key name
429 * @return the SWT key code, <code>-1</code> if no match was found
430 * @see #findKeyCode
431 */
432 private static int findLocalizedKeyCode(String token) {
433 if (localizedKeyCodes == null)
434 initLocalizedKeyCodes();
435 token = token.toUpperCase();
436 Integer i = (Integer) localizedKeyCodes.get(token);
437 if (i != null)
438 return i.intValue();
439 if (token.length() == 1)
440 return token.charAt(0);
441 return -1;
442 }
443 /**
444 * Maps an SWT key code to a standard keyboard key name. The key code is
445 * stripped of modifiers (SWT.CTRL, SWT.ALT, SWT.SHIFT, and SWT.COMMAND). If the key code is
446 * not an SWT code (for example if it a key code for the key 'S'), a string
447 * containing a character representation of the key code is returned.
448 *
449 * @param keyCode the key code to be translated
450 * @return the string representation of the key code
451 * @see SWT
452 * @since 2.0
453 */
454 public static String findKeyString(int keyCode) {
455 if (keyStrings == null)
456 initKeyStrings();
457 int i = keyCode & ~(SWT.CTRL | SWT.ALT | SWT.SHIFT | SWT.COMMAND);
458 Integer integer = new Integer(i);
459 String result = (String) keyStrings.get(integer);
460 if (result != null)
461 return result;
462 result = new String(new char[] {(char) i });
463 return result;
464 }
465 /**
466 * Maps standard keyboard modifier key names to the corresponding
467 * SWT modifier bit. The following modifier key names are recognized
468 * (case is ignored): <code>"CTRL"</code>, <code>"SHIFT"</code>,
469 * <code>"ALT"</code>, and <code>"COMMAND"</code>.
470 * The given modifier key name is converted to upper case before comparison.
471 *
472 * @param token the modifier key name
473 * @return the SWT modifier bit, or <code>0</code> if no match was found
474 * @see SWT
475 */
476 public static int findModifier(String token) {
477 token = token.toUpperCase();
478 if (token.equals("CTRL")) //$NON-NLS-1$
479 return SWT.CTRL;
480 if (token.equals("SHIFT")) //$NON-NLS-1$
481 return SWT.SHIFT;
482 if (token.equals("ALT")) //$NON-NLS-1$
483 return SWT.ALT;
484 if (token.equals("COMMAND")) //$NON-NLS-1$
485 return SWT.COMMAND;
486 return 0;
487 }
488
489 /**
490 * Maps the localized modifier names to a code in the same
491 * manner as #findModifier.
492 *
493 * Support for localized modifiers is for backwards compatibility
494 * with 1.0. Use setAccelerator(int) to set accelerators programatically
495 * or the <code>accelerator</code> tag in action definitions in
496 * plugin.xml.
497 *
498 * @see findModifier
499 */
500 private static int findLocalizedModifier(String token) {
501 if (LOCALIZED_CTRL == null)
502 initLocalizedModifiers();
503
504 token = token.toUpperCase();
505 if (token.equals(LOCALIZED_CTRL)) //$NON-NLS-1$
506 return SWT.CTRL;
507 if (token.equals(LOCALIZED_SHIFT)) //$NON-NLS-1$
508 return SWT.SHIFT;
509 if (token.equals(LOCALIZED_ALT)) //$NON-NLS-1$
510 return SWT.ALT;
511 if (token.equals(LOCALIZED_COMMAND)) //$NON-NLS-1$
512 return SWT.COMMAND;
513 return 0;
514 }
515
516 /**
517 * Initialize the list of localized modifiers
518 */
519 private static void initLocalizedModifiers() {
520 LOCALIZED_CTRL = JFaceResources.getString("Ctrl").toUpperCase(); //$NON-NLS-1$
521 LOCALIZED_SHIFT = JFaceResources.getString("Shift").toUpperCase(); //$NON-NLS-1$
522 LOCALIZED_ALT = JFaceResources.getString("Alt").toUpperCase(); //$NON-NLS-1$
523 LOCALIZED_COMMAND = JFaceResources.getString("Command").toUpperCase(); //$NON-NLS-1$
524 }
525
526 /**
527 * Returns a string representation of an SWT modifier bit (SWT.CTRL,
528 * SWT.ALT, SWT.SHIFT, and SWT.COMMAND). Returns <code>null</code> if the key code
529 * is not an SWT modifier bit.
530 *
531 * @param keyCode the SWT modifier bit to be translated
532 * @return the string representation of the SWT modifier bit, or <code>null</code> if the key code was not an SWT modifier bit
533 * @see SWT
534 * @since 2.0
535 */
536 public static String findModifierString(int keyCode) {
537 if (keyCode == SWT.CTRL)
538 return JFaceResources.getString("Ctrl"); //$NON-NLS-1$
539 if (keyCode == SWT.ALT)
540 return JFaceResources.getString("Alt"); //$NON-NLS-1$
541 if (keyCode == SWT.SHIFT)
542 return JFaceResources.getString("Shift"); //$NON-NLS-1$
543 if (keyCode == SWT.COMMAND)
544 return JFaceResources.getString("Command"); //$NON-NLS-1$
545 return null;
546 }
547 /**
548 * Notifies any property change listeners that a property has changed.
549 * Only listeners registered at the time this method is called are notified.
550 * This method avoids creating an event object if there are no listeners registered,
551 * but calls <code>firePropertyChange(PropertyChangeEvent)</code> if there are.
552 *
553 * @param propertyName the name of the property that has changed
554 * @param oldValue the old value of the property, or <code>null</code> if none
555 * @param newValue the new value of the property, or <code>null</code> if none
556 *
557 * @see IPropertyChangeListener#propertyChange
558 */
559 protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
560 if (!listeners.isEmpty()) {
561 firePropertyChange(new PropertyChangeEvent(this, propertyName, oldValue, newValue));
562 }
563 }
564 /**
565 * Notifies any property change listeners that a property has changed.
566 * Only listeners registered at the time this method is called are notified.
567 *
568 * @param event the property change event
569 *
570 * @see IPropertyChangeListener#propertyChange
571 */
572 protected void firePropertyChange(PropertyChangeEvent event) {
573 Object[] list = this.listeners.getListeners();
574 for (int i = 0; i < list.length; ++i) {
575 ((IPropertyChangeListener) list[i]).propertyChange(event);
576 }
577 }
578 /* (non-Javadoc)
579 * Method declared on IAction.
580 */
581 public int getAccelerator() {
582 return accelerator;
583 }
584
585 /*(non-Javadoc)
586 * Method declared on IAction.
587 *
588 */
589 public String getActionDefinitionId() {
590 return actionDefinitionId;
591 }
592
593 /* (non-Javadoc)
594 * Method declared on IAction.
595 */
596 public String getDescription() {
597 if (description != null)
598 return description;
599 return getToolTipText();
600 }
601 /* (non-Javadoc)
602 * Method declared on IAction.
603 */
604 public ImageDescriptor getDisabledImageDescriptor() {
605 return disabledImage;
606 }
607 /* (non-Javadoc)
608 * Method declared on IAction.
609 */
610 public HelpListener getHelpListener() {
611 return helpListener;
612 }
613 /* (non-Javadoc)
614 * Method declared on IAction.
615 */
616 public ImageDescriptor getHoverImageDescriptor() {
617 return hoverImage;
618 }
619 /* (non-Javadoc)
620 * Method declared on IAction.
621 */
622 public String getId() {
623 return id;
624 }
625 /* (non-Javadoc)
626 * Method declared on IAction.
627 */
628 public ImageDescriptor getImageDescriptor() {
629 return image;
630 }
631 /* (non-Javadoc)
632 * Method declared on IAction.
633 */
634 public IMenuCreator getMenuCreator() {
635 // The default drop down menu value is only used
636 // to mark this action requested style. So do not
637 // return it. For backward compatibility reasons.
638 if (value == VAL_DROP_DOWN_MENU)
639 return null;
640 if (value instanceof IMenuCreator)
641 return (IMenuCreator) value;
642 return null;
643 }
644 /* (non-Javadoc)
645 * Method declared on IAction.
646 */
647 public int getStyle() {
648 // Infer the style from the value field.
649 if (value == VAL_PUSH_BTN || value == null)
650 return AS_PUSH_BUTTON;
651 if (value == VAL_TOGGLE_BTN_ON || value == VAL_TOGGLE_BTN_OFF)
652 return AS_CHECK_BOX;
653 if (value == VAL_RADIO_BTN_ON || value == VAL_RADIO_BTN_OFF)
654 return AS_RADIO_BUTTON;
655 if (value instanceof IMenuCreator)
656 return AS_DROP_DOWN_MENU;
657
658 // We should never get to this line...
659 return AS_PUSH_BUTTON;
660 }
661 /* (non-Javadoc)
662 * Method declared on IAction.
663 */
664 public String getText() {
665 return text;
666 }
667 /* (non-Javadoc)
668 * Method declared on IAction.
669 */
670 public String getToolTipText() {
671 return toolTipText;
672 }
673 /**
674 * Initializes the internal key code table.
675 */
676 private static void initKeyCodes() {
677 keyCodes = new HashMap(40);
678
679 keyCodes.put("BACKSPACE", new Integer(8)); //$NON-NLS-1$
680 keyCodes.put("TAB", new Integer(9)); //$NON-NLS-1$
681 keyCodes.put("RETURN", new Integer(13)); //$NON-NLS-1$
682 keyCodes.put("ENTER", new Integer(13)); //$NON-NLS-1$
683 keyCodes.put("ESCAPE", new Integer(27)); //$NON-NLS-1$
684 keyCodes.put("ESC", new Integer(27)); //$NON-NLS-1$
685 keyCodes.put("DELETE", new Integer(127)); //$NON-NLS-1$
686
687 keyCodes.put("SPACE", new Integer(' ')); //$NON-NLS-1$
688 keyCodes.put("ARROW_UP", new Integer(SWT.ARROW_UP)); //$NON-NLS-1$
689 keyCodes.put("ARROW_DOWN", new Integer(SWT.ARROW_DOWN)); //$NON-NLS-1$
690 keyCodes.put("ARROW_LEFT", new Integer(SWT.ARROW_LEFT)); //$NON-NLS-1$
691 keyCodes.put("ARROW_RIGHT", new Integer(SWT.ARROW_RIGHT)); //$NON-NLS-1$
692 keyCodes.put("PAGE_UP", new Integer(SWT.PAGE_UP)); //$NON-NLS-1$
693 keyCodes.put("PAGE_DOWN", new Integer(SWT.PAGE_DOWN)); //$NON-NLS-1$
694 keyCodes.put("HOME", new Integer(SWT.HOME)); //$NON-NLS-1$
695 keyCodes.put("END", new Integer(SWT.END)); //$NON-NLS-1$
696 keyCodes.put("INSERT", new Integer(SWT.INSERT)); //$NON-NLS-1$
697 keyCodes.put("F1", new Integer(SWT.F1)); //$NON-NLS-1$
698 keyCodes.put("F2", new Integer(SWT.F2)); //$NON-NLS-1$
699 keyCodes.put("F3", new Integer(SWT.F3)); //$NON-NLS-1$
700 keyCodes.put("F4", new Integer(SWT.F4)); //$NON-NLS-1$
701 keyCodes.put("F5", new Integer(SWT.F5)); //$NON-NLS-1$
702 keyCodes.put("F6", new Integer(SWT.F6)); //$NON-NLS-1$
703 keyCodes.put("F7", new Integer(SWT.F7)); //$NON-NLS-1$
704 keyCodes.put("F8", new Integer(SWT.F8)); //$NON-NLS-1$
705 keyCodes.put("F9", new Integer(SWT.F9)); //$NON-NLS-1$
706 keyCodes.put("F10", new Integer(SWT.F10)); //$NON-NLS-1$
707 keyCodes.put("F11", new Integer(SWT.F11)); //$NON-NLS-1$
708 keyCodes.put("F12", new Integer(SWT.F12)); //$NON-NLS-1$
709 }
710
711 /**
712 * Initializes the localized internal key code table.
713 */
714 private static void initLocalizedKeyCodes() {
715 localizedKeyCodes = new HashMap(40);
716
717 localizedKeyCodes.put(JFaceResources.getString("Backspace").toUpperCase(), new Integer(8)); //$NON-NLS-1$
718 localizedKeyCodes.put(JFaceResources.getString("Tab").toUpperCase(), new Integer(9)); //$NON-NLS-1$
719 localizedKeyCodes.put(JFaceResources.getString("Return").toUpperCase(), new Integer(13)); //$NON-NLS-1$
720 localizedKeyCodes.put(JFaceResources.getString("Enter").toUpperCase(), new Integer(13)); //$NON-NLS-1$
721 localizedKeyCodes.put(JFaceResources.getString("Escape").toUpperCase(), new Integer(27)); //$NON-NLS-1$
722 localizedKeyCodes.put(JFaceResources.getString("Esc").toUpperCase(), new Integer(27)); //$NON-NLS-1$
723 localizedKeyCodes.put(JFaceResources.getString("Delete").toUpperCase(), new Integer(127)); //$NON-NLS-1$
724
725 localizedKeyCodes.put(JFaceResources.getString("Space").toUpperCase(), new Integer(' ')); //$NON-NLS-1$
726
727 localizedKeyCodes.put(JFaceResources.getString("Arrow_Up").toUpperCase(), new Integer(SWT.ARROW_UP)); //$NON-NLS-1$
728 localizedKeyCodes.put(JFaceResources.getString("Arrow_Down").toUpperCase(), new Integer(SWT.ARROW_DOWN)); //$NON-NLS-1$
729 localizedKeyCodes.put(JFaceResources.getString("Arrow_Left").toUpperCase(), new Integer(SWT.ARROW_LEFT)); //$NON-NLS-1$
730 localizedKeyCodes.put(JFaceResources.getString("Arrow_Right").toUpperCase(), new Integer(SWT.ARROW_RIGHT)); //$NON-NLS-1$
731 localizedKeyCodes.put(JFaceResources.getString("Page_Up").toUpperCase(), new Integer(SWT.PAGE_UP)); //$NON-NLS-1$
732 localizedKeyCodes.put(JFaceResources.getString("Page_Down").toUpperCase(), new Integer(SWT.PAGE_DOWN)); //$NON-NLS-1$
733 localizedKeyCodes.put(JFaceResources.getString("Home").toUpperCase(), new Integer(SWT.HOME)); //$NON-NLS-1$
734 localizedKeyCodes.put(JFaceResources.getString("End").toUpperCase(), new Integer(SWT.END)); //$NON-NLS-1$
735 localizedKeyCodes.put(JFaceResources.getString("Insert").toUpperCase(), new Integer(SWT.INSERT)); //$NON-NLS-1$
736 localizedKeyCodes.put(JFaceResources.getString("F1").toUpperCase(), new Integer(SWT.F1)); //$NON-NLS-1$
737 localizedKeyCodes.put(JFaceResources.getString("F2").toUpperCase(), new Integer(SWT.F2)); //$NON-NLS-1$
738 localizedKeyCodes.put(JFaceResources.getString("F3").toUpperCase(), new Integer(SWT.F3)); //$NON-NLS-1$
739 localizedKeyCodes.put(JFaceResources.getString("F4").toUpperCase(), new Integer(SWT.F4)); //$NON-NLS-1$
740 localizedKeyCodes.put(JFaceResources.getString("F5").toUpperCase(), new Integer(SWT.F5)); //$NON-NLS-1$
741 localizedKeyCodes.put(JFaceResources.getString("F6").toUpperCase(), new Integer(SWT.F6)); //$NON-NLS-1$
742 localizedKeyCodes.put(JFaceResources.getString("F7").toUpperCase(), new Integer(SWT.F7)); //$NON-NLS-1$
743 localizedKeyCodes.put(JFaceResources.getString("F8").toUpperCase(), new Integer(SWT.F8)); //$NON-NLS-1$
744 localizedKeyCodes.put(JFaceResources.getString("F9").toUpperCase(), new Integer(SWT.F9)); //$NON-NLS-1$
745 localizedKeyCodes.put(JFaceResources.getString("F10").toUpperCase(), new Integer(SWT.F10)); //$NON-NLS-1$
746 localizedKeyCodes.put(JFaceResources.getString("F11").toUpperCase(), new Integer(SWT.F11)); //$NON-NLS-1$
747 localizedKeyCodes.put(JFaceResources.getString("F12").toUpperCase(), new Integer(SWT.F12)); //$NON-NLS-1$
748 }
749
750 /**
751 * Initializes the internal key string table.
752 */
753 private static void initKeyStrings() {
754 keyStrings = new HashMap(40);
755
756 keyStrings.put(new Integer(8), JFaceResources.getString("Backspace")); //$NON-NLS-1$
757 keyStrings.put(new Integer(9), JFaceResources.getString("Tab")); //$NON-NLS-1$
758 keyStrings.put(new Integer(13), JFaceResources.getString("Return")); //$NON-NLS-1$
759 keyStrings.put(new Integer(13), JFaceResources.getString("Enter")); //$NON-NLS-1$
760 keyStrings.put(new Integer(27), JFaceResources.getString("Escape")); //$NON-NLS-1$
761 keyStrings.put(new Integer(27), JFaceResources.getString("Esc")); //$NON-NLS-1$
762 keyStrings.put(new Integer(127), JFaceResources.getString("Delete")); //$NON-NLS-1$
763
764 keyStrings.put(new Integer(' '), JFaceResources.getString("Space")); //$NON-NLS-1$
765
766 keyStrings.put(new Integer(SWT.ARROW_UP), JFaceResources.getString("Arrow_Up")); //$NON-NLS-1$
767 keyStrings.put(new Integer(SWT.ARROW_DOWN), JFaceResources.getString("Arrow_Down")); //$NON-NLS-1$
768 keyStrings.put(new Integer(SWT.ARROW_LEFT), JFaceResources.getString("Arrow_Left")); //$NON-NLS-1$
769 keyStrings.put(new Integer(SWT.ARROW_RIGHT), JFaceResources.getString("Arrow_Right")); //$NON-NLS-1$
770 keyStrings.put(new Integer(SWT.PAGE_UP), JFaceResources.getString("Page_Up")); //$NON-NLS-1$
771 keyStrings.put(new Integer(SWT.PAGE_DOWN), JFaceResources.getString("Page_Down")); //$NON-NLS-1$
772 keyStrings.put(new Integer(SWT.HOME), JFaceResources.getString("Home")); //$NON-NLS-1$
773 keyStrings.put(new Integer(SWT.END), JFaceResources.getString("End")); //$NON-NLS-1$
774 keyStrings.put(new Integer(SWT.INSERT), JFaceResources.getString("Insert")); //$NON-NLS-1$
775 keyStrings.put(new Integer(SWT.F1), JFaceResources.getString("F1")); //$NON-NLS-1$
776 keyStrings.put(new Integer(SWT.F2), JFaceResources.getString("F2")); //$NON-NLS-1$
777 keyStrings.put(new Integer(SWT.F3), JFaceResources.getString("F3")); //$NON-NLS-1$
778 keyStrings.put(new Integer(SWT.F4), JFaceResources.getString("F4")); //$NON-NLS-1$
779 keyStrings.put(new Integer(SWT.F5), JFaceResources.getString("F5")); //$NON-NLS-1$
780 keyStrings.put(new Integer(SWT.F6), JFaceResources.getString("F6")); //$NON-NLS-1$
781 keyStrings.put(new Integer(SWT.F7), JFaceResources.getString("F7")); //$NON-NLS-1$
782 keyStrings.put(new Integer(SWT.F8), JFaceResources.getString("F8")); //$NON-NLS-1$
783 keyStrings.put(new Integer(SWT.F9), JFaceResources.getString("F9")); //$NON-NLS-1$
784 keyStrings.put(new Integer(SWT.F10), JFaceResources.getString("F10")); //$NON-NLS-1$
785 keyStrings.put(new Integer(SWT.F11), JFaceResources.getString("F11")); //$NON-NLS-1$
786 keyStrings.put(new Integer(SWT.F12), JFaceResources.getString("F12")); //$NON-NLS-1$
787 }
788 /* (non-Javadoc)
789 * Method declared on IAction.
790 */
791 public boolean isChecked() {
792 return value == VAL_TOGGLE_BTN_ON || value == VAL_RADIO_BTN_ON;
793 }
794 /* (non-Javadoc)
795 * Method declared on IAction.
796 */
797 public boolean isEnabled() {
798 return enabled;
799 }
800 /**
801 * Convenience method for removing any optional accelerator text from the given string.
802 * The accelerator text appears at the end of the text, and is separated
803 * from the main part by a single tab character <code>'\t'</code>.
804 *
805 * @param text the text
806 * @return the text sans accelerator
807 */
808 public static String removeAcceleratorText(String text) {
809 int index = text.lastIndexOf('\t');
810 if (index == -1)
811 index = text.lastIndexOf('@');
812 if (index >= 0)
813 return text.substring(0, index);
814 return text;
815 }
816
817 /**
818 * Convenience method for removing any mnemonics from the given string.
819 * For example, <code>removeMnemonics("&Open")</code> will return <code>"Open"</code>.
820 *
821 * @param text the text
822 * @return the text sans mnemonics
823 *
824 * @since 3.0
825 */
826 public static String removeMnemonics(String text) {
827 int index = text.indexOf('&');
828 if (index == -1) {
829 return text;
830 }
831 int len = text.length();
832 StringBuffer sb = new StringBuffer(len);
833 int lastIndex = 0;
834 while (index != -1) {
835 // ignore & at the end
836 if (index == len - 1) {
837 break;
838 }
839 // handle the && case
840 if (text.charAt(index + 1) == '&') {
841 ++index;
842 }
843 sb.append(text.substring(lastIndex, index));
844 // skip the &
845 ++index;
846 lastIndex = index;
847 index = text.indexOf('&', index);
848 }
849 if (lastIndex < len) {
850 sb.append(text.substring(lastIndex, len));
851 }
852 return sb.toString();
853 }
854
855 /* (non-Javadoc)
856 * Method declared on IAction.
857 */
858 public void removePropertyChangeListener(IPropertyChangeListener listener) {
859 listeners.remove(listener);
860 }
861
862 /**
863 * The default implementation of this <code>IAction</code> method
864 * does nothing. Subclasses should override this method
865 * if they do not need information from the triggering event,
866 * or override <code>runWithEvent(Event)</code> if they do.
867 */
868 public void run() {
869 // do nothing
870 }
871
872 /**
873 * The default implementation of this <code>IAction</code> method
874 * ignores the event argument, and simply calls <code>run()</code>.
875 * Subclasses should override this method if they need information
876 * from the triggering event, or override <code>run()</code> if not.
877 *
878 * @since 2.0
879 */
880 public void runWithEvent(Event event) {
881 run();
882 }
883
884 /* (non-Javadoc)
885 * Method declared on IAction.
886 */
887 public void setActionDefinitionId(String id) {
888 actionDefinitionId = id;
889 }
890 /* (non-Javadoc)
891 * Method declared on IAction.
892 */
893 public void setChecked(boolean checked) {
894 Object newValue = null;
895
896 // For backward compatibility, if the style is not
897 // set yet, then convert it to a toggle button.
898 if (value == null || value == VAL_TOGGLE_BTN_ON || value == VAL_TOGGLE_BTN_OFF) {
899 newValue = checked ? VAL_TOGGLE_BTN_ON : VAL_TOGGLE_BTN_OFF;
900 } else if (value == VAL_RADIO_BTN_ON || value == VAL_RADIO_BTN_OFF) {
901 newValue = checked ? VAL_RADIO_BTN_ON : VAL_RADIO_BTN_OFF;
902 } else {
903 // Some other style already, so do nothing.
904 return;
905 }
906
907 if (newValue != value) {
908 value = newValue;
909 if (checked)
910 firePropertyChange(CHECKED, Boolean.FALSE, Boolean.TRUE);
911 else
912 firePropertyChange(CHECKED, Boolean.TRUE, Boolean.FALSE);
913 }
914 }
915 /* (non-Javadoc)
916 * Method declared on IAction.
917 */
918 public void setDescription(String text) {
919
920 if ((description == null && text != null) || (description != null && text == null) || (description != null && text != null && !text.equals(description))) {
921 String oldDescription = description;
922 description = text;
923 firePropertyChange(DESCRIPTION, oldDescription, description);
924 }
925 }
926 /* (non-Javadoc)
927 * Method declared on IAction.
928 */
929 public void setDisabledImageDescriptor(ImageDescriptor newImage) {
930 if (disabledImage != newImage) {
931 ImageDescriptor oldImage = disabledImage;
932 disabledImage = newImage;
933 firePropertyChange(IMAGE, oldImage, newImage);
934 }
935 }
936 /* (non-Javadoc)
937 * Method declared on IAction.
938 */
939 public void setEnabled(boolean enabled) {
940 if (enabled != this.enabled) {
941 Boolean oldVal = this.enabled ? Boolean.TRUE : Boolean.FALSE;
942 Boolean newVal = enabled ? Boolean.TRUE : Boolean.FALSE;
943 this.enabled = enabled;
944 firePropertyChange(ENABLED, oldVal, newVal);
945 }
946 }
947 /* (non-Javadoc)
948 * Method declared on IAction.
949 */
950 public void setHelpListener(HelpListener listener) {
951 helpListener = listener;
952 }
953 /* (non-Javadoc)
954 * Method declared on IAction.
955 */
956 public void setHoverImageDescriptor(ImageDescriptor newImage) {
957 if (hoverImage != newImage) {
958 ImageDescriptor oldImage = hoverImage;
959 hoverImage = newImage;
960 firePropertyChange(IMAGE, oldImage, newImage);
961 }
962 }
963 /* (non-Javadoc)
964 * Method declared on IAction.
965 */
966 public void setId(String id) {
967 this.id = id;
968 }
969 /* (non-Javadoc)
970 * Method declared on IAction.
971 */
972 public void setImageDescriptor(ImageDescriptor newImage) {
973 if (image != newImage) {
974 ImageDescriptor oldImage = image;
975 image = newImage;
976 firePropertyChange(IMAGE, oldImage, newImage);
977 }
978 }
979 /**
980 * Sets the menu creator for this action.
981 * <p>
982 * Note that if this method is called, it overrides the check status.
983 * </p>
984 *
985 * @param creator the menu creator, or <code>null</code> if none
986 */
987 public void setMenuCreator(IMenuCreator creator) {
988 // For backward compatibility, if the style is not
989 // set yet, then convert it to a drop down menu.
990 if (value == null) {
991 value = creator;
992 return;
993 }
994
995 if (value instanceof IMenuCreator)
996 value = creator == null ? VAL_DROP_DOWN_MENU : creator;
997 }
998 /**
999 * Sets the text for this action.
1000 * <p>
1001 * Fires a property change event for the <code>TEXT</code> property
1002 * if the text actually changes as a consequence.
1003 * </p>
1004 * <p>
1005 * The accelerator is identified by the last index of a tab character. If
1006 * there are no tab characters, then it is identified by the last index of
1007 * a '@' character. If neither, then there is no accelerator text. Note
1008 * that if you want to insert a '@' character into the text (but no
1009 * accelerator, you can simply insert a '@' or a tab at the end of the text.
1010 * </p>
1011 *
1012 * @param text the text, or <code>null</code> if none
1013 */
1014 public void setText(String text) {
1015 String oldText = this.text;
1016 int oldAccel = this.accelerator;
1017 this.text = text;
1018 if (text != null) {
1019 String acceleratorText = extractAcceleratorText(text);
1020 if (acceleratorText != null) {
1021 int newAccelerator = convertLocalizedAccelerator(acceleratorText);
1022 //Be sure to not wipe out the accelerator if nothing found
1023 if (newAccelerator > 0) {
1024 setAccelerator(newAccelerator);
1025 }
1026 }
1027 }
1028 if (!(this.accelerator == oldAccel && (oldText == null ? this.text == null : oldText.equals(this.text)))) {
1029 firePropertyChange(TEXT, oldText, this.text);
1030 }
1031 }
1032 /**
1033 * Sets the tool tip text for this action.
1034 * <p>
1035 * Fires a property change event for the <code>TOOL_TIP_TEXT</code> property
1036 * if the tool tip text actually changes as a consequence.
1037 * </p>
1038 *
1039 * @param toolTipText the tool tip text, or <code>null</code> if none
1040 */
1041 public void setToolTipText(String toolTipText) {
1042 String oldToolTipText = this.toolTipText;
1043 if (!(oldToolTipText == null ? toolTipText == null : oldToolTipText.equals(toolTipText))) {
1044 this.toolTipText = toolTipText;
1045 firePropertyChange(TOOL_TIP_TEXT, oldToolTipText, toolTipText);
1046 }
1047 }
1048 /*
1049 * @see IAction#setAccelerator(int)
1050 */
1051 public void setAccelerator(int keycode) {
1052 this.accelerator = keycode;
1053 }
1054
1055 /**
1056 * Reports the outcome of the running of this action via the
1057 * {@link IAction#RESULT} property.
1058 *
1059 * @param success <code>true</code> if the action succeeded and
1060 * <code>false</code> if the action failed or was not completed
1061 * @see IAction#RESULT
1062 * @since 3.0
1063 */
1064 public final void notifyResult(boolean success) {
1065 firePropertyChange(RESULT, null, Boolean.valueOf(success));
1066 }
1067
1068}