1 /* 2 * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 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) { 154 } 155 156 private static synchronized AWTKeyStroke getCachedStroke 157 (char keyChar, int keyCode, int modifiers, boolean onKeyRelease) 158 { 159 @SuppressWarnings("unchecked") 160 Map<AWTKeyStroke, AWTKeyStroke> cache = (Map)AppContext.getAppContext().get(APP_CONTEXT_CACHE_KEY); 161 AWTKeyStroke cacheKey = (AWTKeyStroke)AppContext.getAppContext().get(APP_CONTEXT_KEYSTROKE_KEY); 162 163 if (cache == null) { 164 cache = new HashMap<>(); 165 AppContext.getAppContext().put(APP_CONTEXT_CACHE_KEY, cache); 166 } 167 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 @SuppressWarnings("deprecation") 354 public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent anEvent) { 355 int id = anEvent.getID(); 356 switch(id) { 357 case KeyEvent.KEY_PRESSED: 358 case KeyEvent.KEY_RELEASED: 359 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, 360 anEvent.getKeyCode(), 361 anEvent.getModifiers(), 362 (id == KeyEvent.KEY_RELEASED)); 363 case KeyEvent.KEY_TYPED: 364 return getCachedStroke(anEvent.getKeyChar(), 365 KeyEvent.VK_UNDEFINED, 366 anEvent.getModifiers(), 367 false); 368 default: 369 // Invalid ID for this KeyEvent 370 return null; 371 } 372 } 373 374 /** 375 * Parses a string and returns an {@code AWTKeyStroke}. 376 * The string must have the following syntax: 377 * <pre> 378 * <modifiers>* (<typedID> | <pressedReleasedID>) 379 * 380 * modifiers := shift | control | ctrl | meta | alt | altGraph 381 * typedID := typed <typedKey> 382 * typedKey := string of length 1 giving Unicode character. 383 * pressedReleasedID := (pressed | released) key 384 * key := KeyEvent key code name, i.e. the name following "VK_". 385 * </pre> 386 * If typed, pressed or released is not specified, pressed is assumed. Here 387 * are some examples: 388 * <pre> 389 * "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0); 390 * "control DELETE" => getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK); 391 * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK); 392 * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true); 393 * "typed a" => getAWTKeyStroke('a'); 394 * </pre> 395 * 396 * @param s a String formatted as described above 397 * @return an {@code AWTKeyStroke} object for that String 398 * @throws IllegalArgumentException if {@code s} is {@code null}, 399 * or is formatted incorrectly 400 */ 401 @SuppressWarnings("deprecation") 402 public static AWTKeyStroke getAWTKeyStroke(String s) { 403 if (s == null) { 404 throw new IllegalArgumentException("String cannot be null"); 405 } 406 407 final String errmsg = "String formatted incorrectly"; 408 409 StringTokenizer st = new StringTokenizer(s, " "); 410 411 int mask = 0; 412 boolean released = false; 413 boolean typed = false; 414 boolean pressed = false; 415 416 synchronized (AWTKeyStroke.class) { 417 if (modifierKeywords == null) { 418 Map<String, Integer> uninitializedMap = new HashMap<>(8, 1.0f); 419 uninitializedMap.put("shift", 420 Integer.valueOf(InputEvent.SHIFT_DOWN_MASK 421 |InputEvent.SHIFT_MASK)); 422 uninitializedMap.put("control", 423 Integer.valueOf(InputEvent.CTRL_DOWN_MASK 424 |InputEvent.CTRL_MASK)); 425 uninitializedMap.put("ctrl", 426 Integer.valueOf(InputEvent.CTRL_DOWN_MASK 427 |InputEvent.CTRL_MASK)); 428 uninitializedMap.put("meta", 429 Integer.valueOf(InputEvent.META_DOWN_MASK 430 |InputEvent.META_MASK)); 431 uninitializedMap.put("alt", 432 Integer.valueOf(InputEvent.ALT_DOWN_MASK 433 |InputEvent.ALT_MASK)); 434 uninitializedMap.put("altGraph", 435 Integer.valueOf(InputEvent.ALT_GRAPH_DOWN_MASK 436 |InputEvent.ALT_GRAPH_MASK)); 437 uninitializedMap.put("button1", 438 Integer.valueOf(InputEvent.BUTTON1_DOWN_MASK)); 439 uninitializedMap.put("button2", 440 Integer.valueOf(InputEvent.BUTTON2_DOWN_MASK)); 441 uninitializedMap.put("button3", 442 Integer.valueOf(InputEvent.BUTTON3_DOWN_MASK)); 443 modifierKeywords = 444 Collections.synchronizedMap(uninitializedMap); 445 } 446 } 447 448 int count = st.countTokens(); 449 450 for (int i = 1; i <= count; i++) { 451 String token = st.nextToken(); 452 453 if (typed) { 454 if (token.length() != 1 || i != count) { 455 throw new IllegalArgumentException(errmsg); 456 } 457 return getCachedStroke(token.charAt(0), KeyEvent.VK_UNDEFINED, 458 mask, false); 459 } 460 461 if (pressed || released || i == count) { 462 if (i != count) { 463 throw new IllegalArgumentException(errmsg); 464 } 465 466 String keyCodeName = "VK_" + token; 467 int keyCode = getVKValue(keyCodeName); 468 469 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, 470 mask, released); 471 } 472 473 if (token.equals("released")) { 474 released = true; 475 continue; 476 } 477 if (token.equals("pressed")) { 478 pressed = true; 479 continue; 480 } 481 if (token.equals("typed")) { 482 typed = true; 483 continue; 484 } 485 486 Integer tokenMask = modifierKeywords.get(token); 487 if (tokenMask != null) { 488 mask |= tokenMask.intValue(); 489 } else { 490 throw new IllegalArgumentException(errmsg); 491 } 492 } 493 494 throw new IllegalArgumentException(errmsg); 495 } 496 497 private static VKCollection getVKCollection() { 498 if (vks == null) { 499 vks = new VKCollection(); 500 } 501 return vks; 502 } 503 /** 504 * Returns the integer constant for the KeyEvent.VK field named 505 * {@code key}. This will throw an 506 * {@code IllegalArgumentException} if {@code key} is 507 * not a valid constant. 508 */ 509 private static int getVKValue(String key) { 510 VKCollection vkCollect = getVKCollection(); 511 512 Integer value = vkCollect.findCode(key); 513 514 if (value == null) { 515 int keyCode = 0; 516 final String errmsg = "String formatted incorrectly"; 517 518 try { 519 keyCode = KeyEvent.class.getField(key).getInt(KeyEvent.class); 520 } catch (NoSuchFieldException nsfe) { 521 throw new IllegalArgumentException(errmsg); 522 } catch (IllegalAccessException iae) { 523 throw new IllegalArgumentException(errmsg); 524 } 525 value = Integer.valueOf(keyCode); 526 vkCollect.put(key, value); 527 } 528 return value.intValue(); 529 } 530 531 /** 532 * Returns the character for this {@code AWTKeyStroke}. 533 * 534 * @return a char value 535 * @see #getAWTKeyStroke(char) 536 * @see KeyEvent#getKeyChar 537 */ 538 public final char getKeyChar() { 539 return keyChar; 540 } 541 542 /** 543 * Returns the numeric key code for this {@code AWTKeyStroke}. 544 * 545 * @return an int containing the key code value 546 * @see #getAWTKeyStroke(int,int) 547 * @see KeyEvent#getKeyCode 548 */ 549 public final int getKeyCode() { 550 return keyCode; 551 } 552 553 /** 554 * Returns the modifier keys for this {@code AWTKeyStroke}. 555 * 556 * @return an int containing the modifiers 557 * @see #getAWTKeyStroke(int,int) 558 */ 559 public final int getModifiers() { 560 return modifiers; 561 } 562 563 /** 564 * Returns whether this {@code AWTKeyStroke} represents a key release. 565 * 566 * @return {@code true} if this {@code AWTKeyStroke} 567 * represents a key release; {@code false} otherwise 568 * @see #getAWTKeyStroke(int,int,boolean) 569 */ 570 public final boolean isOnKeyRelease() { 571 return onKeyRelease; 572 } 573 574 /** 575 * Returns the type of {@code KeyEvent} which corresponds to 576 * this {@code AWTKeyStroke}. 577 * 578 * @return {@code KeyEvent.KEY_PRESSED}, 579 * {@code KeyEvent.KEY_TYPED}, 580 * or {@code KeyEvent.KEY_RELEASED} 581 * @see java.awt.event.KeyEvent 582 */ 583 public final int getKeyEventType() { 584 if (keyCode == KeyEvent.VK_UNDEFINED) { 585 return KeyEvent.KEY_TYPED; 586 } else { 587 return (onKeyRelease) 588 ? KeyEvent.KEY_RELEASED 589 : KeyEvent.KEY_PRESSED; 590 } 591 } 592 593 /** 594 * Returns a numeric value for this object that is likely to be unique, 595 * making it a good choice as the index value in a hash table. 596 * 597 * @return an int that represents this object 598 */ 599 public int hashCode() { 600 return (((int)keyChar) + 1) * (2 * (keyCode + 1)) * (modifiers + 1) + 601 (onKeyRelease ? 1 : 2); 602 } 603 604 /** 605 * Returns true if this object is identical to the specified object. 606 * 607 * @param anObject the Object to compare this object to 608 * @return true if the objects are identical 609 */ 610 public final boolean equals(Object anObject) { 611 if (anObject instanceof AWTKeyStroke) { 612 AWTKeyStroke ks = (AWTKeyStroke)anObject; 613 return (ks.keyChar == keyChar && ks.keyCode == keyCode && 614 ks.onKeyRelease == onKeyRelease && 615 ks.modifiers == modifiers); 616 } 617 return false; 618 } 619 620 /** 621 * Returns a string that displays and identifies this object's properties. 622 * The {@code String} returned by this method can be passed 623 * as a parameter to {@code getAWTKeyStroke(String)} to produce 624 * a key stroke equal to this key stroke. 625 * 626 * @return a String representation of this object 627 * @see #getAWTKeyStroke(String) 628 */ 629 public String toString() { 630 if (keyCode == KeyEvent.VK_UNDEFINED) { 631 return getModifiersText(modifiers) + "typed " + keyChar; 632 } else { 633 return getModifiersText(modifiers) + 634 (onKeyRelease ? "released" : "pressed") + " " + 635 getVKText(keyCode); 636 } 637 } 638 639 static String getModifiersText(int modifiers) { 640 StringBuilder buf = new StringBuilder(); 641 642 if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0 ) { 643 buf.append("shift "); 644 } 645 if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0 ) { 646 buf.append("ctrl "); 647 } 648 if ((modifiers & InputEvent.META_DOWN_MASK) != 0 ) { 649 buf.append("meta "); 650 } 651 if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0 ) { 652 buf.append("alt "); 653 } 654 if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0 ) { 655 buf.append("altGraph "); 656 } 657 if ((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0 ) { 658 buf.append("button1 "); 659 } 660 if ((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0 ) { 661 buf.append("button2 "); 662 } 663 if ((modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0 ) { 664 buf.append("button3 "); 665 } 666 667 return buf.toString(); 668 } 669 670 static String getVKText(int keyCode) { 671 VKCollection vkCollect = getVKCollection(); 672 Integer key = Integer.valueOf(keyCode); 673 String name = vkCollect.findName(key); 674 if (name != null) { 675 return name.substring(3); 676 } 677 int expected_modifiers = 678 (Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL); 679 680 Field[] fields = KeyEvent.class.getDeclaredFields(); 681 for (int i = 0; i < fields.length; i++) { 682 try { 683 if (fields[i].getModifiers() == expected_modifiers 684 && fields[i].getType() == Integer.TYPE 685 && fields[i].getName().startsWith("VK_") 686 && fields[i].getInt(KeyEvent.class) == keyCode) 687 { 688 name = fields[i].getName(); 689 vkCollect.put(name, key); 690 return name.substring(3); 691 } 692 } catch (IllegalAccessException e) { 693 assert(false); 694 } 695 } 696 return "UNKNOWN"; 697 } 698 699 /** 700 * Returns a cached instance of {@code AWTKeyStroke} (or a subclass of 701 * {@code AWTKeyStroke}) which is equal to this instance. 702 * 703 * @return a cached instance which is equal to this instance 704 * @throws java.io.ObjectStreamException if a serialization problem occurs 705 */ 706 protected Object readResolve() throws java.io.ObjectStreamException { 707 synchronized (AWTKeyStroke.class) { 708 709 return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease); 710 } 711 } 712 713 @SuppressWarnings("deprecation") 714 private static int mapOldModifiers(int modifiers) { 715 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 716 modifiers |= InputEvent.SHIFT_DOWN_MASK; 717 } 718 if ((modifiers & InputEvent.ALT_MASK) != 0) { 719 modifiers |= InputEvent.ALT_DOWN_MASK; 720 } 721 if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 722 modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK; 723 } 724 if ((modifiers & InputEvent.CTRL_MASK) != 0) { 725 modifiers |= InputEvent.CTRL_DOWN_MASK; 726 } 727 if ((modifiers & InputEvent.META_MASK) != 0) { 728 modifiers |= InputEvent.META_DOWN_MASK; 729 } 730 731 modifiers &= InputEvent.SHIFT_DOWN_MASK 732 | InputEvent.ALT_DOWN_MASK 733 | InputEvent.ALT_GRAPH_DOWN_MASK 734 | InputEvent.CTRL_DOWN_MASK 735 | InputEvent.META_DOWN_MASK 736 | InputEvent.BUTTON1_DOWN_MASK 737 | InputEvent.BUTTON2_DOWN_MASK 738 | InputEvent.BUTTON3_DOWN_MASK; 739 740 return modifiers; 741 } 742 743 @SuppressWarnings("deprecation") 744 private static int mapNewModifiers(int modifiers) { 745 if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) { 746 modifiers |= InputEvent.SHIFT_MASK; 747 } 748 if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) { 749 modifiers |= InputEvent.ALT_MASK; 750 } 751 if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) { 752 modifiers |= InputEvent.ALT_GRAPH_MASK; 753 } 754 if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) { 755 modifiers |= InputEvent.CTRL_MASK; 756 } 757 if ((modifiers & InputEvent.META_DOWN_MASK) != 0) { 758 modifiers |= InputEvent.META_MASK; 759 } 760 761 return modifiers; 762 } 763 764 } 765 766 class VKCollection { 767 Map<Integer, String> code2name; 768 Map<String, Integer> name2code; 769 770 public VKCollection() { 771 code2name = new HashMap<>(); 772 name2code = new HashMap<>(); 773 } 774 775 public synchronized void put(String name, Integer code) { 776 assert((name != null) && (code != null)); 777 assert(findName(code) == null); 778 assert(findCode(name) == null); 779 code2name.put(code, name); 780 name2code.put(name, code); 781 } 782 783 public synchronized Integer findCode(String name) { 784 assert(name != null); 785 return name2code.get(name); 786 } 787 788 public synchronized String findName(Integer code) { 789 assert(code != null); 790 return code2name.get(code); 791 } 792 }