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 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", 418 Integer.valueOf(InputEvent.SHIFT_DOWN_MASK 419 |InputEvent.SHIFT_MASK)); 420 uninitializedMap.put("control", 421 Integer.valueOf(InputEvent.CTRL_DOWN_MASK 422 |InputEvent.CTRL_MASK)); 423 uninitializedMap.put("ctrl", 424 Integer.valueOf(InputEvent.CTRL_DOWN_MASK 425 |InputEvent.CTRL_MASK)); 426 uninitializedMap.put("meta", 427 Integer.valueOf(InputEvent.META_DOWN_MASK 428 |InputEvent.META_MASK)); 429 uninitializedMap.put("alt", 430 Integer.valueOf(InputEvent.ALT_DOWN_MASK 431 |InputEvent.ALT_MASK)); 432 uninitializedMap.put("altGraph", 433 Integer.valueOf(InputEvent.ALT_GRAPH_DOWN_MASK 434 |InputEvent.ALT_GRAPH_MASK)); 435 uninitializedMap.put("button1", 436 Integer.valueOf(InputEvent.BUTTON1_DOWN_MASK)); 437 uninitializedMap.put("button2", 438 Integer.valueOf(InputEvent.BUTTON2_DOWN_MASK)); 439 uninitializedMap.put("button3", 440 Integer.valueOf(InputEvent.BUTTON3_DOWN_MASK)); 441 modifierKeywords = 442 Collections.synchronizedMap(uninitializedMap); 443 } 444 } 445 446 int count = st.countTokens(); 447 448 for (int i = 1; i <= count; i++) { 449 String token = st.nextToken(); 450 451 if (typed) { 452 if (token.length() != 1 || i != count) { 453 throw new IllegalArgumentException(errmsg); 454 } 455 return getCachedStroke(token.charAt(0), KeyEvent.VK_UNDEFINED, 456 mask, false); 457 } 458 459 if (pressed || released || i == count) { 460 if (i != count) { 461 throw new IllegalArgumentException(errmsg); 462 } 463 464 String keyCodeName = "VK_" + token; 465 int keyCode = getVKValue(keyCodeName); 466 467 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, 468 mask, released); 469 } 470 471 if (token.equals("released")) { 472 released = true; 473 continue; 474 } 475 if (token.equals("pressed")) { 476 pressed = true; 477 continue; 478 } 479 if (token.equals("typed")) { 480 typed = true; 481 continue; 482 } 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) + 599 (onKeyRelease ? 1 : 2); 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 "); 642 } 643 if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0 ) { 644 buf.append("ctrl "); 645 } 646 if ((modifiers & InputEvent.META_DOWN_MASK) != 0 ) { 647 buf.append("meta "); 648 } 649 if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0 ) { 650 buf.append("alt "); 651 } 652 if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0 ) { 653 buf.append("altGraph "); 654 } 655 if ((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0 ) { 656 buf.append("button1 "); 657 } 658 if ((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0 ) { 659 buf.append("button2 "); 660 } 661 if ((modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0 ) { 662 buf.append("button3 "); 663 } 664 665 return buf.toString(); 666 } 667 668 static String getVKText(int keyCode) { 669 VKCollection vkCollect = getVKCollection(); 670 Integer key = Integer.valueOf(keyCode); 671 String name = vkCollect.findName(key); 672 if (name != null) { 673 return name.substring(3); 674 } 675 int expected_modifiers = 676 (Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL); 677 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; 720 } 721 if ((modifiers & InputEvent.CTRL_MASK) != 0) { 722 modifiers |= InputEvent.CTRL_DOWN_MASK; 723 } 724 if ((modifiers & InputEvent.META_MASK) != 0) { 725 modifiers |= InputEvent.META_DOWN_MASK; 726 } 727 728 modifiers &= InputEvent.SHIFT_DOWN_MASK 729 | InputEvent.ALT_DOWN_MASK 730 | InputEvent.ALT_GRAPH_DOWN_MASK 731 | InputEvent.CTRL_DOWN_MASK 732 | InputEvent.META_DOWN_MASK 733 | InputEvent.BUTTON1_DOWN_MASK 734 | InputEvent.BUTTON2_DOWN_MASK 735 | InputEvent.BUTTON3_DOWN_MASK; 736 737 return modifiers; 738 } 739 740 private static int mapNewModifiers(int modifiers) { 741 if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) { 742 modifiers |= InputEvent.SHIFT_MASK; 743 } 744 if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) { 745 modifiers |= InputEvent.ALT_MASK; 746 } 747 if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) { 748 modifiers |= InputEvent.ALT_GRAPH_MASK; 749 } 750 if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) { 751 modifiers |= InputEvent.CTRL_MASK; 752 } 753 if ((modifiers & InputEvent.META_DOWN_MASK) != 0) { 754 modifiers |= InputEvent.META_MASK; 755 } 756 757 return modifiers; 758 } 759 760 } 761 762 class VKCollection { 763 Map<Integer, String> code2name; 764 Map<String, Integer> name2code; 765 766 public VKCollection() { 767 code2name = new HashMap<>(); 768 name2code = new HashMap<>(); 769 } 770 771 public synchronized void put(String name, Integer code) { 772 assert((name != null) && (code != null)); 773 assert(findName(code) == null); 774 assert(findCode(name) == null); 775 code2name.put(code, name); 776 name2code.put(name, code); 777 } 778 779 public synchronized Integer findCode(String name) { 780 assert(name != null); 781 return name2code.get(name); 782 } 783 784 public synchronized String findName(Integer code) { 785 assert(code != null); 786 return code2name.get(code); 787 } 788 }