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