001: /*
002: * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025: package java.awt;
026:
027: import java.awt.event.KeyEvent;
028: import java.awt.event.InputEvent;
029: import java.util.Collections;
030: import java.util.HashMap;
031: import java.util.Map;
032: import java.util.StringTokenizer;
033: import java.io.Serializable;
034: import java.security.AccessController;
035: import java.security.PrivilegedAction;
036: import java.lang.reflect.Constructor;
037: import java.lang.reflect.InvocationTargetException;
038: import java.lang.reflect.Modifier;
039: import java.lang.reflect.Field;
040:
041: /**
042: * An <code>AWTKeyStroke</code> represents a key action on the
043: * keyboard, or equivalent input device. <code>AWTKeyStroke</code>s
044: * can correspond to only a press or release of a
045: * particular key, just as <code>KEY_PRESSED</code> and
046: * <code>KEY_RELEASED</code> <code>KeyEvent</code>s do;
047: * alternately, they can correspond to typing a specific Java character, just
048: * as <code>KEY_TYPED</code> <code>KeyEvent</code>s do.
049: * In all cases, <code>AWTKeyStroke</code>s can specify modifiers
050: * (alt, shift, control, meta, altGraph, or a combination thereof) which must be present
051: * during the action for an exact match.
052: * <p>
053: * <code>AWTKeyStrokes</code> are immutable, and are intended
054: * to be unique. Client code should never create an
055: * <code>AWTKeyStroke</code> on its own, but should instead use
056: * a variant of <code>getAWTKeyStroke</code>. Client use of these factory
057: * methods allows the <code>AWTKeyStroke</code> implementation
058: * to cache and share instances efficiently.
059: *
060: * @see #getAWTKeyStroke
061: *
062: * @version 1.36, 05/05/07
063: * @author Arnaud Weber
064: * @author David Mendenhall
065: * @since 1.4
066: */
067: public class AWTKeyStroke implements Serializable {
068: static final long serialVersionUID = -6430539691155161871L;
069:
070: private static Map cache;
071: private static AWTKeyStroke cacheKey;
072: private static Constructor ctor = getCtor(AWTKeyStroke.class);
073: private static Map modifierKeywords;
074: /**
075: * Associates VK_XXX (as a String) with code (as Integer). This is
076: * done to avoid the overhead of the reflective call to find the
077: * constant.
078: */
079: private static VKCollection vks;
080:
081: private char keyChar = KeyEvent.CHAR_UNDEFINED;
082: private int keyCode = KeyEvent.VK_UNDEFINED;
083: private int modifiers;
084: private boolean onKeyRelease;
085:
086: static {
087: /* ensure that the necessary native libraries are loaded */
088: Toolkit.loadLibraries();
089: }
090:
091: /**
092: * Constructs an <code>AWTKeyStroke</code> with default values.
093: * The default values used are:
094: * <table border="1" summary="AWTKeyStroke default values">
095: * <tr><th>Property</th><th>Default Value</th></tr>
096: * <tr>
097: * <td>Key Char</td>
098: * <td><code>KeyEvent.CHAR_UNDEFINED</code></td>
099: * </tr>
100: * <tr>
101: * <td>Key Code</td>
102: * <td><code>KeyEvent.VK_UNDEFINED</code></td>
103: * </tr>
104: * <tr>
105: * <td>Modifiers</td>
106: * <td>none</td>
107: * </tr>
108: * <tr>
109: * <td>On key release?</td>
110: * <td><code>false</code></td>
111: * </tr>
112: * </table>
113: *
114: * <code>AWTKeyStroke</code>s should not be constructed
115: * by client code. Use a variant of <code>getAWTKeyStroke</code>
116: * instead.
117: *
118: * @see #getAWTKeyStroke
119: */
120: protected AWTKeyStroke() {
121: }
122:
123: /**
124: * Constructs an <code>AWTKeyStroke</code> with the specified
125: * values. <code>AWTKeyStroke</code>s should not be constructed
126: * by client code. Use a variant of <code>getAWTKeyStroke</code>
127: * instead.
128: *
129: * @param keyChar the character value for a keyboard key
130: * @param keyCode the key code for this <code>AWTKeyStroke</code>
131: * @param modifiers a bitwise-ored combination of any modifiers
132: * @param onKeyRelease <code>true</code> if this
133: * <code>AWTKeyStroke</code> corresponds
134: * to a key release; <code>false</code> otherwise
135: * @see #getAWTKeyStroke
136: */
137: protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
138: boolean onKeyRelease) {
139: this .keyChar = keyChar;
140: this .keyCode = keyCode;
141: this .modifiers = modifiers;
142: this .onKeyRelease = onKeyRelease;
143: }
144:
145: /**
146: * Registers a new class which the factory methods in
147: * <code>AWTKeyStroke</code> will use when generating new
148: * instances of <code>AWTKeyStroke</code>s. After invoking this
149: * method, the factory methods will return instances of the specified
150: * Class. The specified Class must be either <code>AWTKeyStroke</code>
151: * or derived from <code>AWTKeyStroke</code>, and it must have a
152: * no-arg constructor. The constructor can be of any accessibility,
153: * including <code>private</code>. This operation
154: * flushes the current <code>AWTKeyStroke</code> cache.
155: *
156: * @param subclass the new Class of which the factory methods should create
157: * instances
158: * @throws IllegalArgumentException if subclass is <code>null</code>,
159: * or if subclass does not have a no-arg constructor
160: * @throws ClassCastException if subclass is not
161: * <code>AWTKeyStroke</code>, or a class derived from
162: * <code>AWTKeyStroke</code>
163: */
164: protected static void registerSubclass(Class<?> subclass) {
165: if (subclass == null) {
166: throw new IllegalArgumentException(
167: "subclass cannot be null");
168: }
169: if (AWTKeyStroke.ctor.getDeclaringClass().equals(subclass)) {
170: // Already registered
171: return;
172: }
173: if (!AWTKeyStroke.class.isAssignableFrom(subclass)) {
174: throw new ClassCastException(
175: "subclass is not derived from AWTKeyStroke");
176: }
177:
178: Constructor ctor = getCtor(subclass);
179:
180: String couldNotInstantiate = "subclass could not be instantiated";
181:
182: if (ctor == null) {
183: throw new IllegalArgumentException(couldNotInstantiate);
184: }
185: try {
186: AWTKeyStroke stroke = (AWTKeyStroke) ctor
187: .newInstance((Object[]) null);
188: if (stroke == null) {
189: throw new IllegalArgumentException(couldNotInstantiate);
190: }
191: } catch (NoSuchMethodError e) {
192: throw new IllegalArgumentException(couldNotInstantiate);
193: } catch (ExceptionInInitializerError e) {
194: throw new IllegalArgumentException(couldNotInstantiate);
195: } catch (InstantiationException e) {
196: throw new IllegalArgumentException(couldNotInstantiate);
197: } catch (IllegalAccessException e) {
198: throw new IllegalArgumentException(couldNotInstantiate);
199: } catch (InvocationTargetException e) {
200: throw new IllegalArgumentException(couldNotInstantiate);
201: }
202:
203: synchronized (AWTKeyStroke.class) {
204: AWTKeyStroke.ctor = ctor;
205: cache = null;
206: cacheKey = null;
207: }
208: }
209:
210: /* returns noarg Constructor for class with accessible flag. No security
211: threat as accessible flag is set only for this Constructor object,
212: not for Class constructor.
213: */
214: private static Constructor getCtor(final Class clazz) {
215: Object ctor = AccessController
216: .doPrivileged(new PrivilegedAction() {
217: public Object run() {
218: try {
219: Constructor ctor = clazz
220: .getDeclaredConstructor((Class[]) null);
221: if (ctor != null) {
222: ctor.setAccessible(true);
223: }
224: return ctor;
225: } catch (SecurityException e) {
226: } catch (NoSuchMethodException e) {
227: }
228: return null;
229: }
230: });
231: return (Constructor) ctor;
232: }
233:
234: private static synchronized AWTKeyStroke getCachedStroke(
235: char keyChar, int keyCode, int modifiers,
236: boolean onKeyRelease) {
237: if (cache == null) {
238: cache = new HashMap();
239: }
240:
241: if (cacheKey == null) {
242: try {
243: cacheKey = (AWTKeyStroke) ctor
244: .newInstance((Object[]) null);
245: } catch (InstantiationException e) {
246: assert (false);
247: } catch (IllegalAccessException e) {
248: assert (false);
249: } catch (InvocationTargetException e) {
250: assert (false);
251: }
252: }
253: cacheKey.keyChar = keyChar;
254: cacheKey.keyCode = keyCode;
255: cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers));
256: cacheKey.onKeyRelease = onKeyRelease;
257:
258: AWTKeyStroke stroke = (AWTKeyStroke) cache.get(cacheKey);
259: if (stroke == null) {
260: stroke = cacheKey;
261: cache.put(stroke, stroke);
262: cacheKey = null;
263: }
264:
265: return stroke;
266: }
267:
268: /**
269: * Returns a shared instance of an <code>AWTKeyStroke</code>
270: * that represents a <code>KEY_TYPED</code> event for the
271: * specified character.
272: *
273: * @param keyChar the character value for a keyboard key
274: * @return an <code>AWTKeyStroke</code> object for that key
275: */
276: public static AWTKeyStroke getAWTKeyStroke(char keyChar) {
277: return getCachedStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);
278: }
279:
280: /**
281: * Returns a shared instance of an {@code AWTKeyStroke}
282: * that represents a {@code KEY_TYPED} event for the
283: * specified Character object and a set of modifiers. Note
284: * that the first parameter is of type Character rather than
285: * char. This is to avoid inadvertent clashes with
286: * calls to <code>getAWTKeyStroke(int keyCode, int modifiers)</code>.
287: *
288: * The modifiers consist of any combination of following:<ul>
289: * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK
290: * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK
291: * <li>java.awt.event.InputEvent.META_DOWN_MASK
292: * <li>java.awt.event.InputEvent.ALT_DOWN_MASK
293: * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
294: * </ul>
295: * The old modifiers listed below also can be used, but they are
296: * mapped to _DOWN_ modifiers. <ul>
297: * <li>java.awt.event.InputEvent.SHIFT_MASK
298: * <li>java.awt.event.InputEvent.CTRL_MASK
299: * <li>java.awt.event.InputEvent.META_MASK
300: * <li>java.awt.event.InputEvent.ALT_MASK
301: * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK
302: * </ul>
303: * also can be used, but they are mapped to _DOWN_ modifiers.
304: *
305: * Since these numbers are all different powers of two, any combination of
306: * them is an integer in which each bit represents a different modifier
307: * key. Use 0 to specify no modifiers.
308: *
309: * @param keyChar the Character object for a keyboard character
310: * @param modifiers a bitwise-ored combination of any modifiers
311: * @return an <code>AWTKeyStroke</code> object for that key
312: * @throws IllegalArgumentException if <code>keyChar</code> is
313: * <code>null</code>
314: *
315: * @see java.awt.event.InputEvent
316: */
317: public static AWTKeyStroke getAWTKeyStroke(Character keyChar,
318: int modifiers) {
319: if (keyChar == null) {
320: throw new IllegalArgumentException("keyChar cannot be null");
321: }
322: return getCachedStroke(keyChar.charValue(),
323: KeyEvent.VK_UNDEFINED, modifiers, false);
324: }
325:
326: /**
327: * Returns a shared instance of an <code>AWTKeyStroke</code>,
328: * given a numeric key code and a set of modifiers, specifying
329: * whether the key is activated when it is pressed or released.
330: * <p>
331: * The "virtual key" constants defined in
332: * <code>java.awt.event.KeyEvent</code> can be
333: * used to specify the key code. For example:<ul>
334: * <li><code>java.awt.event.KeyEvent.VK_ENTER</code>
335: * <li><code>java.awt.event.KeyEvent.VK_TAB</code>
336: * <li><code>java.awt.event.KeyEvent.VK_SPACE</code>
337: * </ul>
338: * The modifiers consist of any combination of:<ul>
339: * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK
340: * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK
341: * <li>java.awt.event.InputEvent.META_DOWN_MASK
342: * <li>java.awt.event.InputEvent.ALT_DOWN_MASK
343: * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
344: * </ul>
345: * The old modifiers <ul>
346: * <li>java.awt.event.InputEvent.SHIFT_MASK
347: * <li>java.awt.event.InputEvent.CTRL_MASK
348: * <li>java.awt.event.InputEvent.META_MASK
349: * <li>java.awt.event.InputEvent.ALT_MASK
350: * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK
351: * </ul>
352: * also can be used, but they are mapped to _DOWN_ modifiers.
353: *
354: * Since these numbers are all different powers of two, any combination of
355: * them is an integer in which each bit represents a different modifier
356: * key. Use 0 to specify no modifiers.
357: *
358: * @param keyCode an int specifying the numeric code for a keyboard key
359: * @param modifiers a bitwise-ored combination of any modifiers
360: * @param onKeyRelease <code>true</code> if the <code>AWTKeyStroke</code>
361: * should represent a key release; <code>false</code> otherwise
362: * @return an AWTKeyStroke object for that key
363: *
364: * @see java.awt.event.KeyEvent
365: * @see java.awt.event.InputEvent
366: */
367: public static AWTKeyStroke getAWTKeyStroke(int keyCode,
368: int modifiers, boolean onKeyRelease) {
369: return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
370: modifiers, onKeyRelease);
371: }
372:
373: /**
374: * Returns a shared instance of an <code>AWTKeyStroke</code>,
375: * given a numeric key code and a set of modifiers. The returned
376: * <code>AWTKeyStroke</code> will correspond to a key press.
377: * <p>
378: * The "virtual key" constants defined in
379: * <code>java.awt.event.KeyEvent</code> can be
380: * used to specify the key code. For example:<ul>
381: * <li><code>java.awt.event.KeyEvent.VK_ENTER</code>
382: * <li><code>java.awt.event.KeyEvent.VK_TAB</code>
383: * <li><code>java.awt.event.KeyEvent.VK_SPACE</code>
384: * </ul>
385: * The modifiers consist of any combination of:<ul>
386: * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK
387: * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK
388: * <li>java.awt.event.InputEvent.META_DOWN_MASK
389: * <li>java.awt.event.InputEvent.ALT_DOWN_MASK
390: * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
391: * </ul>
392: * The old modifiers <ul>
393: * <li>java.awt.event.InputEvent.SHIFT_MASK
394: * <li>java.awt.event.InputEvent.CTRL_MASK
395: * <li>java.awt.event.InputEvent.META_MASK
396: * <li>java.awt.event.InputEvent.ALT_MASK
397: * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK
398: * </ul>
399: * also can be used, but they are mapped to _DOWN_ modifiers.
400: *
401: * Since these numbers are all different powers of two, any combination of
402: * them is an integer in which each bit represents a different modifier
403: * key. Use 0 to specify no modifiers.
404: *
405: * @param keyCode an int specifying the numeric code for a keyboard key
406: * @param modifiers a bitwise-ored combination of any modifiers
407: * @return an <code>AWTKeyStroke</code> object for that key
408: *
409: * @see java.awt.event.KeyEvent
410: * @see java.awt.event.InputEvent
411: */
412: public static AWTKeyStroke getAWTKeyStroke(int keyCode,
413: int modifiers) {
414: return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
415: modifiers, false);
416: }
417:
418: /**
419: * Returns an <code>AWTKeyStroke</code> which represents the
420: * stroke which generated a given <code>KeyEvent</code>.
421: * <p>
422: * This method obtains the keyChar from a <code>KeyTyped</code>
423: * event, and the keyCode from a <code>KeyPressed</code> or
424: * <code>KeyReleased</code> event. The <code>KeyEvent</code> modifiers are
425: * obtained for all three types of <code>KeyEvent</code>.
426: *
427: * @param anEvent the <code>KeyEvent</code> from which to
428: * obtain the <code>AWTKeyStroke</code>
429: * @throws NullPointerException if <code>anEvent</code> is null
430: * @return the <code>AWTKeyStroke</code> that precipitated the event
431: */
432: public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent anEvent) {
433: int id = anEvent.getID();
434: switch (id) {
435: case KeyEvent.KEY_PRESSED:
436: case KeyEvent.KEY_RELEASED:
437: return getCachedStroke(KeyEvent.CHAR_UNDEFINED, anEvent
438: .getKeyCode(), anEvent.getModifiers(),
439: (id == KeyEvent.KEY_RELEASED));
440: case KeyEvent.KEY_TYPED:
441: return getCachedStroke(anEvent.getKeyChar(),
442: KeyEvent.VK_UNDEFINED, anEvent.getModifiers(),
443: false);
444: default:
445: // Invalid ID for this KeyEvent
446: return null;
447: }
448: }
449:
450: /**
451: * Parses a string and returns an <code>AWTKeyStroke</code>.
452: * The string must have the following syntax:
453: * <pre>
454: * <modifiers>* (<typedID> | <pressedReleasedID>)
455: *
456: * modifiers := shift | control | ctrl | meta | alt | altGraph
457: * typedID := typed <typedKey>
458: * typedKey := string of length 1 giving Unicode character.
459: * pressedReleasedID := (pressed | released) key
460: * key := KeyEvent key code name, i.e. the name following "VK_".
461: * </pre>
462: * If typed, pressed or released is not specified, pressed is assumed. Here
463: * are some examples:
464: * <pre>
465: * "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0);
466: * "control DELETE" => getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);
467: * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);
468: * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);
469: * "typed a" => getAWTKeyStroke('a');
470: * </pre>
471: *
472: * @param s a String formatted as described above
473: * @return an <code>AWTKeyStroke</code> object for that String
474: * @throws IllegalArgumentException if <code>s</code> is <code>null</code>,
475: * or is formatted incorrectly
476: */
477: public static AWTKeyStroke getAWTKeyStroke(String s) {
478: if (s == null) {
479: throw new IllegalArgumentException("String cannot be null");
480: }
481:
482: final String errmsg = "String formatted incorrectly";
483:
484: StringTokenizer st = new StringTokenizer(s, " ");
485:
486: int mask = 0;
487: boolean released = false;
488: boolean typed = false;
489: boolean pressed = false;
490:
491: synchronized (AWTKeyStroke.class) {
492: if (modifierKeywords == null) {
493: Map uninitializedMap = new HashMap(8, 1.0f);
494: uninitializedMap.put("shift", Integer
495: .valueOf(InputEvent.SHIFT_DOWN_MASK
496: | InputEvent.SHIFT_MASK));
497: uninitializedMap.put("control", Integer
498: .valueOf(InputEvent.CTRL_DOWN_MASK
499: | InputEvent.CTRL_MASK));
500: uninitializedMap.put("ctrl", Integer
501: .valueOf(InputEvent.CTRL_DOWN_MASK
502: | InputEvent.CTRL_MASK));
503: uninitializedMap.put("meta", Integer
504: .valueOf(InputEvent.META_DOWN_MASK
505: | InputEvent.META_MASK));
506: uninitializedMap.put("alt", Integer
507: .valueOf(InputEvent.ALT_DOWN_MASK
508: | InputEvent.ALT_MASK));
509: uninitializedMap.put("altGraph", Integer
510: .valueOf(InputEvent.ALT_GRAPH_DOWN_MASK
511: | InputEvent.ALT_GRAPH_MASK));
512: uninitializedMap.put("button1", Integer
513: .valueOf(InputEvent.BUTTON1_DOWN_MASK));
514: uninitializedMap.put("button2", Integer
515: .valueOf(InputEvent.BUTTON2_DOWN_MASK));
516: uninitializedMap.put("button3", Integer
517: .valueOf(InputEvent.BUTTON3_DOWN_MASK));
518: modifierKeywords = Collections
519: .synchronizedMap(uninitializedMap);
520: }
521: }
522:
523: int count = st.countTokens();
524:
525: for (int i = 1; i <= count; i++) {
526: String token = st.nextToken();
527:
528: if (typed) {
529: if (token.length() != 1 || i != count) {
530: throw new IllegalArgumentException(errmsg);
531: }
532: return getCachedStroke(token.charAt(0),
533: KeyEvent.VK_UNDEFINED, mask, false);
534: }
535:
536: if (pressed || released || i == count) {
537: if (i != count) {
538: throw new IllegalArgumentException(errmsg);
539: }
540:
541: String keyCodeName = "VK_" + token;
542: int keyCode = getVKValue(keyCodeName);
543:
544: return getCachedStroke(KeyEvent.CHAR_UNDEFINED,
545: keyCode, mask, released);
546: }
547:
548: if (token.equals("released")) {
549: released = true;
550: continue;
551: }
552: if (token.equals("pressed")) {
553: pressed = true;
554: continue;
555: }
556: if (token.equals("typed")) {
557: typed = true;
558: continue;
559: }
560:
561: Integer tokenMask = (Integer) modifierKeywords.get(token);
562: if (tokenMask != null) {
563: mask |= tokenMask.intValue();
564: } else {
565: throw new IllegalArgumentException(errmsg);
566: }
567: }
568:
569: throw new IllegalArgumentException(errmsg);
570: }
571:
572: private static VKCollection getVKCollection() {
573: if (vks == null) {
574: vks = new VKCollection();
575: }
576: return vks;
577: }
578:
579: /**
580: * Returns the integer constant for the KeyEvent.VK field named
581: * <code>key</code>. This will throw an
582: * <code>IllegalArgumentException</code> if <code>key</code> is
583: * not a valid constant.
584: */
585: private static int getVKValue(String key) {
586: VKCollection vkCollect = getVKCollection();
587:
588: Integer value = vkCollect.findCode(key);
589:
590: if (value == null) {
591: int keyCode = 0;
592: final String errmsg = "String formatted incorrectly";
593:
594: try {
595: keyCode = KeyEvent.class.getField(key).getInt(
596: KeyEvent.class);
597: } catch (NoSuchFieldException nsfe) {
598: throw new IllegalArgumentException(errmsg);
599: } catch (IllegalAccessException iae) {
600: throw new IllegalArgumentException(errmsg);
601: }
602: value = Integer.valueOf(keyCode);
603: vkCollect.put(key, value);
604: }
605: return value.intValue();
606: }
607:
608: /**
609: * Returns the character for this <code>AWTKeyStroke</code>.
610: *
611: * @return a char value
612: * @see #getAWTKeyStroke(char)
613: * @see KeyEvent#getKeyChar
614: */
615: public final char getKeyChar() {
616: return keyChar;
617: }
618:
619: /**
620: * Returns the numeric key code for this <code>AWTKeyStroke</code>.
621: *
622: * @return an int containing the key code value
623: * @see #getAWTKeyStroke(int,int)
624: * @see KeyEvent#getKeyCode
625: */
626: public final int getKeyCode() {
627: return keyCode;
628: }
629:
630: /**
631: * Returns the modifier keys for this <code>AWTKeyStroke</code>.
632: *
633: * @return an int containing the modifiers
634: * @see #getAWTKeyStroke(int,int)
635: */
636: public final int getModifiers() {
637: return modifiers;
638: }
639:
640: /**
641: * Returns whether this <code>AWTKeyStroke</code> represents a key release.
642: *
643: * @return <code>true</code> if this <code>AWTKeyStroke</code>
644: * represents a key release; <code>false</code> otherwise
645: * @see #getAWTKeyStroke(int,int,boolean)
646: */
647: public final boolean isOnKeyRelease() {
648: return onKeyRelease;
649: }
650:
651: /**
652: * Returns the type of <code>KeyEvent</code> which corresponds to
653: * this <code>AWTKeyStroke</code>.
654: *
655: * @return <code>KeyEvent.KEY_PRESSED</code>,
656: * <code>KeyEvent.KEY_TYPED</code>,
657: * or <code>KeyEvent.KEY_RELEASED</code>
658: * @see java.awt.event.KeyEvent
659: */
660: public final int getKeyEventType() {
661: if (keyCode == KeyEvent.VK_UNDEFINED) {
662: return KeyEvent.KEY_TYPED;
663: } else {
664: return (onKeyRelease) ? KeyEvent.KEY_RELEASED
665: : KeyEvent.KEY_PRESSED;
666: }
667: }
668:
669: /**
670: * Returns a numeric value for this object that is likely to be unique,
671: * making it a good choice as the index value in a hash table.
672: *
673: * @return an int that represents this object
674: */
675: public int hashCode() {
676: return (((int) keyChar) + 1) * (2 * (keyCode + 1))
677: * (modifiers + 1) + (onKeyRelease ? 1 : 2);
678: }
679:
680: /**
681: * Returns true if this object is identical to the specified object.
682: *
683: * @param anObject the Object to compare this object to
684: * @return true if the objects are identical
685: */
686: public final boolean equals(Object anObject) {
687: if (anObject instanceof AWTKeyStroke) {
688: AWTKeyStroke ks = (AWTKeyStroke) anObject;
689: return (ks.keyChar == keyChar && ks.keyCode == keyCode
690: && ks.onKeyRelease == onKeyRelease && ks.modifiers == modifiers);
691: }
692: return false;
693: }
694:
695: /**
696: * Returns a string that displays and identifies this object's properties.
697: * The <code>String</code> returned by this method can be passed
698: * as a parameter to <code>getAWTKeyStroke(String)</code> to produce
699: * a key stroke equal to this key stroke.
700: *
701: * @return a String representation of this object
702: * @see #getAWTKeyStroke(String)
703: */
704: public String toString() {
705: if (keyCode == KeyEvent.VK_UNDEFINED) {
706: return getModifiersText(modifiers) + "typed " + keyChar;
707: } else {
708: return getModifiersText(modifiers)
709: + (onKeyRelease ? "released" : "pressed") + " "
710: + getVKText(keyCode);
711: }
712: }
713:
714: static String getModifiersText(int modifiers) {
715: StringBuilder buf = new StringBuilder();
716:
717: if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) {
718: buf.append("shift ");
719: }
720: if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) {
721: buf.append("ctrl ");
722: }
723: if ((modifiers & InputEvent.META_DOWN_MASK) != 0) {
724: buf.append("meta ");
725: }
726: if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) {
727: buf.append("alt ");
728: }
729: if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
730: buf.append("altGraph ");
731: }
732: if ((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0) {
733: buf.append("button1 ");
734: }
735: if ((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0) {
736: buf.append("button2 ");
737: }
738: if ((modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0) {
739: buf.append("button3 ");
740: }
741:
742: return buf.toString();
743: }
744:
745: static String getVKText(int keyCode) {
746: VKCollection vkCollect = getVKCollection();
747: Integer key = Integer.valueOf(keyCode);
748: String name = vkCollect.findName(key);
749: if (name != null) {
750: return name.substring(3);
751: }
752: int expected_modifiers = (Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
753:
754: Field[] fields = KeyEvent.class.getDeclaredFields();
755: for (int i = 0; i < fields.length; i++) {
756: try {
757: if (fields[i].getModifiers() == expected_modifiers
758: && fields[i].getType() == Integer.TYPE
759: && fields[i].getName().startsWith("VK_")
760: && fields[i].getInt(KeyEvent.class) == keyCode) {
761: name = fields[i].getName();
762: vkCollect.put(name, key);
763: return name.substring(3);
764: }
765: } catch (IllegalAccessException e) {
766: assert (false);
767: }
768: }
769: return "UNKNOWN";
770: }
771:
772: /**
773: * Returns a cached instance of <code>AWTKeyStroke</code> (or a subclass of
774: * <code>AWTKeyStroke</code>) which is equal to this instance.
775: *
776: * @return a cached instance which is equal to this instance
777: */
778: protected Object readResolve() throws java.io.ObjectStreamException {
779: synchronized (AWTKeyStroke.class) {
780: Class newClass = getClass();
781: if (!newClass.equals(ctor.getDeclaringClass())) {
782: registerSubclass(newClass);
783: }
784: return getCachedStroke(keyChar, keyCode, modifiers,
785: onKeyRelease);
786: }
787: }
788:
789: private static int mapOldModifiers(int modifiers) {
790: if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
791: modifiers |= InputEvent.SHIFT_DOWN_MASK;
792: }
793: if ((modifiers & InputEvent.ALT_MASK) != 0) {
794: modifiers |= InputEvent.ALT_DOWN_MASK;
795: }
796: if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {
797: modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
798: }
799: if ((modifiers & InputEvent.CTRL_MASK) != 0) {
800: modifiers |= InputEvent.CTRL_DOWN_MASK;
801: }
802: if ((modifiers & InputEvent.META_MASK) != 0) {
803: modifiers |= InputEvent.META_DOWN_MASK;
804: }
805:
806: modifiers &= InputEvent.SHIFT_DOWN_MASK
807: | InputEvent.ALT_DOWN_MASK
808: | InputEvent.ALT_GRAPH_DOWN_MASK
809: | InputEvent.CTRL_DOWN_MASK | InputEvent.META_DOWN_MASK
810: | InputEvent.BUTTON1_DOWN_MASK
811: | InputEvent.BUTTON2_DOWN_MASK
812: | InputEvent.BUTTON3_DOWN_MASK;
813:
814: return modifiers;
815: }
816:
817: private static int mapNewModifiers(int modifiers) {
818: if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) {
819: modifiers |= InputEvent.SHIFT_MASK;
820: }
821: if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) {
822: modifiers |= InputEvent.ALT_MASK;
823: }
824: if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
825: modifiers |= InputEvent.ALT_GRAPH_MASK;
826: }
827: if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) {
828: modifiers |= InputEvent.CTRL_MASK;
829: }
830: if ((modifiers & InputEvent.META_DOWN_MASK) != 0) {
831: modifiers |= InputEvent.META_MASK;
832: }
833:
834: return modifiers;
835: }
836:
837: }
838:
839: class VKCollection {
840: Map code2name;
841: Map name2code;
842:
843: public VKCollection() {
844: code2name = new HashMap();
845: name2code = new HashMap();
846: }
847:
848: public synchronized void put(String name, Integer code) {
849: assert ((name != null) && (code != null));
850: assert (findName(code) == null);
851: assert (findCode(name) == null);
852: code2name.put(code, name);
853: name2code.put(name, code);
854: }
855:
856: public synchronized Integer findCode(String name) {
857: assert (name != null);
858: return (Integer) name2code.get(name);
859: }
860:
861: public synchronized String findName(Integer code) {
862: assert (code != null);
863: return (String) code2name.get(code);
864: }
865: }
|