| 1 | /* AWTKeyStroke.java -- an immutable key stroke
|
|---|
| 2 | Copyright (C) 2002 Free Software Foundation
|
|---|
| 3 |
|
|---|
| 4 | This file is part of GNU Classpath.
|
|---|
| 5 |
|
|---|
| 6 | GNU Classpath is free software; you can redistribute it and/or modify
|
|---|
| 7 | it under the terms of the GNU General Public License as published by
|
|---|
| 8 | the Free Software Foundation; either version 2, or (at your option)
|
|---|
| 9 | any later version.
|
|---|
| 10 |
|
|---|
| 11 | GNU Classpath is distributed in the hope that it will be useful, but
|
|---|
| 12 | WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|---|
| 14 | General Public License for more details.
|
|---|
| 15 |
|
|---|
| 16 | You should have received a copy of the GNU General Public License
|
|---|
| 17 | along with GNU Classpath; see the file COPYING. If not, write to the
|
|---|
| 18 | Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|---|
| 19 | 02111-1307 USA.
|
|---|
| 20 |
|
|---|
| 21 | Linking this library statically or dynamically with other modules is
|
|---|
| 22 | making a combined work based on this library. Thus, the terms and
|
|---|
| 23 | conditions of the GNU General Public License cover the whole
|
|---|
| 24 | combination.
|
|---|
| 25 |
|
|---|
| 26 | As a special exception, the copyright holders of this library give you
|
|---|
| 27 | permission to link this library with independent modules to produce an
|
|---|
| 28 | executable, regardless of the license terms of these independent
|
|---|
| 29 | modules, and to copy and distribute the resulting executable under
|
|---|
| 30 | terms of your choice, provided that you also meet, for each linked
|
|---|
| 31 | independent module, the terms and conditions of the license of that
|
|---|
| 32 | module. An independent module is a module which is not derived from
|
|---|
| 33 | or based on this library. If you modify this library, you may extend
|
|---|
| 34 | this exception to your version of the library, but you are not
|
|---|
| 35 | obligated to do so. If you do not wish to do so, delete this
|
|---|
| 36 | exception statement from your version. */
|
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 | package java.awt;
|
|---|
| 40 |
|
|---|
| 41 | import java.awt.event.KeyEvent;
|
|---|
| 42 | import java.io.ObjectStreamException;
|
|---|
| 43 | import java.io.Serializable;
|
|---|
| 44 | import java.lang.reflect.Constructor;
|
|---|
| 45 | import java.lang.reflect.Field;
|
|---|
| 46 | import java.lang.reflect.InvocationTargetException;
|
|---|
| 47 | import java.security.AccessController;
|
|---|
| 48 | import java.security.PrivilegedAction;
|
|---|
| 49 | import java.security.PrivilegedActionException;
|
|---|
| 50 | import java.security.PrivilegedExceptionAction;
|
|---|
| 51 | import java.util.Map;
|
|---|
| 52 | import java.util.HashMap;
|
|---|
| 53 | import java.util.LinkedHashMap;
|
|---|
| 54 | import java.util.StringTokenizer;
|
|---|
| 55 |
|
|---|
| 56 | /**
|
|---|
| 57 | * This class mirrors KeyEvents, representing both low-level key presses and
|
|---|
| 58 | * key releases, and high level key typed inputs. However, this class forms
|
|---|
| 59 | * immutable strokes, and can be efficiently reused via the factory methods
|
|---|
| 60 | * for creating them.
|
|---|
| 61 | *
|
|---|
| 62 | * <p>For backwards compatibility with Swing, this supports a way to build
|
|---|
| 63 | * instances of a subclass, using reflection, provided the subclass has a
|
|---|
| 64 | * no-arg constructor (of any accessibility).
|
|---|
| 65 | *
|
|---|
| 66 | * @author Eric Blake <[email protected]>
|
|---|
| 67 | * @see #getAWTKeyStroke(char)
|
|---|
| 68 | * @since 1.4
|
|---|
| 69 | * @status updated to 1.4
|
|---|
| 70 | */
|
|---|
| 71 | public class AWTKeyStroke implements Serializable
|
|---|
| 72 | {
|
|---|
| 73 | /**
|
|---|
| 74 | * Compatible with JDK 1.4+.
|
|---|
| 75 | */
|
|---|
| 76 | private static final long serialVersionUID = -6430539691155161871L;
|
|---|
| 77 |
|
|---|
| 78 | /** The mask for modifiers. */
|
|---|
| 79 | private static final int MODIFIERS_MASK = 0x3fef;
|
|---|
| 80 |
|
|---|
| 81 | /**
|
|---|
| 82 | * The cache of recently created keystrokes. This maps KeyStrokes to
|
|---|
| 83 | * KeyStrokes in a cache which removes the least recently accessed entry,
|
|---|
| 84 | * under the assumption that garbage collection of a new keystroke is
|
|---|
| 85 | * easy when we find the old one that it matches in the cache.
|
|---|
| 86 | */
|
|---|
| 87 | private static final LinkedHashMap cache = new LinkedHashMap(11, 0.75f, true)
|
|---|
| 88 | {
|
|---|
| 89 | /** The largest the keystroke cache can grow. */
|
|---|
| 90 | private static final int MAX_CACHE_SIZE = 2048;
|
|---|
| 91 |
|
|---|
| 92 | /** Prune stale entries. */
|
|---|
| 93 | protected boolean removeEldestEntry(Map.Entry eldest)
|
|---|
| 94 | { // XXX - FIXME Use Map.Entry, not just Entry as gcj 3.1 workaround.
|
|---|
| 95 | return size() > MAX_CACHE_SIZE;
|
|---|
| 96 | }
|
|---|
| 97 | };
|
|---|
| 98 |
|
|---|
| 99 | /** The most recently generated keystroke, or null. */
|
|---|
| 100 | private static AWTKeyStroke recent;
|
|---|
| 101 |
|
|---|
| 102 | /**
|
|---|
| 103 | * The no-arg constructor of a subclass, or null to use AWTKeyStroke. Note
|
|---|
| 104 | * that this will be left accessible, to get around private access; but
|
|---|
| 105 | * it should not be a security risk as it is highly unlikely that creating
|
|---|
| 106 | * protected instances of the subclass via reflection will do much damage.
|
|---|
| 107 | */
|
|---|
| 108 | private static Constructor ctor;
|
|---|
| 109 |
|
|---|
| 110 | /**
|
|---|
| 111 | * A table of keyCode names to values.
|
|---|
| 112 | *
|
|---|
| 113 | * @see #getAWTKeyStroke(String)
|
|---|
| 114 | */
|
|---|
| 115 | private static final HashMap vktable = new HashMap();
|
|---|
| 116 | static
|
|---|
| 117 | {
|
|---|
| 118 | // Using reflection saves the hassle of keeping this in sync with KeyEvent,
|
|---|
| 119 | // at the price of an expensive initialization.
|
|---|
| 120 | AccessController.doPrivileged(new PrivilegedAction()
|
|---|
| 121 | {
|
|---|
| 122 | public Object run()
|
|---|
| 123 | {
|
|---|
| 124 | Field[] fields = KeyEvent.class.getFields();
|
|---|
| 125 | int i = fields.length;
|
|---|
| 126 | try
|
|---|
| 127 | {
|
|---|
| 128 | while (--i >= 0)
|
|---|
| 129 | {
|
|---|
| 130 | Field f = fields[i];
|
|---|
| 131 | String name = f.getName();
|
|---|
| 132 | if (name.startsWith("VK_"))
|
|---|
| 133 | vktable.put(name.substring(3), f.get(null));
|
|---|
| 134 | }
|
|---|
| 135 | }
|
|---|
| 136 | catch (Exception e)
|
|---|
| 137 | {
|
|---|
| 138 | throw (Error) new InternalError().initCause(e);
|
|---|
| 139 | }
|
|---|
| 140 | return null;
|
|---|
| 141 | }
|
|---|
| 142 | });
|
|---|
| 143 | }
|
|---|
| 144 |
|
|---|
| 145 | /**
|
|---|
| 146 | * The typed character, or CHAR_UNDEFINED for key presses and releases.
|
|---|
| 147 | *
|
|---|
| 148 | * @serial the keyChar
|
|---|
| 149 | */
|
|---|
| 150 | private char keyChar;
|
|---|
| 151 |
|
|---|
| 152 | /**
|
|---|
| 153 | * The virtual key code, or VK_UNDEFINED for key typed. Package visible for
|
|---|
| 154 | * use by Component.
|
|---|
| 155 | *
|
|---|
| 156 | * @serial the keyCode
|
|---|
| 157 | */
|
|---|
| 158 | int keyCode;
|
|---|
| 159 |
|
|---|
| 160 | /**
|
|---|
| 161 | * The modifiers in effect. To match Sun, this stores the old style masks
|
|---|
| 162 | * for shift, control, alt, meta, and alt-graph (but not button1); as well
|
|---|
| 163 | * as the new style of extended modifiers for all modifiers.
|
|---|
| 164 | *
|
|---|
| 165 | * @serial bitwise or of the *_DOWN_MASK modifiers
|
|---|
| 166 | */
|
|---|
| 167 | private int modifiers;
|
|---|
| 168 |
|
|---|
| 169 | /**
|
|---|
| 170 | * True if this is a key release; should only be true if keyChar is
|
|---|
| 171 | * CHAR_UNDEFINED.
|
|---|
| 172 | *
|
|---|
| 173 | * @serial true to distinguish key pressed from key released
|
|---|
| 174 | */
|
|---|
| 175 | private boolean onKeyRelease;
|
|---|
| 176 |
|
|---|
| 177 | /**
|
|---|
| 178 | * Construct a keystroke with default values: it will be interpreted as a
|
|---|
| 179 | * key typed event with an invalid character and no modifiers. Client code
|
|---|
| 180 | * should use the factory methods instead.
|
|---|
| 181 | *
|
|---|
| 182 | * @see #getAWTKeyStroke(char)
|
|---|
| 183 | * @see #getAWTKeyStroke(Character, int)
|
|---|
| 184 | * @see #getAWTKeyStroke(int, int, boolean)
|
|---|
| 185 | * @see #getAWTKeyStroke(int, int)
|
|---|
| 186 | * @see #getAWTKeyStrokeForEvent(KeyEvent)
|
|---|
| 187 | * @see #getAWTKeyStroke(String)
|
|---|
| 188 | */
|
|---|
| 189 | protected AWTKeyStroke()
|
|---|
| 190 | {
|
|---|
| 191 | keyChar = KeyEvent.CHAR_UNDEFINED;
|
|---|
| 192 | }
|
|---|
| 193 |
|
|---|
| 194 | /**
|
|---|
| 195 | * Construct a keystroke with the given values. Client code should use the
|
|---|
| 196 | * factory methods instead.
|
|---|
| 197 | *
|
|---|
| 198 | * @param keyChar the character entered, if this is a key typed
|
|---|
| 199 | * @param keyCode the key pressed or released, or VK_UNDEFINED for key typed
|
|---|
| 200 | * @param modifiers the modifier keys for the keystroke, in old or new style
|
|---|
| 201 | * @param onKeyRelease true if this is a key release instead of a press
|
|---|
| 202 | * @see #getAWTKeyStroke(char)
|
|---|
| 203 | * @see #getAWTKeyStroke(Character, int)
|
|---|
| 204 | * @see #getAWTKeyStroke(int, int, boolean)
|
|---|
| 205 | * @see #getAWTKeyStroke(int, int)
|
|---|
| 206 | * @see #getAWTKeyStrokeForEvent(KeyEvent)
|
|---|
| 207 | * @see #getAWTKeyStroke(String)
|
|---|
| 208 | */
|
|---|
| 209 | protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
|
|---|
| 210 | boolean onKeyRelease)
|
|---|
| 211 | {
|
|---|
| 212 | this.keyChar = keyChar;
|
|---|
| 213 | this.keyCode = keyCode;
|
|---|
| 214 | // No need to call extend(), as only trusted code calls this constructor.
|
|---|
| 215 | this.modifiers = modifiers;
|
|---|
| 216 | this.onKeyRelease = onKeyRelease;
|
|---|
| 217 | }
|
|---|
| 218 |
|
|---|
| 219 | /**
|
|---|
| 220 | * Registers a new subclass as being the type of keystrokes to generate in
|
|---|
| 221 | * the factory methods. This operation flushes the cache of stored keystrokes
|
|---|
| 222 | * if the class differs from the current one. The new class must be
|
|---|
| 223 | * AWTKeyStroke or a subclass, and must have a no-arg constructor (which may
|
|---|
| 224 | * be private).
|
|---|
| 225 | *
|
|---|
| 226 | * @param subclass the new runtime type of generated keystrokes
|
|---|
| 227 | * @throws IllegalArgumentException subclass doesn't have no-arg constructor
|
|---|
| 228 | * @throws ClassCastException subclass doesn't extend AWTKeyStroke
|
|---|
| 229 | */
|
|---|
| 230 | protected static void registerSubclass(final Class subclass)
|
|---|
| 231 | {
|
|---|
| 232 | if (subclass == null)
|
|---|
| 233 | throw new IllegalArgumentException();
|
|---|
| 234 | if (subclass.equals(ctor == null ? AWTKeyStroke.class
|
|---|
| 235 | : ctor.getDeclaringClass()))
|
|---|
| 236 | return;
|
|---|
| 237 | if (subclass.equals(AWTKeyStroke.class))
|
|---|
| 238 | {
|
|---|
| 239 | cache.clear();
|
|---|
| 240 | recent = null;
|
|---|
| 241 | ctor = null;
|
|---|
| 242 | return;
|
|---|
| 243 | }
|
|---|
| 244 | try
|
|---|
| 245 | {
|
|---|
| 246 | ctor = (Constructor) AccessController.doPrivileged
|
|---|
| 247 | (new PrivilegedExceptionAction()
|
|---|
| 248 | {
|
|---|
| 249 | public Object run()
|
|---|
| 250 | throws NoSuchMethodException, InstantiationException,
|
|---|
| 251 | IllegalAccessException, InvocationTargetException
|
|---|
| 252 | {
|
|---|
| 253 | Constructor c = subclass.getDeclaredConstructor(null);
|
|---|
| 254 | c.setAccessible(true);
|
|---|
| 255 | // Create a new instance, to make sure that we can, and
|
|---|
| 256 | // to cause any ClassCastException.
|
|---|
| 257 | AWTKeyStroke dummy = (AWTKeyStroke) c.newInstance(null);
|
|---|
| 258 | return c;
|
|---|
| 259 | }
|
|---|
| 260 | });
|
|---|
| 261 | }
|
|---|
| 262 | catch (PrivilegedActionException e)
|
|---|
| 263 | {
|
|---|
| 264 | // e.getCause() will not ever be ClassCastException; that should
|
|---|
| 265 | // escape on its own.
|
|---|
| 266 | throw (RuntimeException)
|
|---|
| 267 | new IllegalArgumentException().initCause(e.getCause());
|
|---|
| 268 | }
|
|---|
| 269 | cache.clear();
|
|---|
| 270 | recent = null;
|
|---|
| 271 | }
|
|---|
| 272 |
|
|---|
| 273 | /**
|
|---|
| 274 | * Returns a keystroke representing a typed character.
|
|---|
| 275 | *
|
|---|
| 276 | * @param keyChar the typed character
|
|---|
| 277 | * @return the specified keystroke
|
|---|
| 278 | */
|
|---|
| 279 | public static AWTKeyStroke getAWTKeyStroke(char keyChar)
|
|---|
| 280 | {
|
|---|
| 281 | return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);
|
|---|
| 282 | }
|
|---|
| 283 |
|
|---|
| 284 | /**
|
|---|
| 285 | * Returns a keystroke representing a typed character with the given
|
|---|
| 286 | * modifiers. Note that keyChar is a <code>Character</code> instead of a
|
|---|
| 287 | * <code>char</code> to avoid accidental ambiguity with
|
|---|
| 288 | * <code>getAWTKeyStroke(int, int)</code>. The modifiers are the bitwise
|
|---|
| 289 | * or of the masks found in {@link InputEvent}; the new style (*_DOWN_MASK)
|
|---|
| 290 | * is preferred, but the old style will work.
|
|---|
| 291 | *
|
|---|
| 292 | * @param keyChar the typed character
|
|---|
| 293 | * @param modifiers the modifiers, or 0
|
|---|
| 294 | * @return the specified keystroke
|
|---|
| 295 | * @throws IllegalArgumentException if keyChar is null
|
|---|
| 296 | */
|
|---|
| 297 | public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers)
|
|---|
| 298 | {
|
|---|
| 299 | if (keyChar == null)
|
|---|
| 300 | throw new IllegalArgumentException();
|
|---|
| 301 | return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED,
|
|---|
| 302 | extend(modifiers), false);
|
|---|
| 303 | }
|
|---|
| 304 |
|
|---|
| 305 | /**
|
|---|
| 306 | * Returns a keystroke representing a pressed or released key event, with
|
|---|
| 307 | * the given modifiers. The "virtual key" should be one of the VK_*
|
|---|
| 308 | * constants in {@link KeyEvent}. The modifiers are the bitwise or of the
|
|---|
| 309 | * masks found in {@link InputEvent}; the new style (*_DOWN_MASK) is
|
|---|
| 310 | * preferred, but the old style will work.
|
|---|
| 311 | *
|
|---|
| 312 | * @param keyCode the virtual key
|
|---|
| 313 | * @param modifiers the modifiers, or 0
|
|---|
| 314 | * @param release true if this is a key release instead of a key press
|
|---|
| 315 | * @return the specified keystroke
|
|---|
| 316 | */
|
|---|
| 317 | public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers,
|
|---|
| 318 | boolean release)
|
|---|
| 319 | {
|
|---|
| 320 | return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
|
|---|
| 321 | extend(modifiers), release);
|
|---|
| 322 | }
|
|---|
| 323 |
|
|---|
| 324 | /**
|
|---|
| 325 | * Returns a keystroke representing a pressed key event, with the given
|
|---|
| 326 | * modifiers. The "virtual key" should be one of the VK_* constants in
|
|---|
| 327 | * {@link KeyEvent}. The modifiers are the bitwise or of the masks found
|
|---|
| 328 | * in {@link InputEvent}; the new style (*_DOWN_MASK) is preferred, but the
|
|---|
| 329 | * old style will work.
|
|---|
| 330 | *
|
|---|
| 331 | * @param keyCode the virtual key
|
|---|
| 332 | * @param modifiers the modifiers, or 0
|
|---|
| 333 | * @return the specified keystroke
|
|---|
| 334 | */
|
|---|
| 335 | public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers)
|
|---|
| 336 | {
|
|---|
| 337 | return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
|
|---|
| 338 | extend(modifiers), false);
|
|---|
| 339 | }
|
|---|
| 340 |
|
|---|
| 341 | /**
|
|---|
| 342 | * Returns a keystroke representing what caused the key event.
|
|---|
| 343 | *
|
|---|
| 344 | * @param event the key event to convert
|
|---|
| 345 | * @return the specified keystroke, or null if the event is invalid
|
|---|
| 346 | * @throws NullPointerException if event is null
|
|---|
| 347 | */
|
|---|
| 348 | public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent event)
|
|---|
| 349 | {
|
|---|
| 350 | switch (event.id)
|
|---|
| 351 | {
|
|---|
| 352 | case KeyEvent.KEY_TYPED:
|
|---|
| 353 | return getAWTKeyStroke(event.getKeyChar(), KeyEvent.VK_UNDEFINED,
|
|---|
| 354 | extend(event.getModifiersEx()), false);
|
|---|
| 355 | case KeyEvent.KEY_PRESSED:
|
|---|
| 356 | return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(),
|
|---|
| 357 | extend(event.getModifiersEx()), false);
|
|---|
| 358 | case KeyEvent.KEY_RELEASED:
|
|---|
| 359 | return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(),
|
|---|
| 360 | extend(event.getModifiersEx()), true);
|
|---|
| 361 | default:
|
|---|
| 362 | return null;
|
|---|
| 363 | }
|
|---|
| 364 | }
|
|---|
| 365 |
|
|---|
| 366 | /**
|
|---|
| 367 | * Parses a string and returns the keystroke that it represents. The syntax
|
|---|
| 368 | * for keystrokes is listed below, with tokens separated by an arbitrary
|
|---|
| 369 | * number of spaces:
|
|---|
| 370 | * <pre>
|
|---|
| 371 | * keyStroke := <modifiers>* ( <typedID> | <codeID> )
|
|---|
| 372 | * modifiers := ( shift | control | ctrl | meta | alt
|
|---|
| 373 | * | button1 | button2 | button3 )
|
|---|
| 374 | * typedID := typed <single Unicode character>
|
|---|
| 375 | * codeID := ( pressed | released )? <name>
|
|---|
| 376 | * name := <the KeyEvent field name less the leading "VK_">
|
|---|
| 377 | * </pre>
|
|---|
| 378 | *
|
|---|
| 379 | * <p>Note that the grammar is rather weak, and not all valid keystrokes
|
|---|
| 380 | * can be generated in this manner (for example, a typed space, or anything
|
|---|
| 381 | * with the alt-graph modifier!). The output of AWTKeyStroke.toString()
|
|---|
| 382 | * will not meet the grammar. If pressed or released is not specified,
|
|---|
| 383 | * pressed is assumed. Examples:<br>
|
|---|
| 384 | * <code>
|
|---|
| 385 | * "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0);<br>
|
|---|
| 386 | * "control DELETE" =>
|
|---|
| 387 | * getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);<br>
|
|---|
| 388 | * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X,
|
|---|
| 389 | * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);<br>
|
|---|
| 390 | * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X,
|
|---|
| 391 | * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);<br>
|
|---|
| 392 | * "typed a" => getAWTKeyStroke('a');
|
|---|
| 393 | * </code>
|
|---|
| 394 | *
|
|---|
| 395 | * @param s the string to parse
|
|---|
| 396 | * @return the specified keystroke
|
|---|
| 397 | * @throws NullPointerException if s is null
|
|---|
| 398 | * @throws IllegalArgumentException if s cannot be parsed
|
|---|
| 399 | */
|
|---|
| 400 | public static AWTKeyStroke getAWTKeyStroke(String s)
|
|---|
| 401 | {
|
|---|
| 402 | StringTokenizer t = new StringTokenizer(s, " ");
|
|---|
| 403 | if (! t.hasMoreTokens())
|
|---|
| 404 | throw new IllegalArgumentException();
|
|---|
| 405 | int modifiers = 0;
|
|---|
| 406 | boolean released = false;
|
|---|
| 407 | String token = null;
|
|---|
| 408 | do
|
|---|
| 409 | {
|
|---|
| 410 | token = t.nextToken();
|
|---|
| 411 | if ("shift".equals(token))
|
|---|
| 412 | modifiers |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK;
|
|---|
| 413 | else if ("ctrl".equals(token) || "control".equals(token))
|
|---|
| 414 | modifiers |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK;
|
|---|
| 415 | else if ("meta".equals(token))
|
|---|
| 416 | modifiers |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK;
|
|---|
| 417 | else if ("alt".equals(token))
|
|---|
| 418 | modifiers |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK;
|
|---|
| 419 | else if ("button1".equals(token))
|
|---|
| 420 | modifiers |= KeyEvent.BUTTON1_DOWN_MASK;
|
|---|
| 421 | else if ("button2".equals(token))
|
|---|
| 422 | modifiers |= KeyEvent.BUTTON2_DOWN_MASK;
|
|---|
| 423 | else if ("button3".equals(token))
|
|---|
| 424 | modifiers |= KeyEvent.BUTTON3_DOWN_MASK;
|
|---|
| 425 | else if ("typed".equals(token))
|
|---|
| 426 | {
|
|---|
| 427 | if (t.hasMoreTokens())
|
|---|
| 428 | {
|
|---|
| 429 | token = t.nextToken();
|
|---|
| 430 | if (! t.hasMoreTokens() && token.length() == 1)
|
|---|
| 431 | return getAWTKeyStroke(token.charAt(0),
|
|---|
| 432 | KeyEvent.VK_UNDEFINED, modifiers,
|
|---|
| 433 | false);
|
|---|
| 434 | }
|
|---|
| 435 | throw new IllegalArgumentException();
|
|---|
| 436 | }
|
|---|
| 437 | else if ("pressed".equals(token))
|
|---|
| 438 | {
|
|---|
| 439 | if (t.hasMoreTokens())
|
|---|
| 440 | token = t.nextToken();
|
|---|
| 441 | break;
|
|---|
| 442 | }
|
|---|
| 443 | else if ("released".equals(token))
|
|---|
| 444 | {
|
|---|
| 445 | released = true;
|
|---|
| 446 | if (t.hasMoreTokens())
|
|---|
| 447 | token = t.nextToken();
|
|---|
| 448 | break;
|
|---|
| 449 | }
|
|---|
| 450 | else
|
|---|
| 451 | break;
|
|---|
| 452 | }
|
|---|
| 453 | while (t.hasMoreTokens());
|
|---|
| 454 | // Now token contains the VK name we must parse.
|
|---|
| 455 | Integer code = (Integer) vktable.get(token);
|
|---|
| 456 | if (code == null || t.hasMoreTokens())
|
|---|
| 457 | throw new IllegalArgumentException();
|
|---|
| 458 | return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, code.intValue(),
|
|---|
| 459 | modifiers, released);
|
|---|
| 460 | }
|
|---|
| 461 |
|
|---|
| 462 | /**
|
|---|
| 463 | * Returns the character of this keystroke, if it was typed.
|
|---|
| 464 | *
|
|---|
| 465 | * @return the character value, or CHAR_UNDEFINED
|
|---|
| 466 | * @see #getAWTKeyStroke(char)
|
|---|
| 467 | */
|
|---|
| 468 | public final char getKeyChar()
|
|---|
| 469 | {
|
|---|
| 470 | return keyChar;
|
|---|
| 471 | }
|
|---|
| 472 |
|
|---|
| 473 | /**
|
|---|
| 474 | * Returns the virtual key code of this keystroke, if it was pressed or
|
|---|
| 475 | * released. This will be a VK_* constant from KeyEvent.
|
|---|
| 476 | *
|
|---|
| 477 | * @return the virtual key code value, or VK_UNDEFINED
|
|---|
| 478 | * @see #getAWTKeyStroke(int, int)
|
|---|
| 479 | */
|
|---|
| 480 | public final int getKeyCode()
|
|---|
| 481 | {
|
|---|
| 482 | return keyCode;
|
|---|
| 483 | }
|
|---|
| 484 |
|
|---|
| 485 | /**
|
|---|
| 486 | * Returns the modifiers for this keystroke. This will be a bitwise or of
|
|---|
| 487 | * constants from InputEvent; it includes the old style masks for shift,
|
|---|
| 488 | * control, alt, meta, and alt-graph (but not button1); as well as the new
|
|---|
| 489 | * style of extended modifiers for all modifiers.
|
|---|
| 490 | *
|
|---|
| 491 | * @return the modifiers
|
|---|
| 492 | * @see #getAWTKeyStroke(Character, int)
|
|---|
| 493 | * @see #getAWTKeyStroke(int, int)
|
|---|
| 494 | */
|
|---|
| 495 | public final int getModifiers()
|
|---|
| 496 | {
|
|---|
| 497 | return modifiers;
|
|---|
| 498 | }
|
|---|
| 499 |
|
|---|
| 500 | /**
|
|---|
| 501 | * Tests if this keystroke is a key release.
|
|---|
| 502 | *
|
|---|
| 503 | * @return true if this is a key release
|
|---|
| 504 | * @see #getAWTKeyStroke(int, int, boolean)
|
|---|
| 505 | */
|
|---|
| 506 | public final boolean isOnKeyRelease()
|
|---|
| 507 | {
|
|---|
| 508 | return onKeyRelease;
|
|---|
| 509 | }
|
|---|
| 510 |
|
|---|
| 511 | /**
|
|---|
| 512 | * Returns the AWT event type of this keystroke. This is one of
|
|---|
| 513 | * {@link KeyEvent#KEY_TYPED}, {@link KeyEvent#KEY_PRESSED}, or
|
|---|
| 514 | * {@link KeyEvent#KEY_RELEASED}.
|
|---|
| 515 | *
|
|---|
| 516 | * @return the key event type
|
|---|
| 517 | */
|
|---|
| 518 | public final int getKeyEventType()
|
|---|
| 519 | {
|
|---|
| 520 | return keyCode == KeyEvent.VK_UNDEFINED ? KeyEvent.KEY_TYPED
|
|---|
| 521 | : onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED;
|
|---|
| 522 | }
|
|---|
| 523 |
|
|---|
| 524 | /**
|
|---|
| 525 | * Returns a hashcode for this key event. It is not documented, but appears
|
|---|
| 526 | * to be: <code>(getKeyChar() + 1) * (getKeyCode() + 1)
|
|---|
| 527 | * * (getModifiers() + 1) * 2 + (isOnKeyRelease() ? 1 : 2)</code>.
|
|---|
| 528 | *
|
|---|
| 529 | * @return the hashcode
|
|---|
| 530 | */
|
|---|
| 531 | public int hashCode()
|
|---|
| 532 | {
|
|---|
| 533 | return (keyChar + 1) * (keyCode + 1) * (modifiers + 1) * 2
|
|---|
| 534 | + (onKeyRelease ? 1 : 2);
|
|---|
| 535 | }
|
|---|
| 536 |
|
|---|
| 537 | /**
|
|---|
| 538 | * Tests two keystrokes for equality.
|
|---|
| 539 | *
|
|---|
| 540 | * @param o the object to test
|
|---|
| 541 | * @return true if it is equal
|
|---|
| 542 | */
|
|---|
| 543 | public final boolean equals(Object o)
|
|---|
| 544 | {
|
|---|
| 545 | if (! (o instanceof AWTKeyStroke))
|
|---|
| 546 | return false;
|
|---|
| 547 | AWTKeyStroke s = (AWTKeyStroke) o;
|
|---|
| 548 | return this == o || (keyChar == s.keyChar && keyCode == s.keyCode
|
|---|
| 549 | && modifiers == s.modifiers
|
|---|
| 550 | && onKeyRelease == s.onKeyRelease);
|
|---|
| 551 | }
|
|---|
| 552 |
|
|---|
| 553 | /**
|
|---|
| 554 | * Returns a string representation of this keystroke. For typed keystrokes,
|
|---|
| 555 | * this is <code>"keyChar " + KeyEvent.getKeyModifiersText(getModifiers())
|
|---|
| 556 | + getKeyChar()</code>; for pressed and released keystrokes, this is
|
|---|
| 557 | * <code>"keyCode " + KeyEvent.getKeyModifiersText(getModifiers())
|
|---|
| 558 | * + KeyEvent.getKeyText(getKeyCode())
|
|---|
| 559 | * + (isOnKeyRelease() ? "-R" : "-P")</code>.
|
|---|
| 560 | *
|
|---|
| 561 | * @return a string representation
|
|---|
| 562 | */
|
|---|
| 563 | public String toString()
|
|---|
| 564 | {
|
|---|
| 565 | if (keyCode == KeyEvent.VK_UNDEFINED)
|
|---|
| 566 | return "keyChar " + KeyEvent.getKeyModifiersText(modifiers) + keyChar;
|
|---|
| 567 | return "keyCode " + KeyEvent.getKeyModifiersText(modifiers)
|
|---|
| 568 | + KeyEvent.getKeyText(keyCode) + (onKeyRelease ? "-R" : "-P");
|
|---|
| 569 | }
|
|---|
| 570 |
|
|---|
| 571 | /**
|
|---|
| 572 | * Returns a cached version of the deserialized keystroke, if available.
|
|---|
| 573 | *
|
|---|
| 574 | * @return a cached replacement
|
|---|
| 575 | * @throws ObjectStreamException if something goes wrong
|
|---|
| 576 | */
|
|---|
| 577 | protected Object readResolve() throws ObjectStreamException
|
|---|
| 578 | {
|
|---|
| 579 | AWTKeyStroke s = (AWTKeyStroke) cache.get(this);
|
|---|
| 580 | if (s != null)
|
|---|
| 581 | return s;
|
|---|
| 582 | cache.put(this, this);
|
|---|
| 583 | return this;
|
|---|
| 584 | }
|
|---|
| 585 |
|
|---|
| 586 | /**
|
|---|
| 587 | * Gets the appropriate keystroke, creating one if necessary.
|
|---|
| 588 | *
|
|---|
| 589 | * @param keyChar the keyChar
|
|---|
| 590 | * @param keyCode the keyCode
|
|---|
| 591 | * @param modifiers the modifiers
|
|---|
| 592 | * @param release true for key release
|
|---|
| 593 | * @return the specified keystroke
|
|---|
| 594 | */
|
|---|
| 595 | private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode,
|
|---|
| 596 | int modifiers, boolean release)
|
|---|
| 597 | {
|
|---|
| 598 | // Check level 0 cache.
|
|---|
| 599 | AWTKeyStroke stroke = recent; // Avoid thread races.
|
|---|
| 600 | if (stroke != null && stroke.keyChar == keyChar
|
|---|
| 601 | && stroke.keyCode == keyCode && stroke.modifiers == modifiers
|
|---|
| 602 | && stroke.onKeyRelease == release)
|
|---|
| 603 | return stroke;
|
|---|
| 604 | // Create a new object, on the assumption that if it has a match in the
|
|---|
| 605 | // cache, the VM can easily garbage collect it as it is temporary.
|
|---|
| 606 | Constructor c = ctor; // Avoid thread races.
|
|---|
| 607 | if (c == null)
|
|---|
| 608 | stroke = new AWTKeyStroke(keyChar, keyCode, modifiers, release);
|
|---|
| 609 | else
|
|---|
| 610 | try
|
|---|
| 611 | {
|
|---|
| 612 | stroke = (AWTKeyStroke) c.newInstance(null);
|
|---|
| 613 | stroke.keyChar = keyChar;
|
|---|
| 614 | stroke.keyCode = keyCode;
|
|---|
| 615 | stroke.modifiers = modifiers;
|
|---|
| 616 | stroke.onKeyRelease = release;
|
|---|
| 617 | }
|
|---|
| 618 | catch (Exception e)
|
|---|
| 619 | {
|
|---|
| 620 | throw (Error) new InternalError().initCause(e);
|
|---|
| 621 | }
|
|---|
| 622 | // Check level 1 cache.
|
|---|
| 623 | AWTKeyStroke cached = (AWTKeyStroke) cache.get(stroke);
|
|---|
| 624 | if (cached == null)
|
|---|
| 625 | cache.put(stroke, stroke);
|
|---|
| 626 | else
|
|---|
| 627 | stroke = cached;
|
|---|
| 628 | return recent = stroke;
|
|---|
| 629 | }
|
|---|
| 630 |
|
|---|
| 631 | /**
|
|---|
| 632 | * Converts the modifiers to the appropriate format.
|
|---|
| 633 | *
|
|---|
| 634 | * @param mod the modifiers to convert
|
|---|
| 635 | * @return the adjusted modifiers
|
|---|
| 636 | */
|
|---|
| 637 | private static int extend(int mod)
|
|---|
| 638 | {
|
|---|
| 639 | if ((mod & (KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK)) != 0)
|
|---|
| 640 | mod |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK;
|
|---|
| 641 | if ((mod & (KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK)) != 0)
|
|---|
| 642 | mod |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK;
|
|---|
| 643 | if ((mod & (KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK)) != 0)
|
|---|
| 644 | mod |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK;
|
|---|
| 645 | if ((mod & (KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK)) != 0)
|
|---|
| 646 | mod |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK;
|
|---|
| 647 | if ((mod & (KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK)) != 0)
|
|---|
| 648 | mod |= KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK;
|
|---|
| 649 | if ((mod & KeyEvent.BUTTON1_MASK) != 0)
|
|---|
| 650 | mod |= KeyEvent.BUTTON1_DOWN_MASK;
|
|---|
| 651 | return mod & MODIFIERS_MASK;
|
|---|
| 652 | }
|
|---|
| 653 | } // class AWTKeyStroke
|
|---|