1/* 2 * Copyright (c) 2000, 2017, 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 */ 25package java.awt; 26 27import java.awt.event.KeyEvent; 28import sun.awt.AppContext; 29import java.awt.event.InputEvent; 30import java.util.Collections; 31import java.util.HashMap; 32import java.util.Map; 33import java.util.StringTokenizer; 34import java.io.Serializable; 35import java.lang.reflect.Modifier; 36import java.lang.reflect.Field; 37import 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 */ 64public 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 * 94 * <table class="striped"> 95 * <caption>AWTKeyStroke default values</caption> 96 * <thead> 97 * <tr><th>Property</th><th>Default Value</th></tr> 98 * </thead> 99 * <tbody> 100 * <tr> 101 * <td>Key Char</td> 102 * <td>{@code KeyEvent.CHAR_UNDEFINED}</td> 103 * </tr> 104 * <tr> 105 * <td>Key Code</td> 106 * <td>{@code KeyEvent.VK_UNDEFINED}</td> 107 * </tr> 108 * <tr> 109 * <td>Modifiers</td> 110 * <td>none</td> 111 * </tr> 112 * <tr> 113 * <td>On key release?</td> 114 * <td>{@code false}</td> 115 * </tr> 116 * </tbody> 117 * </table> 118 * 119 * {@code AWTKeyStroke}s should not be constructed 120 * by client code. Use a variant of {@code getAWTKeyStroke} 121 * instead. 122 * 123 * @see #getAWTKeyStroke 124 */ 125 protected AWTKeyStroke() { 126 } 127 128 /** 129 * Constructs an {@code AWTKeyStroke} with the specified 130 * values. {@code AWTKeyStroke}s should not be constructed 131 * by client code. Use a variant of {@code getAWTKeyStroke} 132 * instead. 133 * 134 * @param keyChar the character value for a keyboard key 135 * @param keyCode the key code for this {@code AWTKeyStroke} 136 * @param modifiers a bitwise-ored combination of any modifiers 137 * @param onKeyRelease {@code true} if this 138 * {@code AWTKeyStroke} corresponds 139 * to a key release; {@code false} otherwise 140 * @see #getAWTKeyStroke 141 */ 142 protected AWTKeyStroke(char keyChar, int keyCode, int modifiers, 143 boolean onKeyRelease) { 144 this.keyChar = keyChar; 145 this.keyCode = keyCode; 146 this.modifiers = modifiers; 147 this.onKeyRelease = onKeyRelease; 148 } 149 150 /** 151 * The method has no effect and is only left present to avoid introducing 152 * a binary incompatibility. 153 * 154 * @param subclass the new Class of which the factory methods should create 155 * instances 156 * @deprecated 157 */ 158 @Deprecated 159 protected static void registerSubclass(Class<?> subclass) { 160 } 161 162 private static synchronized AWTKeyStroke getCachedStroke 163 (char keyChar, int keyCode, int modifiers, boolean onKeyRelease) 164 { 165 @SuppressWarnings("unchecked") 166 Map<AWTKeyStroke, AWTKeyStroke> cache = (Map)AppContext.getAppContext().get(APP_CONTEXT_CACHE_KEY); 167 AWTKeyStroke cacheKey = (AWTKeyStroke)AppContext.getAppContext().get(APP_CONTEXT_KEYSTROKE_KEY); 168 169 if (cache == null) { 170 cache = new HashMap<>(); 171 AppContext.getAppContext().put(APP_CONTEXT_CACHE_KEY, cache); 172 } 173 174 if (cacheKey == null) { 175 cacheKey = SwingAccessor.getKeyStrokeAccessor().create(); 176 AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey); 177 } 178 179 cacheKey.keyChar = keyChar; 180 cacheKey.keyCode = keyCode; 181 cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers)); 182 cacheKey.onKeyRelease = onKeyRelease; 183 184 AWTKeyStroke stroke = cache.get(cacheKey); 185 if (stroke == null) { 186 stroke = cacheKey; 187 cache.put(stroke, stroke); 188 AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY); 189 } 190 return stroke; 191 } 192 193 /** 194 * Returns a shared instance of an {@code AWTKeyStroke} 195 * that represents a {@code KEY_TYPED} event for the 196 * specified character. 197 * 198 * @param keyChar the character value for a keyboard key 199 * @return an {@code AWTKeyStroke} object for that key 200 */ 201 public static AWTKeyStroke getAWTKeyStroke(char keyChar) { 202 return getCachedStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false); 203 } 204 205 /** 206 * Returns a shared instance of an {@code AWTKeyStroke} 207 * that represents a {@code KEY_TYPED} event for the 208 * specified Character object and a set of modifiers. Note 209 * that the first parameter is of type Character rather than 210 * char. This is to avoid inadvertent clashes with 211 * calls to {@code getAWTKeyStroke(int keyCode, int modifiers)}. 212 * 213 * The modifiers consist of any combination of following:<ul> 214 * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK 215 * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK 216 * <li>java.awt.event.InputEvent.META_DOWN_MASK 217 * <li>java.awt.event.InputEvent.ALT_DOWN_MASK 218 * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK 219 * </ul> 220 * The old modifiers listed below also can be used, but they are 221 * mapped to _DOWN_ modifiers. <ul> 222 * <li>java.awt.event.InputEvent.SHIFT_MASK 223 * <li>java.awt.event.InputEvent.CTRL_MASK 224 * <li>java.awt.event.InputEvent.META_MASK 225 * <li>java.awt.event.InputEvent.ALT_MASK 226 * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK 227 * </ul> 228 * also can be used, but they are mapped to _DOWN_ modifiers. 229 * 230 * Since these numbers are all different powers of two, any combination of 231 * them is an integer in which each bit represents a different modifier 232 * key. Use 0 to specify no modifiers. 233 * 234 * @param keyChar the Character object for a keyboard character 235 * @param modifiers a bitwise-ored combination of any modifiers 236 * @return an {@code AWTKeyStroke} object for that key 237 * @throws IllegalArgumentException if {@code keyChar} is 238 * {@code null} 239 * 240 * @see java.awt.event.InputEvent 241 */ 242 public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers) 243 { 244 if (keyChar == null) { 245 throw new IllegalArgumentException("keyChar cannot be null"); 246 } 247 return getCachedStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED, 248 modifiers, false); 249 } 250 251 /** 252 * Returns a shared instance of an {@code AWTKeyStroke}, 253 * given a numeric key code and a set of modifiers, specifying 254 * whether the key is activated when it is pressed or released. 255 * <p> 256 * The "virtual key" constants defined in 257 * {@code java.awt.event.KeyEvent} can be 258 * used to specify the key code. For example:<ul> 259 * <li>{@code java.awt.event.KeyEvent.VK_ENTER} 260 * <li>{@code java.awt.event.KeyEvent.VK_TAB} 261 * <li>{@code java.awt.event.KeyEvent.VK_SPACE} 262 * </ul> 263 * Alternatively, the key code may be obtained by calling 264 * {@code java.awt.event.KeyEvent.getExtendedKeyCodeForChar}. 265 * 266 * The modifiers consist of any combination of:<ul> 267 * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK 268 * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK 269 * <li>java.awt.event.InputEvent.META_DOWN_MASK 270 * <li>java.awt.event.InputEvent.ALT_DOWN_MASK 271 * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK 272 * </ul> 273 * The old modifiers <ul> 274 * <li>java.awt.event.InputEvent.SHIFT_MASK 275 * <li>java.awt.event.InputEvent.CTRL_MASK 276 * <li>java.awt.event.InputEvent.META_MASK 277 * <li>java.awt.event.InputEvent.ALT_MASK 278 * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK 279 * </ul> 280 * also can be used, but they are mapped to _DOWN_ modifiers. 281 * 282 * Since these numbers are all different powers of two, any combination of 283 * them is an integer in which each bit represents a different modifier 284 * key. Use 0 to specify no modifiers. 285 * 286 * @param keyCode an int specifying the numeric code for a keyboard key 287 * @param modifiers a bitwise-ored combination of any modifiers 288 * @param onKeyRelease {@code true} if the {@code AWTKeyStroke} 289 * should represent a key release; {@code false} otherwise 290 * @return an AWTKeyStroke object for that key 291 * 292 * @see java.awt.event.KeyEvent 293 * @see java.awt.event.InputEvent 294 */ 295 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers, 296 boolean onKeyRelease) { 297 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers, 298 onKeyRelease); 299 } 300 301 /** 302 * Returns a shared instance of an {@code AWTKeyStroke}, 303 * given a numeric key code and a set of modifiers. The returned 304 * {@code AWTKeyStroke} will correspond to a key press. 305 * <p> 306 * The "virtual key" constants defined in 307 * {@code java.awt.event.KeyEvent} can be 308 * used to specify the key code. For example:<ul> 309 * <li>{@code java.awt.event.KeyEvent.VK_ENTER} 310 * <li>{@code java.awt.event.KeyEvent.VK_TAB} 311 * <li>{@code java.awt.event.KeyEvent.VK_SPACE} 312 * </ul> 313 * The modifiers consist of any combination of:<ul> 314 * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK 315 * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK 316 * <li>java.awt.event.InputEvent.META_DOWN_MASK 317 * <li>java.awt.event.InputEvent.ALT_DOWN_MASK 318 * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK 319 * </ul> 320 * The old modifiers <ul> 321 * <li>java.awt.event.InputEvent.SHIFT_MASK 322 * <li>java.awt.event.InputEvent.CTRL_MASK 323 * <li>java.awt.event.InputEvent.META_MASK 324 * <li>java.awt.event.InputEvent.ALT_MASK 325 * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK 326 * </ul> 327 * also can be used, but they are mapped to _DOWN_ modifiers. 328 * 329 * Since these numbers are all different powers of two, any combination of 330 * them is an integer in which each bit represents a different modifier 331 * key. Use 0 to specify no modifiers. 332 * 333 * @param keyCode an int specifying the numeric code for a keyboard key 334 * @param modifiers a bitwise-ored combination of any modifiers 335 * @return an {@code AWTKeyStroke} object for that key 336 * 337 * @see java.awt.event.KeyEvent 338 * @see java.awt.event.InputEvent 339 */ 340 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) { 341 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers, 342 false); 343 } 344 345 /** 346 * Returns an {@code AWTKeyStroke} which represents the 347 * stroke which generated a given {@code KeyEvent}. 348 * <p> 349 * This method obtains the keyChar from a {@code KeyTyped} 350 * event, and the keyCode from a {@code KeyPressed} or 351 * {@code KeyReleased} event. The {@code KeyEvent} modifiers are 352 * obtained for all three types of {@code KeyEvent}. 353 * 354 * @param anEvent the {@code KeyEvent} from which to 355 * obtain the {@code AWTKeyStroke} 356 * @throws NullPointerException if {@code anEvent} is null 357 * @return the {@code AWTKeyStroke} that precipitated the event 358 */ 359 @SuppressWarnings("deprecation") 360 public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent anEvent) { 361 int id = anEvent.getID(); 362 switch(id) { 363 case KeyEvent.KEY_PRESSED: 364 case KeyEvent.KEY_RELEASED: 365 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, 366 anEvent.getKeyCode(), 367 anEvent.getModifiers(), 368 (id == KeyEvent.KEY_RELEASED)); 369 case KeyEvent.KEY_TYPED: 370 return getCachedStroke(anEvent.getKeyChar(), 371 KeyEvent.VK_UNDEFINED, 372 anEvent.getModifiers(), 373 false); 374 default: 375 // Invalid ID for this KeyEvent 376 return null; 377 } 378 } 379 380 /** 381 * Parses a string and returns an {@code AWTKeyStroke}. 382 * The string must have the following syntax: 383 * <pre> 384 * <modifiers>* (<typedID> | <pressedReleasedID>) 385 * 386 * modifiers := shift | control | ctrl | meta | alt | altGraph 387 * typedID := typed <typedKey> 388 * typedKey := string of length 1 giving Unicode character. 389 * pressedReleasedID := (pressed | released) key 390 * key := KeyEvent key code name, i.e. the name following "VK_". 391 * </pre> 392 * If typed, pressed or released is not specified, pressed is assumed. Here 393 * are some examples: 394 * <pre> 395 * "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0); 396 * "control DELETE" => getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK); 397 * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK); 398 * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true); 399 * "typed a" => getAWTKeyStroke('a'); 400 * </pre> 401 * 402 * @param s a String formatted as described above 403 * @return an {@code AWTKeyStroke} object for that String 404 * @throws IllegalArgumentException if {@code s} is {@code null}, 405 * or is formatted incorrectly 406 */ 407 @SuppressWarnings("deprecation") 408 public static AWTKeyStroke getAWTKeyStroke(String s) { 409 if (s == null) { 410 throw new IllegalArgumentException("String cannot be null"); 411 } 412 413 final String errmsg = "String formatted incorrectly"; 414 415 StringTokenizer st = new StringTokenizer(s, " "); 416 417 int mask = 0; 418 boolean released = false; 419 boolean typed = false; 420 boolean pressed = false; 421 422 synchronized (AWTKeyStroke.class) { 423 if (modifierKeywords == null) { 424 Map<String, Integer> uninitializedMap = new HashMap<>(8, 1.0f); 425 uninitializedMap.put("shift", 426 Integer.valueOf(InputEvent.SHIFT_DOWN_MASK 427 |InputEvent.SHIFT_MASK)); 428 uninitializedMap.put("control", 429 Integer.valueOf(InputEvent.CTRL_DOWN_MASK 430 |InputEvent.CTRL_MASK)); 431 uninitializedMap.put("ctrl", 432 Integer.valueOf(InputEvent.CTRL_DOWN_MASK 433 |InputEvent.CTRL_MASK)); 434 uninitializedMap.put("meta", 435 Integer.valueOf(InputEvent.META_DOWN_MASK 436 |InputEvent.META_MASK)); 437 uninitializedMap.put("alt", 438 Integer.valueOf(InputEvent.ALT_DOWN_MASK 439 |InputEvent.ALT_MASK)); 440 uninitializedMap.put("altGraph", 441 Integer.valueOf(InputEvent.ALT_GRAPH_DOWN_MASK 442 |InputEvent.ALT_GRAPH_MASK)); 443 uninitializedMap.put("button1", 444 Integer.valueOf(InputEvent.BUTTON1_DOWN_MASK)); 445 uninitializedMap.put("button2", 446 Integer.valueOf(InputEvent.BUTTON2_DOWN_MASK)); 447 uninitializedMap.put("button3", 448 Integer.valueOf(InputEvent.BUTTON3_DOWN_MASK)); 449 modifierKeywords = 450 Collections.synchronizedMap(uninitializedMap); 451 } 452 } 453 454 int count = st.countTokens(); 455 456 for (int i = 1; i <= count; i++) { 457 String token = st.nextToken(); 458 459 if (typed) { 460 if (token.length() != 1 || i != count) { 461 throw new IllegalArgumentException(errmsg); 462 } 463 return getCachedStroke(token.charAt(0), KeyEvent.VK_UNDEFINED, 464 mask, false); 465 } 466 467 if (pressed || released || i == count) { 468 if (i != count) { 469 throw new IllegalArgumentException(errmsg); 470 } 471 472 String keyCodeName = "VK_" + token; 473 int keyCode = getVKValue(keyCodeName); 474 475 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, 476 mask, released); 477 } 478 479 if (token.equals("released")) { 480 released = true; 481 continue; 482 } 483 if (token.equals("pressed")) { 484 pressed = true; 485 continue; 486 } 487 if (token.equals("typed")) { 488 typed = true; 489 continue; 490 } 491 492 Integer tokenMask = modifierKeywords.get(token); 493 if (tokenMask != null) { 494 mask |= tokenMask.intValue(); 495 } else { 496 throw new IllegalArgumentException(errmsg); 497 } 498 } 499 500 throw new IllegalArgumentException(errmsg); 501 } 502 503 private static VKCollection getVKCollection() { 504 if (vks == null) { 505 vks = new VKCollection(); 506 } 507 return vks; 508 } 509 /** 510 * Returns the integer constant for the KeyEvent.VK field named 511 * {@code key}. This will throw an 512 * {@code IllegalArgumentException} if {@code key} is 513 * not a valid constant. 514 */ 515 private static int getVKValue(String key) { 516 VKCollection vkCollect = getVKCollection(); 517 518 Integer value = vkCollect.findCode(key); 519 520 if (value == null) { 521 int keyCode = 0; 522 final String errmsg = "String formatted incorrectly"; 523 524 try { 525 keyCode = KeyEvent.class.getField(key).getInt(KeyEvent.class); 526 } catch (NoSuchFieldException nsfe) { 527 throw new IllegalArgumentException(errmsg); 528 } catch (IllegalAccessException iae) { 529 throw new IllegalArgumentException(errmsg); 530 } 531 value = Integer.valueOf(keyCode); 532 vkCollect.put(key, value); 533 } 534 return value.intValue(); 535 } 536 537 /** 538 * Returns the character for this {@code AWTKeyStroke}. 539 * 540 * @return a char value 541 * @see #getAWTKeyStroke(char) 542 * @see KeyEvent#getKeyChar 543 */ 544 public final char getKeyChar() { 545 return keyChar; 546 } 547 548 /** 549 * Returns the numeric key code for this {@code AWTKeyStroke}. 550 * 551 * @return an int containing the key code value 552 * @see #getAWTKeyStroke(int,int) 553 * @see KeyEvent#getKeyCode 554 */ 555 public final int getKeyCode() { 556 return keyCode; 557 } 558 559 /** 560 * Returns the modifier keys for this {@code AWTKeyStroke}. 561 * 562 * @return an int containing the modifiers 563 * @see #getAWTKeyStroke(int,int) 564 */ 565 public final int getModifiers() { 566 return modifiers; 567 } 568 569 /** 570 * Returns whether this {@code AWTKeyStroke} represents a key release. 571 * 572 * @return {@code true} if this {@code AWTKeyStroke} 573 * represents a key release; {@code false} otherwise 574 * @see #getAWTKeyStroke(int,int,boolean) 575 */ 576 public final boolean isOnKeyRelease() { 577 return onKeyRelease; 578 } 579 580 /** 581 * Returns the type of {@code KeyEvent} which corresponds to 582 * this {@code AWTKeyStroke}. 583 * 584 * @return {@code KeyEvent.KEY_PRESSED}, 585 * {@code KeyEvent.KEY_TYPED}, 586 * or {@code KeyEvent.KEY_RELEASED} 587 * @see java.awt.event.KeyEvent 588 */ 589 public final int getKeyEventType() { 590 if (keyCode == KeyEvent.VK_UNDEFINED) { 591 return KeyEvent.KEY_TYPED; 592 } else { 593 return (onKeyRelease) 594 ? KeyEvent.KEY_RELEASED 595 : KeyEvent.KEY_PRESSED; 596 } 597 } 598 599 /** 600 * Returns a numeric value for this object that is likely to be unique, 601 * making it a good choice as the index value in a hash table. 602 * 603 * @return an int that represents this object 604 */ 605 public int hashCode() { 606 return (((int)keyChar) + 1) * (2 * (keyCode + 1)) * (modifiers + 1) + 607 (onKeyRelease ? 1 : 2); 608 } 609 610 /** 611 * Returns true if this object is identical to the specified object. 612 * 613 * @param anObject the Object to compare this object to 614 * @return true if the objects are identical 615 */ 616 public final boolean equals(Object anObject) { 617 if (anObject instanceof AWTKeyStroke) { 618 AWTKeyStroke ks = (AWTKeyStroke)anObject; 619 return (ks.keyChar == keyChar && ks.keyCode == keyCode && 620 ks.onKeyRelease == onKeyRelease && 621 ks.modifiers == modifiers); 622 } 623 return false; 624 } 625 626 /** 627 * Returns a string that displays and identifies this object's properties. 628 * The {@code String} returned by this method can be passed 629 * as a parameter to {@code getAWTKeyStroke(String)} to produce 630 * a key stroke equal to this key stroke. 631 * 632 * @return a String representation of this object 633 * @see #getAWTKeyStroke(String) 634 */ 635 public String toString() { 636 if (keyCode == KeyEvent.VK_UNDEFINED) { 637 return getModifiersText(modifiers) + "typed " + keyChar; 638 } else { 639 return getModifiersText(modifiers) + 640 (onKeyRelease ? "released" : "pressed") + " " + 641 getVKText(keyCode); 642 } 643 } 644 645 static String getModifiersText(int modifiers) { 646 StringBuilder buf = new StringBuilder(); 647 648 if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0 ) { 649 buf.append("shift "); 650 } 651 if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0 ) { 652 buf.append("ctrl "); 653 } 654 if ((modifiers & InputEvent.META_DOWN_MASK) != 0 ) { 655 buf.append("meta "); 656 } 657 if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0 ) { 658 buf.append("alt "); 659 } 660 if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0 ) { 661 buf.append("altGraph "); 662 } 663 if ((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0 ) { 664 buf.append("button1 "); 665 } 666 if ((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0 ) { 667 buf.append("button2 "); 668 } 669 if ((modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0 ) { 670 buf.append("button3 "); 671 } 672 673 return buf.toString(); 674 } 675 676 static String getVKText(int keyCode) { 677 VKCollection vkCollect = getVKCollection(); 678 Integer key = Integer.valueOf(keyCode); 679 String name = vkCollect.findName(key); 680 if (name != null) { 681 return name.substring(3); 682 } 683 int expected_modifiers = 684 (Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL); 685 686 Field[] fields = KeyEvent.class.getDeclaredFields(); 687 for (int i = 0; i < fields.length; i++) { 688 try { 689 if (fields[i].getModifiers() == expected_modifiers 690 && fields[i].getType() == Integer.TYPE 691 && fields[i].getName().startsWith("VK_") 692 && fields[i].getInt(KeyEvent.class) == keyCode) 693 { 694 name = fields[i].getName(); 695 vkCollect.put(name, key); 696 return name.substring(3); 697 } 698 } catch (IllegalAccessException e) { 699 assert(false); 700 } 701 } 702 return "UNKNOWN"; 703 } 704 705 /** 706 * Returns a cached instance of {@code AWTKeyStroke} (or a subclass of 707 * {@code AWTKeyStroke}) which is equal to this instance. 708 * 709 * @return a cached instance which is equal to this instance 710 * @throws java.io.ObjectStreamException if a serialization problem occurs 711 */ 712 protected Object readResolve() throws java.io.ObjectStreamException { 713 synchronized (AWTKeyStroke.class) { 714 715 return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease); 716 } 717 } 718 719 @SuppressWarnings("deprecation") 720 private static int mapOldModifiers(int modifiers) { 721 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 722 modifiers |= InputEvent.SHIFT_DOWN_MASK; 723 } 724 if ((modifiers & InputEvent.ALT_MASK) != 0) { 725 modifiers |= InputEvent.ALT_DOWN_MASK; 726 } 727 if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 728 modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK; 729 } 730 if ((modifiers & InputEvent.CTRL_MASK) != 0) { 731 modifiers |= InputEvent.CTRL_DOWN_MASK; 732 } 733 if ((modifiers & InputEvent.META_MASK) != 0) { 734 modifiers |= InputEvent.META_DOWN_MASK; 735 } 736 737 modifiers &= InputEvent.SHIFT_DOWN_MASK 738 | InputEvent.ALT_DOWN_MASK 739 | InputEvent.ALT_GRAPH_DOWN_MASK 740 | InputEvent.CTRL_DOWN_MASK 741 | InputEvent.META_DOWN_MASK 742 | InputEvent.BUTTON1_DOWN_MASK 743 | InputEvent.BUTTON2_DOWN_MASK 744 | InputEvent.BUTTON3_DOWN_MASK; 745 746 return modifiers; 747 } 748 749 @SuppressWarnings("deprecation") 750 private static int mapNewModifiers(int modifiers) { 751 if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) { 752 modifiers |= InputEvent.SHIFT_MASK; 753 } 754 if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) { 755 modifiers |= InputEvent.ALT_MASK; 756 } 757 if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) { 758 modifiers |= InputEvent.ALT_GRAPH_MASK; 759 } 760 if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) { 761 modifiers |= InputEvent.CTRL_MASK; 762 } 763 if ((modifiers & InputEvent.META_DOWN_MASK) != 0) { 764 modifiers |= InputEvent.META_MASK; 765 } 766 767 return modifiers; 768 } 769 770} 771 772class VKCollection { 773 Map<Integer, String> code2name; 774 Map<String, Integer> name2code; 775 776 public VKCollection() { 777 code2name = new HashMap<>(); 778 name2code = new HashMap<>(); 779 } 780 781 public synchronized void put(String name, Integer code) { 782 assert((name != null) && (code != null)); 783 assert(findName(code) == null); 784 assert(findCode(name) == null); 785 code2name.put(code, name); 786 name2code.put(name, code); 787 } 788 789 public synchronized Integer findCode(String name) { 790 assert(name != null); 791 return name2code.get(name); 792 } 793 794 public synchronized String findName(Integer code) { 795 assert(code != null); 796 return code2name.get(code); 797 } 798} 799