1. /*
  2. * @(#)KeyStroke.java 1.38 00/07/26
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing;
  11. import java.awt.event.KeyEvent;
  12. import java.awt.event.InputEvent;
  13. import java.util.Hashtable;
  14. import java.util.StringTokenizer;
  15. import java.io.Serializable;
  16. /**
  17. * A KeyStroke instance represents a key being typed on the keyboard -- it
  18. * contains both a char code for the key and a modifier (alt, shift, ctrl,
  19. * meta, or a combination).
  20. * <p>
  21. * KeyStroke objects are used to define high-level (semantic) action events.
  22. * Instead of trapping every keystroke and throwing away the ones you are
  23. * not interested in, those keystrokes you care about automatically initiate
  24. * actions on the components they are registered with.
  25. * <p>
  26. * KeyStroke objects handle both character-code generating keystrokes you
  27. * would trap with a KeyTyped event handler and key-code generating keystrokes
  28. * (like Enter or F1) that you would trap with a KeyPressed event handler.
  29. * <p>
  30. * KeyStroke objects are immutable and unique.
  31. * <p>
  32. * All KeyStroke objects are cached. To get one, use <code>getKeyStroke</code>.
  33. * <p>
  34. * <strong>Warning:</strong>
  35. * Serialized objects of this class will not be compatible with
  36. * future Swing releases. The current serialization support is appropriate
  37. * for short term storage or RMI between applications running the same
  38. * version of Swing. A future release of Swing will provide support for
  39. * long term persistence.
  40. *
  41. * @see javax.swing.text.Keymap
  42. * @see #getKeyStroke
  43. *
  44. * @version 1.38 07/26/00
  45. * @author Arnaud Weber
  46. */
  47. public class KeyStroke implements Serializable {
  48. private static final Object pressedCharCacheKey =
  49. new StringBuffer("KeyStroke.pressedCharCacheKey");
  50. private static final Object releasedCharCacheKey =
  51. new StringBuffer("KeyStroke.releasedCharCacheKey");
  52. private static final Object pressedCodeCacheKey =
  53. new StringBuffer("KeyStroke.pressedCodeCacheKey");
  54. private static final Object releasedCodeCacheKey =
  55. new StringBuffer("KeyStroke.releasedCodeCacheKey");
  56. char keyChar;
  57. int keyCode;
  58. int modifiers;
  59. boolean onKeyRelease;
  60. /* We cache 114 key stroke with keyChar (1368 bytes) */
  61. /* We cache 114 * 8 key stroke with keyCode and popular modifiers (10944 bytes) */
  62. /* Total cache is around 11K */
  63. static final int MIN_ASCII_CACHE_INDEX = '\n';
  64. static final int MAX_ASCII_CACHE_INDEX = 0x7F;
  65. /* Lock object used in place of class object for synchronization.
  66. * (4187686)
  67. */
  68. private static final Object classLock = new Object();
  69. /* It is impossible to instantiate a KeyStroke. Use getKeyStroke()
  70. * instead */
  71. private KeyStroke() {
  72. }
  73. static KeyStroke getCachedKeyCharKeyStroke(char keyChar,boolean onKeyRelease) {
  74. KeyStroke result = null;
  75. if(keyChar >= MIN_ASCII_CACHE_INDEX && keyChar < MAX_ASCII_CACHE_INDEX) {
  76. synchronized(classLock) {
  77. KeyStroke cache[];
  78. if(onKeyRelease)
  79. cache = (KeyStroke[])SwingUtilities.appContextGet(
  80. releasedCharCacheKey);
  81. else
  82. cache = (KeyStroke[])SwingUtilities.appContextGet(
  83. pressedCharCacheKey);
  84. if(cache != null)
  85. result = cache[((int)keyChar) - MIN_ASCII_CACHE_INDEX];
  86. }
  87. }
  88. return result;
  89. }
  90. static void cacheKeyCharKeyStroke(KeyStroke ks,boolean onKeyRelease) {
  91. if(ks.keyChar >= MIN_ASCII_CACHE_INDEX && ks.keyChar < MAX_ASCII_CACHE_INDEX) {
  92. synchronized(classLock) {
  93. if(onKeyRelease) {
  94. KeyStroke releasedKeyCharKeyStrokeCache[] = (KeyStroke[])
  95. SwingUtilities.appContextGet(releasedCharCacheKey);
  96. if(releasedKeyCharKeyStrokeCache == null) {
  97. releasedKeyCharKeyStrokeCache = new KeyStroke[MAX_ASCII_CACHE_INDEX - MIN_ASCII_CACHE_INDEX];
  98. SwingUtilities.appContextPut(
  99. releasedCharCacheKey, releasedKeyCharKeyStrokeCache);
  100. }
  101. releasedKeyCharKeyStrokeCache[((int)ks.keyChar) - MIN_ASCII_CACHE_INDEX] = ks;
  102. } else {
  103. KeyStroke pressedKeyCharKeyStrokeCache[] = (KeyStroke[])
  104. SwingUtilities.appContextGet(pressedCharCacheKey);
  105. if(pressedKeyCharKeyStrokeCache == null) {
  106. pressedKeyCharKeyStrokeCache = new KeyStroke[MAX_ASCII_CACHE_INDEX - MIN_ASCII_CACHE_INDEX];
  107. SwingUtilities.appContextPut(
  108. pressedCharCacheKey, pressedKeyCharKeyStrokeCache);
  109. }
  110. pressedKeyCharKeyStrokeCache[((int)ks.keyChar) - MIN_ASCII_CACHE_INDEX] = ks;
  111. }
  112. }
  113. }
  114. }
  115. static int subIndexForModifier(int modifiers) {
  116. if(modifiers == 0)
  117. return 0;
  118. else if(modifiers == InputEvent.SHIFT_MASK)
  119. return 1;
  120. else if(modifiers == InputEvent.CTRL_MASK)
  121. return 2;
  122. else if(modifiers == InputEvent.ALT_MASK)
  123. return 3;
  124. return -1;
  125. }
  126. static KeyStroke getCachedKeyStroke(int keyCode,int modifiers,boolean onKeyRelease) {
  127. int subIndex;
  128. KeyStroke result = null;
  129. if(keyCode >= MIN_ASCII_CACHE_INDEX && keyCode < MAX_ASCII_CACHE_INDEX &&
  130. (subIndex = subIndexForModifier(modifiers)) != -1) {
  131. synchronized(classLock) {
  132. KeyStroke cache[][];
  133. if(onKeyRelease)
  134. cache = (KeyStroke[][])SwingUtilities.appContextGet(
  135. pressedCodeCacheKey);
  136. else
  137. cache = (KeyStroke[][])SwingUtilities.appContextGet(
  138. releasedCodeCacheKey);
  139. if(cache != null)
  140. result = cache[subIndex][keyCode - MIN_ASCII_CACHE_INDEX];
  141. }
  142. }
  143. return result;
  144. }
  145. static void cacheKeyStroke(KeyStroke ks) {
  146. int subIndex = -1;
  147. if(ks.keyCode >= MIN_ASCII_CACHE_INDEX && ks.keyCode < MAX_ASCII_CACHE_INDEX &&
  148. (subIndex = subIndexForModifier(ks.modifiers)) != -1) {
  149. synchronized(classLock) {
  150. KeyStroke cache[][] = null;
  151. if(ks.onKeyRelease) {
  152. KeyStroke[][] pressedKeyCodeKeyStrokeCache = (KeyStroke[][])
  153. SwingUtilities.appContextGet(pressedCodeCacheKey);
  154. if(pressedKeyCodeKeyStrokeCache == null) {
  155. pressedKeyCodeKeyStrokeCache = new KeyStroke[4][MAX_ASCII_CACHE_INDEX -
  156. MIN_ASCII_CACHE_INDEX];
  157. SwingUtilities.appContextPut(
  158. pressedCodeCacheKey, pressedKeyCodeKeyStrokeCache);
  159. }
  160. cache = pressedKeyCodeKeyStrokeCache;
  161. } else {
  162. KeyStroke[][] releasedKeyCodeKeyStrokeCache = (KeyStroke[][])
  163. SwingUtilities.appContextGet(releasedCodeCacheKey);
  164. if(releasedKeyCodeKeyStrokeCache == null) {
  165. releasedKeyCodeKeyStrokeCache = new KeyStroke[4][MAX_ASCII_CACHE_INDEX -
  166. MIN_ASCII_CACHE_INDEX];
  167. SwingUtilities.appContextPut(
  168. releasedCodeCacheKey, releasedKeyCodeKeyStrokeCache);
  169. }
  170. cache = releasedKeyCodeKeyStrokeCache;
  171. }
  172. cache[subIndex][ks.keyCode - MIN_ASCII_CACHE_INDEX] = ks;
  173. }
  174. }
  175. }
  176. /**
  177. * Return a shared instance of a key stroke that is
  178. * activated when the key is pressed (that is, a KeyStroke
  179. * for the KEY_TYPED event).
  180. *
  181. * @param keyChar the character value for a keyboard key
  182. * @return a KeyStroke object for that key
  183. */
  184. public static KeyStroke getKeyStroke(char keyChar) {
  185. return getKeyStroke(keyChar,false);
  186. }
  187. /**
  188. * Return a shared instance of a key stroke, specifying
  189. * whether the key is considered to be activated when it is
  190. * pressed or when it is released.
  191. *
  192. * @param keyChar the character value for a keyboard key
  193. * @param onKeyRelease a boolean value. When true, specifies that
  194. * the key is active when it is released.
  195. * @return a KeyStroke object for that key
  196. * @deprecated use getKeyStroke(char)
  197. */
  198. public static KeyStroke getKeyStroke(char keyChar,boolean onKeyRelease) {
  199. KeyStroke result = getCachedKeyCharKeyStroke(keyChar,onKeyRelease);
  200. if(result == null) {
  201. result = new KeyStroke();
  202. result.keyChar = keyChar;
  203. result.modifiers = 0;
  204. result.onKeyRelease = onKeyRelease;
  205. cacheKeyCharKeyStroke(result,onKeyRelease);
  206. }
  207. return result;
  208. }
  209. /**
  210. * Return a shared instance of a key stroke, given a character object
  211. * and a set of modifiers. Note that the first
  212. * parameter is of type Character rather
  213. * than char. This is to avoid clashes with code that existed prior
  214. * to the introduction of this method, which may have been calling
  215. * getKeyStroke(int keyCode, int modifiers) with quoted characters
  216. * and relying on their automatic conversion to type int.
  217. *
  218. * The modifiers consist of any combination of:<ul>
  219. * <li>java.awt.Event.SHIFT_MASK (1)
  220. * <li>java.awt.Event.CTRL_MASK (2)
  221. * <li>java.awt.Event.META_MASK (4)
  222. * <li>java.awt.Event.ALT_MASK (8)
  223. * </ul>
  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
  226. * modifier key.
  227. *
  228. * @param keyChar the character object for a keyboard character.
  229. * @param modifiers an int specifying any combination of the key modifiers.
  230. * @return a KeyStroke object for that key
  231. *
  232. * @since 1.3
  233. */
  234. public static KeyStroke getKeyStroke(Character keyChar, int modifiers) {
  235. /* For now, we don't cache key strokes constructed from a keyChar and
  236. modifiers. */
  237. KeyStroke result = null;
  238. if(result == null) {
  239. result = new KeyStroke();
  240. result.keyChar = keyChar.charValue();
  241. result.modifiers = modifiers;
  242. result.onKeyRelease = false;
  243. }
  244. return result;
  245. }
  246. /**
  247. * Return a shared instance of a key stroke given a numeric keycode and a set
  248. * of modifiers, specifying whether the key is activated when it is pressed
  249. * or released.
  250. * <p>
  251. * The "virtual key" constants defined in java.awt.event.KeyEvent can be
  252. * used to specify the key code. For example:<ul>
  253. * <li>java.awt.event.KeyEvent.VK_ENTER
  254. * <li>java.awt.event.KeyEvent.VK_TAB
  255. * <li>java.awt.event.KeyEvent.VK_SPACE
  256. * </ul>
  257. * The modifiers consist of any combination of:<ul>
  258. * <li>java.awt.Event.SHIFT_MASK (1)
  259. * <li>java.awt.Event.CTRL_MASK (2)
  260. * <li>java.awt.Event.META_MASK (4)
  261. * <li>java.awt.Event.ALT_MASK (8)
  262. * </ul>
  263. * Since these numbers are all different powers of two, any combination of
  264. * them is an integer in which each bit represents a different
  265. * modifier key.
  266. *
  267. * @param keyCode an int specifying the numeric code for a keyboard key
  268. * @param modifiers an int specifying any combination of the key modifiers.
  269. * @param onKeyRelease a boolean value. When true, specifies that
  270. * the key is active when it is released.
  271. * @return a KeyStroke object for that key
  272. *
  273. * @see java.awt.event.KeyEvent
  274. * @see java.awt.Event
  275. */
  276. public static KeyStroke getKeyStroke(int keyCode,int modifiers, boolean onKeyRelease) {
  277. KeyStroke result = getCachedKeyStroke(keyCode,modifiers,onKeyRelease);
  278. if(result == null) {
  279. result = new KeyStroke();
  280. result.keyCode = keyCode;
  281. result.modifiers = modifiers;
  282. result.onKeyRelease = onKeyRelease;
  283. cacheKeyStroke(result);
  284. }
  285. return result;
  286. }
  287. /**
  288. * Return a shared instance of a key stroke given a char code and a set
  289. * of modifiers -- the key is activated when it is pressed.
  290. * <p>
  291. * <p>
  292. * The "virtual key" constants defined in java.awt.event.KeyEvent can be
  293. * used to specify the key code. For example:<ul>
  294. * <li>java.awt.event.KeyEvent.VK_ENTER
  295. * <li>java.awt.event.KeyEvent.VK_TAB
  296. * <li>java.awt.event.KeyEvent.VK_SPACE
  297. * </ul>
  298. * The modifiers consist of any combination of:<ul>
  299. * <li>java.awt.Event.SHIFT_MASK (1)
  300. * <li>java.awt.Event.CTRL_MASK (2)
  301. * <li>java.awt.Event.META_MASK (4)
  302. * <li>java.awt.Event.ALT_MASK (8)
  303. * </ul>
  304. * Since these numbers are all different powers of two, any combination of
  305. * them is an integer in which each bit represents a different
  306. * modifier key.
  307. *
  308. * @param keyCode an int specifying the numeric code for a keyboard key
  309. * @param modifiers an int specifying any combination of the key modifiers.
  310. * @return a KeyStroke object for that key
  311. * @see java.awt.event.KeyEvent
  312. */
  313. public static KeyStroke getKeyStroke(int keyCode,int modifiers) {
  314. return getKeyStroke(keyCode,modifiers,false);
  315. }
  316. /**
  317. * Return a keystroke from an event.
  318. * <p>
  319. * This method obtains the keyChar from a KeyTyped event,
  320. * and the keyCode from a KeyPressed or KeyReleased event,
  321. * so you the type of event doesn't matter.
  322. *
  323. * @param anEvent the KeyEvent to obtain the KeyStroke from
  324. * @return the KeyStroke that precipitated the event
  325. */
  326. public static KeyStroke getKeyStrokeForEvent(KeyEvent anEvent) {
  327. KeyStroke ks = null;
  328. switch(anEvent.getID()) {
  329. case KeyEvent.KEY_PRESSED:
  330. ks = getKeyStroke(anEvent.getKeyCode(),anEvent.getModifiers(),false);
  331. break;
  332. case KeyEvent.KEY_RELEASED:
  333. ks = getKeyStroke(anEvent.getKeyCode(),anEvent.getModifiers(),true);
  334. break;
  335. case KeyEvent.KEY_TYPED:
  336. ks = getKeyStroke(anEvent.getKeyChar());
  337. break;
  338. }
  339. return ks;
  340. }
  341. /**
  342. * Parse a string and return a KeyStroke.
  343. * The string has the following syntax:
  344. * <pre>
  345. * <modifiers>* (<typedID> | <pressedReleasedID>)
  346. * modifiers := shift | control | meta | alt | button1 | button2 | button3
  347. * typedID := typed <typedKey>
  348. * typedKey := string of length 1 giving unicode character.
  349. * pressedReleasedID := (pressed | released)? key
  350. * key := KeyEvent keycode name, i.e. the name following "VK_".
  351. * </pre>
  352. * If typed, pressed or released is not specified, pressed is assumed.
  353. * Here are some examples:
  354. * <pre>
  355. * "INSERT" => new KeyStroke(0, KeyEvent.VK_INSERT);
  356. * "control DELETE" => new KeyStroke(InputEvent.CTRL_MASK, KeyEvent.VK_DELETE);
  357. * "alt shift X" => new KeyStroke(InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, KeyEvent.VK_X);
  358. * "alt shift released X" => new KeyStroke(InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, KeyEvent.VK_X, true);
  359. * "typed a" => new KeyStroke('a');
  360. * </pre>
  361. * For backward compatability, this will return null if <code>s</code> is
  362. * not in the above format.
  363. */
  364. public static KeyStroke getKeyStroke(String s) {
  365. if (s == null || s.length() == 0) {
  366. return null;
  367. }
  368. StringTokenizer st = new StringTokenizer(s);
  369. String token;
  370. int mask = 0;
  371. boolean released = false;
  372. boolean typed = false;
  373. while((token = st.nextToken()) != null) {
  374. int tokenMask = 0;
  375. /* if token matches a modifier keyword update mask and continue */
  376. for(int i = 0; (tokenMask == 0) && (i < modifierKeywords.length);i++) {
  377. tokenMask = modifierKeywords[i].getModifierMask(token);
  378. }
  379. if (tokenMask != 0) {
  380. mask |= tokenMask;
  381. continue;
  382. }
  383. if (token.equals("released")) {
  384. released = true;
  385. continue;
  386. }
  387. if (token.equals("pressed")) {
  388. continue;
  389. }
  390. if (token.equals("typed")) {
  391. typed = true;
  392. continue;
  393. }
  394. if (typed) {
  395. if (token.length() != 1) {
  396. // bogus format, should really throw.
  397. return null;
  398. }
  399. return KeyStroke.getKeyStroke(token.charAt(0), released);
  400. }
  401. /* otherwise token is the keycode name less the "VK_" prefix */
  402. String keycodeName = "VK_" + token;
  403. int keycode;
  404. try {
  405. keycode = KeyEvent.class.getField(keycodeName).getInt(KeyEvent.class);
  406. }
  407. catch (Exception e) {
  408. // Should really throw here.
  409. return null;
  410. }
  411. return KeyStroke.getKeyStroke(keycode, mask, released);
  412. }
  413. return null;
  414. }
  415. /*
  416. * // see getKeyStroke (String)
  417. */
  418. private static class ModifierKeyword {
  419. final String keyword;
  420. final int mask;
  421. ModifierKeyword(String keyword, int mask) {
  422. this.keyword = keyword;
  423. this.mask = mask;
  424. }
  425. int getModifierMask(String s) {
  426. return (s.equals(keyword)) ? mask : 0;
  427. }
  428. };
  429. /*
  430. * // see getKeyStroke (String)
  431. */
  432. private static ModifierKeyword[] modifierKeywords = {
  433. new ModifierKeyword("shift", InputEvent.SHIFT_MASK),
  434. new ModifierKeyword("control", InputEvent.CTRL_MASK),
  435. new ModifierKeyword("ctrl", InputEvent.CTRL_MASK),
  436. new ModifierKeyword("meta", InputEvent.META_MASK),
  437. new ModifierKeyword("alt", InputEvent.ALT_MASK),
  438. new ModifierKeyword("button1", InputEvent.BUTTON1_MASK),
  439. new ModifierKeyword("button2", InputEvent.BUTTON2_MASK),
  440. new ModifierKeyword("button3", InputEvent.BUTTON3_MASK)
  441. };
  442. /**
  443. * Returns the character defined by this KeyStroke object.
  444. * @return a char value
  445. * @see #getKeyStroke(char)
  446. */
  447. public char getKeyChar() { return keyChar; }
  448. /**
  449. * Returns the numeric keycode defined by this KeyStroke object.
  450. * @return an int containing the keycode value
  451. * @see #getKeyStroke(int,int)
  452. */
  453. public int getKeyCode() { return keyCode; }
  454. /**
  455. * Returns the modifier keys defined by this KeyStroke object.
  456. * @return an int containing the modifiers
  457. * @see #getKeyStroke(int,int)
  458. */
  459. public int getModifiers() { return modifiers; }
  460. /**
  461. * Returns true if this keystroke is active on key release.
  462. * @return true if active on key release, false if active on key press
  463. * @see #getKeyStroke(int,int,boolean)
  464. */
  465. public boolean isOnKeyRelease() { return onKeyRelease; }
  466. private static String getStringRepresentation(char keyChar,int modifiers,boolean kr) {
  467. return "keyChar " + KeyEvent.getKeyModifiersText(modifiers) + keyChar +
  468. (kr?"-R":"-P");
  469. }
  470. private static String getStringRepresentation(int keyCode,int modifiers,boolean kr) {
  471. return "keyCode " + KeyEvent.getKeyModifiersText(modifiers) + KeyEvent.getKeyText(keyCode) +
  472. (kr?"-R":"-P");
  473. }
  474. /**
  475. * Returns a numeric value for this object that is likely to be
  476. * reasonably unique, so it can be used as the index value in a
  477. * Hashtable.
  478. *
  479. * @return an int that "represents" this object
  480. * @see java.util.Hashtable
  481. */
  482. public int hashCode() {
  483. return (((int) keyChar) + 1) * (2 * (keyCode + 1)) * (modifiers+1) +
  484. (onKeyRelease ? 1 : 2);
  485. }
  486. /**
  487. * Returns true if this object is identical to the specified object.
  488. *
  489. * @param anObject the Object to compare this object to
  490. * @return true if the objects are identical
  491. */
  492. public boolean equals(Object anObject) {
  493. if(anObject instanceof KeyStroke) {
  494. KeyStroke ks = (KeyStroke) anObject;
  495. if(ks.keyChar == keyChar && ks.keyCode == keyCode &&
  496. ks.onKeyRelease == onKeyRelease && ks.modifiers == modifiers)
  497. return true;
  498. }
  499. return false;
  500. }
  501. /**
  502. * Returns a string that displays and identifies this
  503. * object's properties.
  504. *
  505. * @return a String representation of this object
  506. */
  507. public String toString() {
  508. if(keyChar == 0)
  509. return getStringRepresentation(keyCode,modifiers,onKeyRelease);
  510. else
  511. return getStringRepresentation(keyChar,0,onKeyRelease);
  512. }
  513. }