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 }
|