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