20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package java.awt; 26 27 import java.awt.event.KeyEvent; 28 import sun.awt.AppContext; 29 import java.awt.event.InputEvent; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.Map; 33 import java.util.StringTokenizer; 34 import java.io.Serializable; 35 import java.lang.reflect.Modifier; 36 import java.lang.reflect.Field; 37 import sun.swing.SwingAccessor; 38 39 /** 40 * An <code>AWTKeyStroke</code> represents a key action on the 41 * keyboard, or equivalent input device. <code>AWTKeyStroke</code>s 42 * can correspond to only a press or release of a 43 * particular key, just as <code>KEY_PRESSED</code> and 44 * <code>KEY_RELEASED</code> <code>KeyEvent</code>s do; 45 * alternately, they can correspond to typing a specific Java character, just 46 * as <code>KEY_TYPED</code> <code>KeyEvent</code>s do. 47 * In all cases, <code>AWTKeyStroke</code>s can specify modifiers 48 * (alt, shift, control, meta, altGraph, or a combination thereof) which must be present 49 * during the action for an exact match. 50 * <p> 51 * <code>AWTKeyStrokes</code> are immutable, and are intended 52 * to be unique. Client code should never create an 53 * <code>AWTKeyStroke</code> on its own, but should instead use 54 * a variant of <code>getAWTKeyStroke</code>. Client use of these factory 55 * methods allows the <code>AWTKeyStroke</code> implementation 56 * to cache and share instances efficiently. 57 * 58 * @see #getAWTKeyStroke 59 * 60 * @author Arnaud Weber 61 * @author David Mendenhall 62 * @since 1.4 63 */ 64 public class AWTKeyStroke implements Serializable { 65 static final long serialVersionUID = -6430539691155161871L; 66 67 private static Map<String, Integer> modifierKeywords; 68 /** 69 * Associates VK_XXX (as a String) with code (as Integer). This is 70 * done to avoid the overhead of the reflective call to find the 71 * constant. 72 */ 73 private static VKCollection vks; 74 75 //A key for the collection of AWTKeyStrokes within AppContext. 76 private static Object APP_CONTEXT_CACHE_KEY = new Object(); 77 //A key withing the cache 78 private static AWTKeyStroke APP_CONTEXT_KEYSTROKE_KEY = new AWTKeyStroke(); 79 80 private char keyChar = KeyEvent.CHAR_UNDEFINED; 81 private int keyCode = KeyEvent.VK_UNDEFINED; 82 private int modifiers; 83 private boolean onKeyRelease; 84 85 static { 86 /* ensure that the necessary native libraries are loaded */ 87 Toolkit.loadLibraries(); 88 } 89 90 /** 91 * Constructs an <code>AWTKeyStroke</code> with default values. 92 * The default values used are: 93 * <table border="1" summary="AWTKeyStroke default values"> 94 * <tr><th>Property</th><th>Default Value</th></tr> 95 * <tr> 96 * <td>Key Char</td> 97 * <td><code>KeyEvent.CHAR_UNDEFINED</code></td> 98 * </tr> 99 * <tr> 100 * <td>Key Code</td> 101 * <td><code>KeyEvent.VK_UNDEFINED</code></td> 102 * </tr> 103 * <tr> 104 * <td>Modifiers</td> 105 * <td>none</td> 106 * </tr> 107 * <tr> 108 * <td>On key release?</td> 109 * <td><code>false</code></td> 110 * </tr> 111 * </table> 112 * 113 * <code>AWTKeyStroke</code>s should not be constructed 114 * by client code. Use a variant of <code>getAWTKeyStroke</code> 115 * instead. 116 * 117 * @see #getAWTKeyStroke 118 */ 119 protected AWTKeyStroke() { 120 } 121 122 /** 123 * Constructs an <code>AWTKeyStroke</code> with the specified 124 * values. <code>AWTKeyStroke</code>s should not be constructed 125 * by client code. Use a variant of <code>getAWTKeyStroke</code> 126 * instead. 127 * 128 * @param keyChar the character value for a keyboard key 129 * @param keyCode the key code for this <code>AWTKeyStroke</code> 130 * @param modifiers a bitwise-ored combination of any modifiers 131 * @param onKeyRelease <code>true</code> if this 132 * <code>AWTKeyStroke</code> corresponds 133 * to a key release; <code>false</code> otherwise 134 * @see #getAWTKeyStroke 135 */ 136 protected AWTKeyStroke(char keyChar, int keyCode, int modifiers, 137 boolean onKeyRelease) { 138 this.keyChar = keyChar; 139 this.keyCode = keyCode; 140 this.modifiers = modifiers; 141 this.onKeyRelease = onKeyRelease; 142 } 143 144 /** 145 * The method has no effect and is only left present to avoid introducing 146 * a binary incompatibility. 147 * 148 * @param subclass the new Class of which the factory methods should create 149 * instances 150 * @deprecated 151 */ 152 @Deprecated 153 protected static void registerSubclass(Class<?> subclass) { 168 if (cacheKey == null) { 169 cacheKey = SwingAccessor.getKeyStrokeAccessor().create(); 170 AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey); 171 } 172 173 cacheKey.keyChar = keyChar; 174 cacheKey.keyCode = keyCode; 175 cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers)); 176 cacheKey.onKeyRelease = onKeyRelease; 177 178 AWTKeyStroke stroke = cache.get(cacheKey); 179 if (stroke == null) { 180 stroke = cacheKey; 181 cache.put(stroke, stroke); 182 AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY); 183 } 184 return stroke; 185 } 186 187 /** 188 * Returns a shared instance of an <code>AWTKeyStroke</code> 189 * that represents a <code>KEY_TYPED</code> event for the 190 * specified character. 191 * 192 * @param keyChar the character value for a keyboard key 193 * @return an <code>AWTKeyStroke</code> object for that key 194 */ 195 public static AWTKeyStroke getAWTKeyStroke(char keyChar) { 196 return getCachedStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false); 197 } 198 199 /** 200 * Returns a shared instance of an {@code AWTKeyStroke} 201 * that represents a {@code KEY_TYPED} event for the 202 * specified Character object and a set of modifiers. Note 203 * that the first parameter is of type Character rather than 204 * char. This is to avoid inadvertent clashes with 205 * calls to <code>getAWTKeyStroke(int keyCode, int modifiers)</code>. 206 * 207 * The modifiers consist of any combination of following:<ul> 208 * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK 209 * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK 210 * <li>java.awt.event.InputEvent.META_DOWN_MASK 211 * <li>java.awt.event.InputEvent.ALT_DOWN_MASK 212 * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK 213 * </ul> 214 * The old modifiers listed below also can be used, but they are 215 * mapped to _DOWN_ modifiers. <ul> 216 * <li>java.awt.event.InputEvent.SHIFT_MASK 217 * <li>java.awt.event.InputEvent.CTRL_MASK 218 * <li>java.awt.event.InputEvent.META_MASK 219 * <li>java.awt.event.InputEvent.ALT_MASK 220 * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK 221 * </ul> 222 * also can be used, but they are mapped to _DOWN_ modifiers. 223 * 224 * Since these numbers are all different powers of two, any combination of 225 * them is an integer in which each bit represents a different modifier 226 * key. Use 0 to specify no modifiers. 227 * 228 * @param keyChar the Character object for a keyboard character 229 * @param modifiers a bitwise-ored combination of any modifiers 230 * @return an <code>AWTKeyStroke</code> object for that key 231 * @throws IllegalArgumentException if <code>keyChar</code> is 232 * <code>null</code> 233 * 234 * @see java.awt.event.InputEvent 235 */ 236 public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers) 237 { 238 if (keyChar == null) { 239 throw new IllegalArgumentException("keyChar cannot be null"); 240 } 241 return getCachedStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED, 242 modifiers, false); 243 } 244 245 /** 246 * Returns a shared instance of an <code>AWTKeyStroke</code>, 247 * given a numeric key code and a set of modifiers, specifying 248 * whether the key is activated when it is pressed or released. 249 * <p> 250 * The "virtual key" constants defined in 251 * <code>java.awt.event.KeyEvent</code> can be 252 * used to specify the key code. For example:<ul> 253 * <li><code>java.awt.event.KeyEvent.VK_ENTER</code> 254 * <li><code>java.awt.event.KeyEvent.VK_TAB</code> 255 * <li><code>java.awt.event.KeyEvent.VK_SPACE</code> 256 * </ul> 257 * Alternatively, the key code may be obtained by calling 258 * <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar</code>. 259 * 260 * The modifiers consist of any combination of:<ul> 261 * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK 262 * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK 263 * <li>java.awt.event.InputEvent.META_DOWN_MASK 264 * <li>java.awt.event.InputEvent.ALT_DOWN_MASK 265 * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK 266 * </ul> 267 * The old modifiers <ul> 268 * <li>java.awt.event.InputEvent.SHIFT_MASK 269 * <li>java.awt.event.InputEvent.CTRL_MASK 270 * <li>java.awt.event.InputEvent.META_MASK 271 * <li>java.awt.event.InputEvent.ALT_MASK 272 * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK 273 * </ul> 274 * also can be used, but they are mapped to _DOWN_ modifiers. 275 * 276 * Since these numbers are all different powers of two, any combination of 277 * them is an integer in which each bit represents a different modifier 278 * key. Use 0 to specify no modifiers. 279 * 280 * @param keyCode an int specifying the numeric code for a keyboard key 281 * @param modifiers a bitwise-ored combination of any modifiers 282 * @param onKeyRelease <code>true</code> if the <code>AWTKeyStroke</code> 283 * should represent a key release; <code>false</code> otherwise 284 * @return an AWTKeyStroke object for that key 285 * 286 * @see java.awt.event.KeyEvent 287 * @see java.awt.event.InputEvent 288 */ 289 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers, 290 boolean onKeyRelease) { 291 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers, 292 onKeyRelease); 293 } 294 295 /** 296 * Returns a shared instance of an <code>AWTKeyStroke</code>, 297 * given a numeric key code and a set of modifiers. The returned 298 * <code>AWTKeyStroke</code> will correspond to a key press. 299 * <p> 300 * The "virtual key" constants defined in 301 * <code>java.awt.event.KeyEvent</code> can be 302 * used to specify the key code. For example:<ul> 303 * <li><code>java.awt.event.KeyEvent.VK_ENTER</code> 304 * <li><code>java.awt.event.KeyEvent.VK_TAB</code> 305 * <li><code>java.awt.event.KeyEvent.VK_SPACE</code> 306 * </ul> 307 * The modifiers consist of any combination of:<ul> 308 * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK 309 * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK 310 * <li>java.awt.event.InputEvent.META_DOWN_MASK 311 * <li>java.awt.event.InputEvent.ALT_DOWN_MASK 312 * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK 313 * </ul> 314 * The old modifiers <ul> 315 * <li>java.awt.event.InputEvent.SHIFT_MASK 316 * <li>java.awt.event.InputEvent.CTRL_MASK 317 * <li>java.awt.event.InputEvent.META_MASK 318 * <li>java.awt.event.InputEvent.ALT_MASK 319 * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK 320 * </ul> 321 * also can be used, but they are mapped to _DOWN_ modifiers. 322 * 323 * Since these numbers are all different powers of two, any combination of 324 * them is an integer in which each bit represents a different modifier 325 * key. Use 0 to specify no modifiers. 326 * 327 * @param keyCode an int specifying the numeric code for a keyboard key 328 * @param modifiers a bitwise-ored combination of any modifiers 329 * @return an <code>AWTKeyStroke</code> object for that key 330 * 331 * @see java.awt.event.KeyEvent 332 * @see java.awt.event.InputEvent 333 */ 334 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) { 335 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers, 336 false); 337 } 338 339 /** 340 * Returns an <code>AWTKeyStroke</code> which represents the 341 * stroke which generated a given <code>KeyEvent</code>. 342 * <p> 343 * This method obtains the keyChar from a <code>KeyTyped</code> 344 * event, and the keyCode from a <code>KeyPressed</code> or 345 * <code>KeyReleased</code> event. The <code>KeyEvent</code> modifiers are 346 * obtained for all three types of <code>KeyEvent</code>. 347 * 348 * @param anEvent the <code>KeyEvent</code> from which to 349 * obtain the <code>AWTKeyStroke</code> 350 * @throws NullPointerException if <code>anEvent</code> is null 351 * @return the <code>AWTKeyStroke</code> that precipitated the event 352 */ 353 public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent anEvent) { 354 int id = anEvent.getID(); 355 switch(id) { 356 case KeyEvent.KEY_PRESSED: 357 case KeyEvent.KEY_RELEASED: 358 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, 359 anEvent.getKeyCode(), 360 anEvent.getModifiers(), 361 (id == KeyEvent.KEY_RELEASED)); 362 case KeyEvent.KEY_TYPED: 363 return getCachedStroke(anEvent.getKeyChar(), 364 KeyEvent.VK_UNDEFINED, 365 anEvent.getModifiers(), 366 false); 367 default: 368 // Invalid ID for this KeyEvent 369 return null; 370 } 371 } 372 373 /** 374 * Parses a string and returns an <code>AWTKeyStroke</code>. 375 * The string must have the following syntax: 376 * <pre> 377 * <modifiers>* (<typedID> | <pressedReleasedID>) 378 * 379 * modifiers := shift | control | ctrl | meta | alt | altGraph 380 * typedID := typed <typedKey> 381 * typedKey := string of length 1 giving Unicode character. 382 * pressedReleasedID := (pressed | released) key 383 * key := KeyEvent key code name, i.e. the name following "VK_". 384 * </pre> 385 * If typed, pressed or released is not specified, pressed is assumed. Here 386 * are some examples: 387 * <pre> 388 * "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0); 389 * "control DELETE" => getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK); 390 * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK); 391 * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true); 392 * "typed a" => getAWTKeyStroke('a'); 393 * </pre> 394 * 395 * @param s a String formatted as described above 396 * @return an <code>AWTKeyStroke</code> object for that String 397 * @throws IllegalArgumentException if <code>s</code> is <code>null</code>, 398 * or is formatted incorrectly 399 */ 400 public static AWTKeyStroke getAWTKeyStroke(String s) { 401 if (s == null) { 402 throw new IllegalArgumentException("String cannot be null"); 403 } 404 405 final String errmsg = "String formatted incorrectly"; 406 407 StringTokenizer st = new StringTokenizer(s, " "); 408 409 int mask = 0; 410 boolean released = false; 411 boolean typed = false; 412 boolean pressed = false; 413 414 synchronized (AWTKeyStroke.class) { 415 if (modifierKeywords == null) { 416 Map<String, Integer> uninitializedMap = new HashMap<>(8, 1.0f); 417 uninitializedMap.put("shift", 483 484 Integer tokenMask = modifierKeywords.get(token); 485 if (tokenMask != null) { 486 mask |= tokenMask.intValue(); 487 } else { 488 throw new IllegalArgumentException(errmsg); 489 } 490 } 491 492 throw new IllegalArgumentException(errmsg); 493 } 494 495 private static VKCollection getVKCollection() { 496 if (vks == null) { 497 vks = new VKCollection(); 498 } 499 return vks; 500 } 501 /** 502 * Returns the integer constant for the KeyEvent.VK field named 503 * <code>key</code>. This will throw an 504 * <code>IllegalArgumentException</code> if <code>key</code> is 505 * not a valid constant. 506 */ 507 private static int getVKValue(String key) { 508 VKCollection vkCollect = getVKCollection(); 509 510 Integer value = vkCollect.findCode(key); 511 512 if (value == null) { 513 int keyCode = 0; 514 final String errmsg = "String formatted incorrectly"; 515 516 try { 517 keyCode = KeyEvent.class.getField(key).getInt(KeyEvent.class); 518 } catch (NoSuchFieldException nsfe) { 519 throw new IllegalArgumentException(errmsg); 520 } catch (IllegalAccessException iae) { 521 throw new IllegalArgumentException(errmsg); 522 } 523 value = Integer.valueOf(keyCode); 524 vkCollect.put(key, value); 525 } 526 return value.intValue(); 527 } 528 529 /** 530 * Returns the character for this <code>AWTKeyStroke</code>. 531 * 532 * @return a char value 533 * @see #getAWTKeyStroke(char) 534 * @see KeyEvent#getKeyChar 535 */ 536 public final char getKeyChar() { 537 return keyChar; 538 } 539 540 /** 541 * Returns the numeric key code for this <code>AWTKeyStroke</code>. 542 * 543 * @return an int containing the key code value 544 * @see #getAWTKeyStroke(int,int) 545 * @see KeyEvent#getKeyCode 546 */ 547 public final int getKeyCode() { 548 return keyCode; 549 } 550 551 /** 552 * Returns the modifier keys for this <code>AWTKeyStroke</code>. 553 * 554 * @return an int containing the modifiers 555 * @see #getAWTKeyStroke(int,int) 556 */ 557 public final int getModifiers() { 558 return modifiers; 559 } 560 561 /** 562 * Returns whether this <code>AWTKeyStroke</code> represents a key release. 563 * 564 * @return <code>true</code> if this <code>AWTKeyStroke</code> 565 * represents a key release; <code>false</code> otherwise 566 * @see #getAWTKeyStroke(int,int,boolean) 567 */ 568 public final boolean isOnKeyRelease() { 569 return onKeyRelease; 570 } 571 572 /** 573 * Returns the type of <code>KeyEvent</code> which corresponds to 574 * this <code>AWTKeyStroke</code>. 575 * 576 * @return <code>KeyEvent.KEY_PRESSED</code>, 577 * <code>KeyEvent.KEY_TYPED</code>, 578 * or <code>KeyEvent.KEY_RELEASED</code> 579 * @see java.awt.event.KeyEvent 580 */ 581 public final int getKeyEventType() { 582 if (keyCode == KeyEvent.VK_UNDEFINED) { 583 return KeyEvent.KEY_TYPED; 584 } else { 585 return (onKeyRelease) 586 ? KeyEvent.KEY_RELEASED 587 : KeyEvent.KEY_PRESSED; 588 } 589 } 590 591 /** 592 * Returns a numeric value for this object that is likely to be unique, 593 * making it a good choice as the index value in a hash table. 594 * 595 * @return an int that represents this object 596 */ 597 public int hashCode() { 598 return (((int)keyChar) + 1) * (2 * (keyCode + 1)) * (modifiers + 1) + 600 } 601 602 /** 603 * Returns true if this object is identical to the specified object. 604 * 605 * @param anObject the Object to compare this object to 606 * @return true if the objects are identical 607 */ 608 public final boolean equals(Object anObject) { 609 if (anObject instanceof AWTKeyStroke) { 610 AWTKeyStroke ks = (AWTKeyStroke)anObject; 611 return (ks.keyChar == keyChar && ks.keyCode == keyCode && 612 ks.onKeyRelease == onKeyRelease && 613 ks.modifiers == modifiers); 614 } 615 return false; 616 } 617 618 /** 619 * Returns a string that displays and identifies this object's properties. 620 * The <code>String</code> returned by this method can be passed 621 * as a parameter to <code>getAWTKeyStroke(String)</code> to produce 622 * a key stroke equal to this key stroke. 623 * 624 * @return a String representation of this object 625 * @see #getAWTKeyStroke(String) 626 */ 627 public String toString() { 628 if (keyCode == KeyEvent.VK_UNDEFINED) { 629 return getModifiersText(modifiers) + "typed " + keyChar; 630 } else { 631 return getModifiersText(modifiers) + 632 (onKeyRelease ? "released" : "pressed") + " " + 633 getVKText(keyCode); 634 } 635 } 636 637 static String getModifiersText(int modifiers) { 638 StringBuilder buf = new StringBuilder(); 639 640 if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0 ) { 641 buf.append("shift "); 678 Field[] fields = KeyEvent.class.getDeclaredFields(); 679 for (int i = 0; i < fields.length; i++) { 680 try { 681 if (fields[i].getModifiers() == expected_modifiers 682 && fields[i].getType() == Integer.TYPE 683 && fields[i].getName().startsWith("VK_") 684 && fields[i].getInt(KeyEvent.class) == keyCode) 685 { 686 name = fields[i].getName(); 687 vkCollect.put(name, key); 688 return name.substring(3); 689 } 690 } catch (IllegalAccessException e) { 691 assert(false); 692 } 693 } 694 return "UNKNOWN"; 695 } 696 697 /** 698 * Returns a cached instance of <code>AWTKeyStroke</code> (or a subclass of 699 * <code>AWTKeyStroke</code>) which is equal to this instance. 700 * 701 * @return a cached instance which is equal to this instance 702 * @throws java.io.ObjectStreamException if a serialization problem occurs 703 */ 704 protected Object readResolve() throws java.io.ObjectStreamException { 705 synchronized (AWTKeyStroke.class) { 706 707 return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease); 708 } 709 } 710 711 private static int mapOldModifiers(int modifiers) { 712 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 713 modifiers |= InputEvent.SHIFT_DOWN_MASK; 714 } 715 if ((modifiers & InputEvent.ALT_MASK) != 0) { 716 modifiers |= InputEvent.ALT_DOWN_MASK; 717 } 718 if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 719 modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK; | 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package java.awt; 26 27 import java.awt.event.KeyEvent; 28 import sun.awt.AppContext; 29 import java.awt.event.InputEvent; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.Map; 33 import java.util.StringTokenizer; 34 import java.io.Serializable; 35 import java.lang.reflect.Modifier; 36 import java.lang.reflect.Field; 37 import sun.swing.SwingAccessor; 38 39 /** 40 * An {@code AWTKeyStroke} represents a key action on the 41 * keyboard, or equivalent input device. {@code AWTKeyStroke}s 42 * can correspond to only a press or release of a 43 * particular key, just as {@code KEY_PRESSED} and 44 * {@code KEY_RELEASED KeyEvent}s do; 45 * alternately, they can correspond to typing a specific Java character, just 46 * as {@code KEY_TYPED KeyEvent}s do. 47 * In all cases, {@code AWTKeyStroke}s can specify modifiers 48 * (alt, shift, control, meta, altGraph, or a combination thereof) which must be present 49 * during the action for an exact match. 50 * <p> 51 * {@code AWTKeyStrokes} are immutable, and are intended 52 * to be unique. Client code should never create an 53 * {@code AWTKeyStroke} on its own, but should instead use 54 * a variant of {@code getAWTKeyStroke}. Client use of these factory 55 * methods allows the {@code AWTKeyStroke} implementation 56 * to cache and share instances efficiently. 57 * 58 * @see #getAWTKeyStroke 59 * 60 * @author Arnaud Weber 61 * @author David Mendenhall 62 * @since 1.4 63 */ 64 public class AWTKeyStroke implements Serializable { 65 static final long serialVersionUID = -6430539691155161871L; 66 67 private static Map<String, Integer> modifierKeywords; 68 /** 69 * Associates VK_XXX (as a String) with code (as Integer). This is 70 * done to avoid the overhead of the reflective call to find the 71 * constant. 72 */ 73 private static VKCollection vks; 74 75 //A key for the collection of AWTKeyStrokes within AppContext. 76 private static Object APP_CONTEXT_CACHE_KEY = new Object(); 77 //A key withing the cache 78 private static AWTKeyStroke APP_CONTEXT_KEYSTROKE_KEY = new AWTKeyStroke(); 79 80 private char keyChar = KeyEvent.CHAR_UNDEFINED; 81 private int keyCode = KeyEvent.VK_UNDEFINED; 82 private int modifiers; 83 private boolean onKeyRelease; 84 85 static { 86 /* ensure that the necessary native libraries are loaded */ 87 Toolkit.loadLibraries(); 88 } 89 90 /** 91 * Constructs an {@code AWTKeyStroke} with default values. 92 * The default values used are: 93 * <table border="1" summary="AWTKeyStroke default values"> 94 * <tr><th>Property</th><th>Default Value</th></tr> 95 * <tr> 96 * <td>Key Char</td> 97 * <td>{@code KeyEvent.CHAR_UNDEFINED}</td> 98 * </tr> 99 * <tr> 100 * <td>Key Code</td> 101 * <td>{@code KeyEvent.VK_UNDEFINED}</td> 102 * </tr> 103 * <tr> 104 * <td>Modifiers</td> 105 * <td>none</td> 106 * </tr> 107 * <tr> 108 * <td>On key release?</td> 109 * <td>{@code false}</td> 110 * </tr> 111 * </table> 112 * 113 * {@code AWTKeyStroke}s should not be constructed 114 * by client code. Use a variant of {@code getAWTKeyStroke} 115 * instead. 116 * 117 * @see #getAWTKeyStroke 118 */ 119 protected AWTKeyStroke() { 120 } 121 122 /** 123 * Constructs an {@code AWTKeyStroke} with the specified 124 * values. {@code AWTKeyStroke}s should not be constructed 125 * by client code. Use a variant of {@code getAWTKeyStroke} 126 * instead. 127 * 128 * @param keyChar the character value for a keyboard key 129 * @param keyCode the key code for this {@code AWTKeyStroke} 130 * @param modifiers a bitwise-ored combination of any modifiers 131 * @param onKeyRelease {@code true} if this 132 * {@code AWTKeyStroke} corresponds 133 * to a key release; {@code false} otherwise 134 * @see #getAWTKeyStroke 135 */ 136 protected AWTKeyStroke(char keyChar, int keyCode, int modifiers, 137 boolean onKeyRelease) { 138 this.keyChar = keyChar; 139 this.keyCode = keyCode; 140 this.modifiers = modifiers; 141 this.onKeyRelease = onKeyRelease; 142 } 143 144 /** 145 * The method has no effect and is only left present to avoid introducing 146 * a binary incompatibility. 147 * 148 * @param subclass the new Class of which the factory methods should create 149 * instances 150 * @deprecated 151 */ 152 @Deprecated 153 protected static void registerSubclass(Class<?> subclass) { 168 if (cacheKey == null) { 169 cacheKey = SwingAccessor.getKeyStrokeAccessor().create(); 170 AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey); 171 } 172 173 cacheKey.keyChar = keyChar; 174 cacheKey.keyCode = keyCode; 175 cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers)); 176 cacheKey.onKeyRelease = onKeyRelease; 177 178 AWTKeyStroke stroke = cache.get(cacheKey); 179 if (stroke == null) { 180 stroke = cacheKey; 181 cache.put(stroke, stroke); 182 AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY); 183 } 184 return stroke; 185 } 186 187 /** 188 * Returns a shared instance of an {@code AWTKeyStroke} 189 * that represents a {@code KEY_TYPED} event for the 190 * specified character. 191 * 192 * @param keyChar the character value for a keyboard key 193 * @return an {@code AWTKeyStroke} object for that key 194 */ 195 public static AWTKeyStroke getAWTKeyStroke(char keyChar) { 196 return getCachedStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false); 197 } 198 199 /** 200 * Returns a shared instance of an {@code AWTKeyStroke} 201 * that represents a {@code KEY_TYPED} event for the 202 * specified Character object and a set of modifiers. Note 203 * that the first parameter is of type Character rather than 204 * char. This is to avoid inadvertent clashes with 205 * calls to {@code getAWTKeyStroke(int keyCode, int modifiers)}. 206 * 207 * The modifiers consist of any combination of following:<ul> 208 * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK 209 * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK 210 * <li>java.awt.event.InputEvent.META_DOWN_MASK 211 * <li>java.awt.event.InputEvent.ALT_DOWN_MASK 212 * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK 213 * </ul> 214 * The old modifiers listed below also can be used, but they are 215 * mapped to _DOWN_ modifiers. <ul> 216 * <li>java.awt.event.InputEvent.SHIFT_MASK 217 * <li>java.awt.event.InputEvent.CTRL_MASK 218 * <li>java.awt.event.InputEvent.META_MASK 219 * <li>java.awt.event.InputEvent.ALT_MASK 220 * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK 221 * </ul> 222 * also can be used, but they are mapped to _DOWN_ modifiers. 223 * 224 * Since these numbers are all different powers of two, any combination of 225 * them is an integer in which each bit represents a different modifier 226 * key. Use 0 to specify no modifiers. 227 * 228 * @param keyChar the Character object for a keyboard character 229 * @param modifiers a bitwise-ored combination of any modifiers 230 * @return an {@code AWTKeyStroke} object for that key 231 * @throws IllegalArgumentException if {@code keyChar} is 232 * {@code null} 233 * 234 * @see java.awt.event.InputEvent 235 */ 236 public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers) 237 { 238 if (keyChar == null) { 239 throw new IllegalArgumentException("keyChar cannot be null"); 240 } 241 return getCachedStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED, 242 modifiers, false); 243 } 244 245 /** 246 * Returns a shared instance of an {@code AWTKeyStroke}, 247 * given a numeric key code and a set of modifiers, specifying 248 * whether the key is activated when it is pressed or released. 249 * <p> 250 * The "virtual key" constants defined in 251 * {@code java.awt.event.KeyEvent} can be 252 * used to specify the key code. For example:<ul> 253 * <li>{@code java.awt.event.KeyEvent.VK_ENTER} 254 * <li>{@code java.awt.event.KeyEvent.VK_TAB} 255 * <li>{@code java.awt.event.KeyEvent.VK_SPACE} 256 * </ul> 257 * Alternatively, the key code may be obtained by calling 258 * {@code java.awt.event.KeyEvent.getExtendedKeyCodeForChar}. 259 * 260 * The modifiers consist of any combination of:<ul> 261 * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK 262 * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK 263 * <li>java.awt.event.InputEvent.META_DOWN_MASK 264 * <li>java.awt.event.InputEvent.ALT_DOWN_MASK 265 * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK 266 * </ul> 267 * The old modifiers <ul> 268 * <li>java.awt.event.InputEvent.SHIFT_MASK 269 * <li>java.awt.event.InputEvent.CTRL_MASK 270 * <li>java.awt.event.InputEvent.META_MASK 271 * <li>java.awt.event.InputEvent.ALT_MASK 272 * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK 273 * </ul> 274 * also can be used, but they are mapped to _DOWN_ modifiers. 275 * 276 * Since these numbers are all different powers of two, any combination of 277 * them is an integer in which each bit represents a different modifier 278 * key. Use 0 to specify no modifiers. 279 * 280 * @param keyCode an int specifying the numeric code for a keyboard key 281 * @param modifiers a bitwise-ored combination of any modifiers 282 * @param onKeyRelease {@code true} if the {@code AWTKeyStroke} 283 * should represent a key release; {@code false} otherwise 284 * @return an AWTKeyStroke object for that key 285 * 286 * @see java.awt.event.KeyEvent 287 * @see java.awt.event.InputEvent 288 */ 289 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers, 290 boolean onKeyRelease) { 291 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers, 292 onKeyRelease); 293 } 294 295 /** 296 * Returns a shared instance of an {@code AWTKeyStroke}, 297 * given a numeric key code and a set of modifiers. The returned 298 * {@code AWTKeyStroke} will correspond to a key press. 299 * <p> 300 * The "virtual key" constants defined in 301 * {@code java.awt.event.KeyEvent} can be 302 * used to specify the key code. For example:<ul> 303 * <li>{@code java.awt.event.KeyEvent.VK_ENTER} 304 * <li>{@code java.awt.event.KeyEvent.VK_TAB} 305 * <li>{@code java.awt.event.KeyEvent.VK_SPACE} 306 * </ul> 307 * The modifiers consist of any combination of:<ul> 308 * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK 309 * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK 310 * <li>java.awt.event.InputEvent.META_DOWN_MASK 311 * <li>java.awt.event.InputEvent.ALT_DOWN_MASK 312 * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK 313 * </ul> 314 * The old modifiers <ul> 315 * <li>java.awt.event.InputEvent.SHIFT_MASK 316 * <li>java.awt.event.InputEvent.CTRL_MASK 317 * <li>java.awt.event.InputEvent.META_MASK 318 * <li>java.awt.event.InputEvent.ALT_MASK 319 * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK 320 * </ul> 321 * also can be used, but they are mapped to _DOWN_ modifiers. 322 * 323 * Since these numbers are all different powers of two, any combination of 324 * them is an integer in which each bit represents a different modifier 325 * key. Use 0 to specify no modifiers. 326 * 327 * @param keyCode an int specifying the numeric code for a keyboard key 328 * @param modifiers a bitwise-ored combination of any modifiers 329 * @return an {@code AWTKeyStroke} object for that key 330 * 331 * @see java.awt.event.KeyEvent 332 * @see java.awt.event.InputEvent 333 */ 334 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) { 335 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers, 336 false); 337 } 338 339 /** 340 * Returns an {@code AWTKeyStroke} which represents the 341 * stroke which generated a given {@code KeyEvent}. 342 * <p> 343 * This method obtains the keyChar from a {@code KeyTyped} 344 * event, and the keyCode from a {@code KeyPressed} or 345 * {@code KeyReleased} event. The {@code KeyEvent} modifiers are 346 * obtained for all three types of {@code KeyEvent}. 347 * 348 * @param anEvent the {@code KeyEvent} from which to 349 * obtain the {@code AWTKeyStroke} 350 * @throws NullPointerException if {@code anEvent} is null 351 * @return the {@code AWTKeyStroke} that precipitated the event 352 */ 353 public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent anEvent) { 354 int id = anEvent.getID(); 355 switch(id) { 356 case KeyEvent.KEY_PRESSED: 357 case KeyEvent.KEY_RELEASED: 358 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, 359 anEvent.getKeyCode(), 360 anEvent.getModifiers(), 361 (id == KeyEvent.KEY_RELEASED)); 362 case KeyEvent.KEY_TYPED: 363 return getCachedStroke(anEvent.getKeyChar(), 364 KeyEvent.VK_UNDEFINED, 365 anEvent.getModifiers(), 366 false); 367 default: 368 // Invalid ID for this KeyEvent 369 return null; 370 } 371 } 372 373 /** 374 * Parses a string and returns an {@code AWTKeyStroke}. 375 * The string must have the following syntax: 376 * <pre> 377 * <modifiers>* (<typedID> | <pressedReleasedID>) 378 * 379 * modifiers := shift | control | ctrl | meta | alt | altGraph 380 * typedID := typed <typedKey> 381 * typedKey := string of length 1 giving Unicode character. 382 * pressedReleasedID := (pressed | released) key 383 * key := KeyEvent key code name, i.e. the name following "VK_". 384 * </pre> 385 * If typed, pressed or released is not specified, pressed is assumed. Here 386 * are some examples: 387 * <pre> 388 * "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0); 389 * "control DELETE" => getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK); 390 * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK); 391 * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true); 392 * "typed a" => getAWTKeyStroke('a'); 393 * </pre> 394 * 395 * @param s a String formatted as described above 396 * @return an {@code AWTKeyStroke} object for that String 397 * @throws IllegalArgumentException if {@code s} is {@code null}, 398 * or is formatted incorrectly 399 */ 400 public static AWTKeyStroke getAWTKeyStroke(String s) { 401 if (s == null) { 402 throw new IllegalArgumentException("String cannot be null"); 403 } 404 405 final String errmsg = "String formatted incorrectly"; 406 407 StringTokenizer st = new StringTokenizer(s, " "); 408 409 int mask = 0; 410 boolean released = false; 411 boolean typed = false; 412 boolean pressed = false; 413 414 synchronized (AWTKeyStroke.class) { 415 if (modifierKeywords == null) { 416 Map<String, Integer> uninitializedMap = new HashMap<>(8, 1.0f); 417 uninitializedMap.put("shift", 483 484 Integer tokenMask = modifierKeywords.get(token); 485 if (tokenMask != null) { 486 mask |= tokenMask.intValue(); 487 } else { 488 throw new IllegalArgumentException(errmsg); 489 } 490 } 491 492 throw new IllegalArgumentException(errmsg); 493 } 494 495 private static VKCollection getVKCollection() { 496 if (vks == null) { 497 vks = new VKCollection(); 498 } 499 return vks; 500 } 501 /** 502 * Returns the integer constant for the KeyEvent.VK field named 503 * {@code key}. This will throw an 504 * {@code IllegalArgumentException} if {@code key} is 505 * not a valid constant. 506 */ 507 private static int getVKValue(String key) { 508 VKCollection vkCollect = getVKCollection(); 509 510 Integer value = vkCollect.findCode(key); 511 512 if (value == null) { 513 int keyCode = 0; 514 final String errmsg = "String formatted incorrectly"; 515 516 try { 517 keyCode = KeyEvent.class.getField(key).getInt(KeyEvent.class); 518 } catch (NoSuchFieldException nsfe) { 519 throw new IllegalArgumentException(errmsg); 520 } catch (IllegalAccessException iae) { 521 throw new IllegalArgumentException(errmsg); 522 } 523 value = Integer.valueOf(keyCode); 524 vkCollect.put(key, value); 525 } 526 return value.intValue(); 527 } 528 529 /** 530 * Returns the character for this {@code AWTKeyStroke}. 531 * 532 * @return a char value 533 * @see #getAWTKeyStroke(char) 534 * @see KeyEvent#getKeyChar 535 */ 536 public final char getKeyChar() { 537 return keyChar; 538 } 539 540 /** 541 * Returns the numeric key code for this {@code AWTKeyStroke}. 542 * 543 * @return an int containing the key code value 544 * @see #getAWTKeyStroke(int,int) 545 * @see KeyEvent#getKeyCode 546 */ 547 public final int getKeyCode() { 548 return keyCode; 549 } 550 551 /** 552 * Returns the modifier keys for this {@code AWTKeyStroke}. 553 * 554 * @return an int containing the modifiers 555 * @see #getAWTKeyStroke(int,int) 556 */ 557 public final int getModifiers() { 558 return modifiers; 559 } 560 561 /** 562 * Returns whether this {@code AWTKeyStroke} represents a key release. 563 * 564 * @return {@code true} if this {@code AWTKeyStroke} 565 * represents a key release; {@code false} otherwise 566 * @see #getAWTKeyStroke(int,int,boolean) 567 */ 568 public final boolean isOnKeyRelease() { 569 return onKeyRelease; 570 } 571 572 /** 573 * Returns the type of {@code KeyEvent} which corresponds to 574 * this {@code AWTKeyStroke}. 575 * 576 * @return {@code KeyEvent.KEY_PRESSED}, 577 * {@code KeyEvent.KEY_TYPED}, 578 * or {@code KeyEvent.KEY_RELEASED} 579 * @see java.awt.event.KeyEvent 580 */ 581 public final int getKeyEventType() { 582 if (keyCode == KeyEvent.VK_UNDEFINED) { 583 return KeyEvent.KEY_TYPED; 584 } else { 585 return (onKeyRelease) 586 ? KeyEvent.KEY_RELEASED 587 : KeyEvent.KEY_PRESSED; 588 } 589 } 590 591 /** 592 * Returns a numeric value for this object that is likely to be unique, 593 * making it a good choice as the index value in a hash table. 594 * 595 * @return an int that represents this object 596 */ 597 public int hashCode() { 598 return (((int)keyChar) + 1) * (2 * (keyCode + 1)) * (modifiers + 1) + 600 } 601 602 /** 603 * Returns true if this object is identical to the specified object. 604 * 605 * @param anObject the Object to compare this object to 606 * @return true if the objects are identical 607 */ 608 public final boolean equals(Object anObject) { 609 if (anObject instanceof AWTKeyStroke) { 610 AWTKeyStroke ks = (AWTKeyStroke)anObject; 611 return (ks.keyChar == keyChar && ks.keyCode == keyCode && 612 ks.onKeyRelease == onKeyRelease && 613 ks.modifiers == modifiers); 614 } 615 return false; 616 } 617 618 /** 619 * Returns a string that displays and identifies this object's properties. 620 * The {@code String} returned by this method can be passed 621 * as a parameter to {@code getAWTKeyStroke(String)} to produce 622 * a key stroke equal to this key stroke. 623 * 624 * @return a String representation of this object 625 * @see #getAWTKeyStroke(String) 626 */ 627 public String toString() { 628 if (keyCode == KeyEvent.VK_UNDEFINED) { 629 return getModifiersText(modifiers) + "typed " + keyChar; 630 } else { 631 return getModifiersText(modifiers) + 632 (onKeyRelease ? "released" : "pressed") + " " + 633 getVKText(keyCode); 634 } 635 } 636 637 static String getModifiersText(int modifiers) { 638 StringBuilder buf = new StringBuilder(); 639 640 if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0 ) { 641 buf.append("shift "); 678 Field[] fields = KeyEvent.class.getDeclaredFields(); 679 for (int i = 0; i < fields.length; i++) { 680 try { 681 if (fields[i].getModifiers() == expected_modifiers 682 && fields[i].getType() == Integer.TYPE 683 && fields[i].getName().startsWith("VK_") 684 && fields[i].getInt(KeyEvent.class) == keyCode) 685 { 686 name = fields[i].getName(); 687 vkCollect.put(name, key); 688 return name.substring(3); 689 } 690 } catch (IllegalAccessException e) { 691 assert(false); 692 } 693 } 694 return "UNKNOWN"; 695 } 696 697 /** 698 * Returns a cached instance of {@code AWTKeyStroke} (or a subclass of 699 * {@code AWTKeyStroke}) which is equal to this instance. 700 * 701 * @return a cached instance which is equal to this instance 702 * @throws java.io.ObjectStreamException if a serialization problem occurs 703 */ 704 protected Object readResolve() throws java.io.ObjectStreamException { 705 synchronized (AWTKeyStroke.class) { 706 707 return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease); 708 } 709 } 710 711 private static int mapOldModifiers(int modifiers) { 712 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 713 modifiers |= InputEvent.SHIFT_DOWN_MASK; 714 } 715 if ((modifiers & InputEvent.ALT_MASK) != 0) { 716 modifiers |= InputEvent.ALT_DOWN_MASK; 717 } 718 if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 719 modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK; |