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