1 /******************************************************************************* 2 * Copyright (c) 2000, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jface.action; 15 16 import org.eclipse.jface.resource.ImageDescriptor; 17 import org.eclipse.swt.events.HelpListener; 18 import org.eclipse.swt.widgets.Control; 19 import org.eclipse.swt.widgets.Event; 20 import org.eclipse.swt.widgets.Menu; 21 22 /** 23 * The standard abstract implementation of an action. 24 * <p> 25 * Subclasses must implement the <code>IAction.run</code> method to carry out 26 * the action's semantics. 27 * </p> 28 */ 29 public abstract class Action extends AbstractAction { 30 31 private static final IMenuCreator VAL_DROP_DOWN_MENU = new IMenuCreator() { 32 @Override 33 public void dispose() { 34 // do nothing 35 } 36 37 @Override 38 public Menu getMenu(Control parent) { 39 // do nothing 40 return null; 41 } 42 43 @Override 44 public Menu getMenu(Menu parent) { 45 // do nothing 46 return null; 47 } 48 }; 49 50 /* 51 * The list of default values the action can have. These values will 52 * determine the style of the action. 53 */ 54 private static final String VAL_PUSH_BTN = "PUSH_BTN"; //$NON-NLS-1$ 55 56 private static final Integer VAL_RADIO_BTN_OFF = Integer.valueOf(0); 57 58 private static final Integer VAL_RADIO_BTN_ON = Integer.valueOf(1); 59 60 private static final Boolean VAL_TOGGLE_BTN_OFF = Boolean.FALSE; 61 62 private static final Boolean VAL_TOGGLE_BTN_ON = Boolean.TRUE; 63 64 /** 65 * Converts an accelerator key code to a string representation. 66 * 67 * @param keyCode 68 * the key code to be translated 69 * @return a string representation of the key code 70 */ convertAccelerator(int keyCode)71 public static String convertAccelerator(int keyCode) { 72 return LegacyActionTools.convertAccelerator(keyCode); 73 } 74 75 /** 76 * Parses the given accelerator text, and converts it to an accelerator key 77 * code. 78 * 79 * @param acceleratorText 80 * the accelerator text 81 * @return the SWT key code, or 0 if there is no accelerator 82 */ convertAccelerator(String acceleratorText)83 public static int convertAccelerator(String acceleratorText) { 84 return LegacyActionTools.convertAccelerator(acceleratorText); 85 } 86 87 /** 88 * Maps a standard keyboard key name to an SWT key code. Key names are converted 89 * to upper case before comparison. If the key name is a single letter, for 90 * example "S", its character code is returned. 91 * <p> 92 * The following key names are known (case is ignored): 93 * </p> 94 * <ul> 95 * <li><code>"BACKSPACE"</code></li> 96 * <li><code>"TAB"</code></li> 97 * <li><code>"RETURN"</code></li> 98 * <li><code>"ENTER"</code></li> 99 * <li><code>"ESC"</code></li> 100 * <li><code>"ESCAPE"</code></li> 101 * <li><code>"DELETE"</code></li> 102 * <li><code>"SPACE"</code></li> 103 * <li><code>"ARROW_UP"</code>, <code>"ARROW_DOWN"</code>, 104 * <code>"ARROW_LEFT"</code>, and <code>"ARROW_RIGHT"</code></li> 105 * <li><code>"PAGE_UP"</code> and <code>"PAGE_DOWN"</code></li> 106 * <li><code>"HOME"</code></li> 107 * <li><code>"END"</code></li> 108 * <li><code>"INSERT"</code></li> 109 * <li><code>"F1"</code>, <code>"F2"</code> through <code>"F12"</code></li> 110 * </ul> 111 * 112 * @param token the key name 113 * @return the SWT key code, <code>-1</code> if no match was found 114 * @see org.eclipse.swt.SWT 115 */ findKeyCode(String token)116 public static int findKeyCode(String token) { 117 return LegacyActionTools.findKeyCode(token); 118 } 119 120 /** 121 * Maps an SWT key code to a standard keyboard key name. The key code is 122 * stripped of modifiers (SWT.CTRL, SWT.ALT, SWT.SHIFT, and SWT.COMMAND). If 123 * the key code is not an SWT code (for example if it a key code for the key 124 * 'S'), a string containing a character representation of the key code is 125 * returned. 126 * 127 * @param keyCode 128 * the key code to be translated 129 * @return the string representation of the key code 130 * @see org.eclipse.swt.SWT 131 * @since 2.0 132 */ findKeyString(int keyCode)133 public static String findKeyString(int keyCode) { 134 return LegacyActionTools.findKeyString(keyCode); 135 } 136 137 /** 138 * Maps standard keyboard modifier key names to the corresponding SWT 139 * modifier bit. The following modifier key names are recognized (case is 140 * ignored): <code>"CTRL"</code>, <code>"SHIFT"</code>, 141 * <code>"ALT"</code>, and <code>"COMMAND"</code>. The given modifier 142 * key name is converted to upper case before comparison. 143 * 144 * @param token 145 * the modifier key name 146 * @return the SWT modifier bit, or <code>0</code> if no match was found 147 * @see org.eclipse.swt.SWT 148 */ findModifier(String token)149 public static int findModifier(String token) { 150 return LegacyActionTools.findModifier(token); 151 } 152 153 /** 154 * Returns a string representation of an SWT modifier bit (SWT.CTRL, 155 * SWT.ALT, SWT.SHIFT, and SWT.COMMAND). Returns <code>null</code> if the 156 * key code is not an SWT modifier bit. 157 * 158 * @param keyCode 159 * the SWT modifier bit to be translated 160 * @return the string representation of the SWT modifier bit, or 161 * <code>null</code> if the key code was not an SWT modifier bit 162 * @see org.eclipse.swt.SWT 163 * @since 2.0 164 */ findModifierString(int keyCode)165 public static String findModifierString(int keyCode) { 166 return LegacyActionTools.findModifierString(keyCode); 167 } 168 169 /** 170 * Convenience method for removing any optional accelerator text from the 171 * given string. The accelerator text appears at the end of the text, and is 172 * separated from the main part by the last tab character <code>'\t'</code> 173 * (or the last <code>'@'</code> if there is no tab). 174 * 175 * @param text 176 * the text 177 * @return the text sans accelerator 178 */ removeAcceleratorText(String text)179 public static String removeAcceleratorText(String text) { 180 return LegacyActionTools.removeAcceleratorText(text); 181 } 182 183 /** 184 * Convenience method for removing any mnemonics from the given string. For 185 * example, <code>removeMnemonics("&Open")</code> will return 186 * <code>"Open"</code>. 187 * 188 * @param text the text 189 * @return the text sans mnemonics 190 * 191 * @since 3.0 192 */ removeMnemonics(String text)193 public static String removeMnemonics(String text) { 194 return LegacyActionTools.removeMnemonics(text); 195 } 196 197 /** 198 * This action's accelerator; <code>0</code> means none. 199 */ 200 private int accelerator = 0; 201 202 /** 203 * This action's action definition id, or <code>null</code> if none. 204 */ 205 private String actionDefinitionId; 206 207 /** 208 * This action's description, or <code>null</code> if none. 209 */ 210 private String description; 211 212 /** 213 * This action's disabled image, or <code>null</code> if none. 214 */ 215 private ImageDescriptor disabledImage; 216 217 /** 218 * Indicates this action is enabled. 219 */ 220 private boolean enabled = true; 221 222 /** 223 * An action's help listener, or <code>null</code> if none. 224 */ 225 private HelpListener helpListener; 226 227 /** 228 * This action's hover image, or <code>null</code> if none. 229 */ 230 private ImageDescriptor hoverImage; 231 232 /** 233 * This action's id, or <code>null</code> if none. 234 */ 235 private String id; 236 237 /** 238 * This action's image, or <code>null</code> if none. 239 */ 240 private ImageDescriptor image; 241 242 /** 243 * This action's text, or <code>null</code> if none. 244 */ 245 private String text; 246 247 /** 248 * This action's tool tip text, or <code>null</code> if none. 249 */ 250 private String toolTipText; 251 252 /** 253 * Holds the action's menu creator (an IMenuCreator) or checked state (a 254 * Boolean for toggle button, or an Integer for radio button), or 255 * <code>null</code> if neither have been set. 256 * <p> 257 * The value of this field affects the value of <code>getStyle()</code>. 258 * </p> 259 */ 260 private Object value = null; 261 262 /** 263 * Creates a new action with no text and no image. 264 * <p> 265 * Configure the action later using the set methods. 266 * </p> 267 */ Action()268 protected Action() { 269 // do nothing 270 } 271 272 /** 273 * Creates a new action with the given text and no image. Calls the zero-arg 274 * constructor, then <code>setText</code>. 275 * 276 * @param text 277 * the string used as the text for the action, or 278 * <code>null</code> if there is no text 279 * @see #setText 280 */ Action(String text)281 protected Action(String text) { 282 this(); 283 setText(text); 284 } 285 286 /** 287 * Creates a new action with the given text and image. Calls the zero-arg 288 * constructor, then <code>setText</code> and 289 * <code>setImageDescriptor</code>. 290 * 291 * @param text 292 * the action's text, or <code>null</code> if there is no text 293 * @param image 294 * the action's image, or <code>null</code> if there is no 295 * image 296 * @see #setText 297 * @see #setImageDescriptor 298 */ Action(String text, ImageDescriptor image)299 protected Action(String text, ImageDescriptor image) { 300 this(text); 301 setImageDescriptor(image); 302 } 303 304 /** 305 * Creates a new action with the given text and style. 306 * 307 * @param text 308 * the action's text, or <code>null</code> if there is no text 309 * @param style 310 * one of <code>AS_PUSH_BUTTON</code>, 311 * <code>AS_CHECK_BOX</code>, <code>AS_DROP_DOWN_MENU</code>, 312 * <code>AS_RADIO_BUTTON</code>, and 313 * <code>AS_UNSPECIFIED</code>. 314 */ Action(String text, int style)315 protected Action(String text, int style) { 316 this(text); 317 switch (style) { 318 case AS_PUSH_BUTTON: 319 value = VAL_PUSH_BTN; 320 break; 321 case AS_CHECK_BOX: 322 value = VAL_TOGGLE_BTN_OFF; 323 break; 324 case AS_DROP_DOWN_MENU: 325 value = VAL_DROP_DOWN_MENU; 326 break; 327 case AS_RADIO_BUTTON: 328 value = VAL_RADIO_BTN_OFF; 329 break; 330 } 331 } 332 333 @Override getAccelerator()334 public int getAccelerator() { 335 return accelerator; 336 } 337 338 @Override getActionDefinitionId()339 public String getActionDefinitionId() { 340 return actionDefinitionId; 341 } 342 343 @Override getDescription()344 public String getDescription() { 345 if (description != null) { 346 return description; 347 } 348 return getToolTipText(); 349 } 350 351 @Override getDisabledImageDescriptor()352 public ImageDescriptor getDisabledImageDescriptor() { 353 return disabledImage; 354 } 355 356 @Override getHelpListener()357 public HelpListener getHelpListener() { 358 return helpListener; 359 } 360 361 @Override getHoverImageDescriptor()362 public ImageDescriptor getHoverImageDescriptor() { 363 return hoverImage; 364 } 365 366 @Override getId()367 public String getId() { 368 return id; 369 } 370 371 @Override getImageDescriptor()372 public ImageDescriptor getImageDescriptor() { 373 return image; 374 } 375 376 @Override getMenuCreator()377 public IMenuCreator getMenuCreator() { 378 // The default drop down menu value is only used 379 // to mark this action requested style. So do not 380 // return it. For backward compatibility reasons. 381 if (value == VAL_DROP_DOWN_MENU) { 382 return null; 383 } 384 if (value instanceof IMenuCreator) { 385 return (IMenuCreator) value; 386 } 387 return null; 388 } 389 390 @Override getStyle()391 public int getStyle() { 392 // Infer the style from the value field. 393 if (value == VAL_PUSH_BTN || value == null) { 394 return AS_PUSH_BUTTON; 395 } 396 if (value == VAL_TOGGLE_BTN_ON || value == VAL_TOGGLE_BTN_OFF) { 397 return AS_CHECK_BOX; 398 } 399 if (value == VAL_RADIO_BTN_ON || value == VAL_RADIO_BTN_OFF) { 400 return AS_RADIO_BUTTON; 401 } 402 if (value instanceof IMenuCreator) { 403 return AS_DROP_DOWN_MENU; 404 } 405 406 // We should never get to this line... 407 return AS_PUSH_BUTTON; 408 } 409 410 @Override getText()411 public String getText() { 412 return text; 413 } 414 415 @Override getToolTipText()416 public String getToolTipText() { 417 return toolTipText; 418 } 419 420 @Override isChecked()421 public boolean isChecked() { 422 return value == VAL_TOGGLE_BTN_ON || value == VAL_RADIO_BTN_ON; 423 } 424 425 @Override isEnabled()426 public boolean isEnabled() { 427 return enabled; 428 } 429 430 @Override isHandled()431 public boolean isHandled() { 432 return true; 433 } 434 435 /** 436 * Reports the outcome of the running of this action via the 437 * {@link IAction#RESULT} property. 438 * 439 * @param success 440 * <code>true</code> if the action succeeded and 441 * <code>false</code> if the action failed or was not completed 442 * @see IAction#RESULT 443 * @since 3.0 444 */ notifyResult(boolean success)445 public final void notifyResult(boolean success) { 446 // avoid Boolean.valueOf(boolean) to allow compilation against JCL 447 // Foundation (bug 80059) 448 firePropertyChange(RESULT, null, success ? Boolean.TRUE : Boolean.FALSE); 449 } 450 451 /** 452 * The default implementation of this <code>IAction</code> method does 453 * nothing. Subclasses should override this method if they do not need 454 * information from the triggering event, or override 455 * <code>runWithEvent(Event)</code> if they do. 456 */ 457 @Override run()458 public void run() { 459 // do nothing 460 } 461 462 /** 463 * The default implementation of this <code>IAction</code> method ignores 464 * the event argument, and simply calls <code>run()</code>. Subclasses 465 * should override this method if they need information from the triggering 466 * event, or override <code>run()</code> if not. 467 * 468 * @param event 469 * the SWT event which triggered this action being run 470 * @since 2.0 471 */ 472 @Override runWithEvent(Event event)473 public void runWithEvent(Event event) { 474 run(); 475 } 476 477 @Override setAccelerator(int keycode)478 public void setAccelerator(int keycode) { 479 this.accelerator = keycode; 480 } 481 482 @Override setActionDefinitionId(String id)483 public void setActionDefinitionId(String id) { 484 actionDefinitionId = id; 485 } 486 487 @Override setChecked(boolean checked)488 public void setChecked(boolean checked) { 489 Object newValue = null; 490 491 // For backward compatibility, if the style is not 492 // set yet, then convert it to a toggle button. 493 if (value == null || value == VAL_TOGGLE_BTN_ON 494 || value == VAL_TOGGLE_BTN_OFF) { 495 newValue = checked ? VAL_TOGGLE_BTN_ON : VAL_TOGGLE_BTN_OFF; 496 } else if (value == VAL_RADIO_BTN_ON || value == VAL_RADIO_BTN_OFF) { 497 newValue = checked ? VAL_RADIO_BTN_ON : VAL_RADIO_BTN_OFF; 498 } else { 499 // Some other style already, so do nothing. 500 return; 501 } 502 503 if (newValue != value) { 504 value = newValue; 505 if (checked) { 506 firePropertyChange(CHECKED, Boolean.FALSE, Boolean.TRUE); 507 } else { 508 firePropertyChange(CHECKED, Boolean.TRUE, Boolean.FALSE); 509 } 510 } 511 } 512 513 @Override setDescription(String text)514 public void setDescription(String text) { 515 516 if ((description == null && text != null) 517 || (description != null && text == null) 518 || (description != null && text != null && !text 519 .equals(description))) { 520 String oldDescription = description; 521 description = text; 522 firePropertyChange(DESCRIPTION, oldDescription, description); 523 } 524 } 525 526 @Override setDisabledImageDescriptor(ImageDescriptor newImage)527 public void setDisabledImageDescriptor(ImageDescriptor newImage) { 528 if (disabledImage != newImage) { 529 ImageDescriptor oldImage = disabledImage; 530 disabledImage = newImage; 531 firePropertyChange(IMAGE, oldImage, newImage); 532 } 533 } 534 535 @Override setEnabled(boolean enabled)536 public void setEnabled(boolean enabled) { 537 if (enabled != this.enabled) { 538 Boolean oldVal = this.enabled ? Boolean.TRUE : Boolean.FALSE; 539 Boolean newVal = enabled ? Boolean.TRUE : Boolean.FALSE; 540 this.enabled = enabled; 541 firePropertyChange(ENABLED, oldVal, newVal); 542 } 543 } 544 545 @Override setHelpListener(HelpListener listener)546 public void setHelpListener(HelpListener listener) { 547 helpListener = listener; 548 } 549 550 @Override setHoverImageDescriptor(ImageDescriptor newImage)551 public void setHoverImageDescriptor(ImageDescriptor newImage) { 552 if (hoverImage != newImage) { 553 ImageDescriptor oldImage = hoverImage; 554 hoverImage = newImage; 555 firePropertyChange(IMAGE, oldImage, newImage); 556 } 557 } 558 559 @Override setId(String id)560 public void setId(String id) { 561 this.id = id; 562 } 563 564 @Override setImageDescriptor(ImageDescriptor newImage)565 public void setImageDescriptor(ImageDescriptor newImage) { 566 if (image != newImage) { 567 ImageDescriptor oldImage = image; 568 image = newImage; 569 firePropertyChange(IMAGE, oldImage, newImage); 570 } 571 } 572 573 /** 574 * Sets the menu creator for this action. 575 * <p> 576 * Note that if this method is called, it overrides the check status. 577 * </p> 578 * 579 * @param creator 580 * the menu creator, or <code>null</code> if none 581 */ 582 @Override setMenuCreator(IMenuCreator creator)583 public void setMenuCreator(IMenuCreator creator) { 584 // For backward compatibility, if the style is not 585 // set yet, then convert it to a drop down menu. 586 if (value == null) { 587 value = creator; 588 return; 589 } 590 591 if (value instanceof IMenuCreator) { 592 value = creator == null ? VAL_DROP_DOWN_MENU : creator; 593 } 594 } 595 596 @Override setText(String text)597 public void setText(String text) { 598 String oldText = this.text; 599 int oldAccel = this.accelerator; 600 this.text = text; 601 if (text != null) { 602 String acceleratorText = LegacyActionTools 603 .extractAcceleratorText(text); 604 if (acceleratorText != null) { 605 int newAccelerator = LegacyActionTools 606 .convertLocalizedAccelerator(acceleratorText); 607 // Be sure to not wipe out the accelerator if nothing found 608 if (newAccelerator > 0) { 609 setAccelerator(newAccelerator); 610 } 611 } 612 } 613 if (!(this.accelerator == oldAccel && (oldText == null ? this.text == null 614 : oldText.equals(this.text)))) { 615 firePropertyChange(TEXT, oldText, this.text); 616 } 617 } 618 619 /** 620 * Sets the tool tip text for this action. 621 * <p> 622 * Fires a property change event for the <code>TOOL_TIP_TEXT</code> 623 * property if the tool tip text actually changes as a consequence. 624 * </p> 625 * 626 * @param toolTipText 627 * the tool tip text, or <code>null</code> if none 628 */ 629 @Override setToolTipText(String toolTipText)630 public void setToolTipText(String toolTipText) { 631 String oldToolTipText = this.toolTipText; 632 if (!(oldToolTipText == null ? toolTipText == null : oldToolTipText 633 .equals(toolTipText))) { 634 this.toolTipText = toolTipText; 635 firePropertyChange(TOOL_TIP_TEXT, oldToolTipText, toolTipText); 636 } 637 } 638 639 } 640