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