You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
890 lines
33 KiB
890 lines
33 KiB
/*
|
|
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
package java.awt;
|
|
|
|
import java.awt.event.KeyEvent;
|
|
import sun.awt.AppContext;
|
|
import java.awt.event.InputEvent;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.StringTokenizer;
|
|
import java.io.Serializable;
|
|
import java.security.AccessController;
|
|
import java.security.PrivilegedAction;
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Modifier;
|
|
import java.lang.reflect.Field;
|
|
|
|
/**
|
|
* An <code>AWTKeyStroke</code> represents a key action on the
|
|
* keyboard, or equivalent input device. <code>AWTKeyStroke</code>s
|
|
* can correspond to only a press or release of a
|
|
* particular key, just as <code>KEY_PRESSED</code> and
|
|
* <code>KEY_RELEASED</code> <code>KeyEvent</code>s do;
|
|
* alternately, they can correspond to typing a specific Java character, just
|
|
* as <code>KEY_TYPED</code> <code>KeyEvent</code>s do.
|
|
* In all cases, <code>AWTKeyStroke</code>s can specify modifiers
|
|
* (alt, shift, control, meta, altGraph, or a combination thereof) which must be present
|
|
* during the action for an exact match.
|
|
* <p>
|
|
* <code>AWTKeyStrokes</code> are immutable, and are intended
|
|
* to be unique. Client code should never create an
|
|
* <code>AWTKeyStroke</code> on its own, but should instead use
|
|
* a variant of <code>getAWTKeyStroke</code>. Client use of these factory
|
|
* methods allows the <code>AWTKeyStroke</code> implementation
|
|
* to cache and share instances efficiently.
|
|
*
|
|
* @see #getAWTKeyStroke
|
|
*
|
|
* @author Arnaud Weber
|
|
* @author David Mendenhall
|
|
* @since 1.4
|
|
*/
|
|
public class AWTKeyStroke implements Serializable {
|
|
static final long serialVersionUID = -6430539691155161871L;
|
|
|
|
private static Map<String, Integer> modifierKeywords;
|
|
/**
|
|
* Associates VK_XXX (as a String) with code (as Integer). This is
|
|
* done to avoid the overhead of the reflective call to find the
|
|
* constant.
|
|
*/
|
|
private static VKCollection vks;
|
|
|
|
//A key for the collection of AWTKeyStrokes within AppContext.
|
|
private static Object APP_CONTEXT_CACHE_KEY = new Object();
|
|
//A key withing the cache
|
|
private static AWTKeyStroke APP_CONTEXT_KEYSTROKE_KEY = new AWTKeyStroke();
|
|
|
|
/*
|
|
* Reads keystroke class from AppContext and if null, puts there the
|
|
* AWTKeyStroke class.
|
|
* Must be called under locked AWTKeyStro
|
|
*/
|
|
private static Class<AWTKeyStroke> getAWTKeyStrokeClass() {
|
|
Class<AWTKeyStroke> clazz = (Class)AppContext.getAppContext().get(AWTKeyStroke.class);
|
|
if (clazz == null) {
|
|
clazz = AWTKeyStroke.class;
|
|
AppContext.getAppContext().put(AWTKeyStroke.class, AWTKeyStroke.class);
|
|
}
|
|
return clazz;
|
|
}
|
|
|
|
private char keyChar = KeyEvent.CHAR_UNDEFINED;
|
|
private int keyCode = KeyEvent.VK_UNDEFINED;
|
|
private int modifiers;
|
|
private boolean onKeyRelease;
|
|
|
|
static {
|
|
/* ensure that the necessary native libraries are loaded */
|
|
Toolkit.loadLibraries();
|
|
}
|
|
|
|
/**
|
|
* Constructs an <code>AWTKeyStroke</code> with default values.
|
|
* The default values used are:
|
|
* <table border="1" summary="AWTKeyStroke default values">
|
|
* <tr><th>Property</th><th>Default Value</th></tr>
|
|
* <tr>
|
|
* <td>Key Char</td>
|
|
* <td><code>KeyEvent.CHAR_UNDEFINED</code></td>
|
|
* </tr>
|
|
* <tr>
|
|
* <td>Key Code</td>
|
|
* <td><code>KeyEvent.VK_UNDEFINED</code></td>
|
|
* </tr>
|
|
* <tr>
|
|
* <td>Modifiers</td>
|
|
* <td>none</td>
|
|
* </tr>
|
|
* <tr>
|
|
* <td>On key release?</td>
|
|
* <td><code>false</code></td>
|
|
* </tr>
|
|
* </table>
|
|
*
|
|
* <code>AWTKeyStroke</code>s should not be constructed
|
|
* by client code. Use a variant of <code>getAWTKeyStroke</code>
|
|
* instead.
|
|
*
|
|
* @see #getAWTKeyStroke
|
|
*/
|
|
protected AWTKeyStroke() {
|
|
}
|
|
|
|
/**
|
|
* Constructs an <code>AWTKeyStroke</code> with the specified
|
|
* values. <code>AWTKeyStroke</code>s should not be constructed
|
|
* by client code. Use a variant of <code>getAWTKeyStroke</code>
|
|
* instead.
|
|
*
|
|
* @param keyChar the character value for a keyboard key
|
|
* @param keyCode the key code for this <code>AWTKeyStroke</code>
|
|
* @param modifiers a bitwise-ored combination of any modifiers
|
|
* @param onKeyRelease <code>true</code> if this
|
|
* <code>AWTKeyStroke</code> corresponds
|
|
* to a key release; <code>false</code> otherwise
|
|
* @see #getAWTKeyStroke
|
|
*/
|
|
protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
|
|
boolean onKeyRelease) {
|
|
this.keyChar = keyChar;
|
|
this.keyCode = keyCode;
|
|
this.modifiers = modifiers;
|
|
this.onKeyRelease = onKeyRelease;
|
|
}
|
|
|
|
/**
|
|
* Registers a new class which the factory methods in
|
|
* <code>AWTKeyStroke</code> will use when generating new
|
|
* instances of <code>AWTKeyStroke</code>s. After invoking this
|
|
* method, the factory methods will return instances of the specified
|
|
* Class. The specified Class must be either <code>AWTKeyStroke</code>
|
|
* or derived from <code>AWTKeyStroke</code>, and it must have a
|
|
* no-arg constructor. The constructor can be of any accessibility,
|
|
* including <code>private</code>. This operation
|
|
* flushes the current <code>AWTKeyStroke</code> cache.
|
|
*
|
|
* @param subclass the new Class of which the factory methods should create
|
|
* instances
|
|
* @throws IllegalArgumentException if subclass is <code>null</code>,
|
|
* or if subclass does not have a no-arg constructor
|
|
* @throws ClassCastException if subclass is not
|
|
* <code>AWTKeyStroke</code>, or a class derived from
|
|
* <code>AWTKeyStroke</code>
|
|
*/
|
|
protected static void registerSubclass(Class<?> subclass) {
|
|
if (subclass == null) {
|
|
throw new IllegalArgumentException("subclass cannot be null");
|
|
}
|
|
synchronized (AWTKeyStroke.class) {
|
|
Class<AWTKeyStroke> keyStrokeClass = (Class)AppContext.getAppContext().get(AWTKeyStroke.class);
|
|
if (keyStrokeClass != null && keyStrokeClass.equals(subclass)){
|
|
// Already registered
|
|
return;
|
|
}
|
|
}
|
|
if (!AWTKeyStroke.class.isAssignableFrom(subclass)) {
|
|
throw new ClassCastException("subclass is not derived from AWTKeyStroke");
|
|
}
|
|
|
|
Constructor ctor = getCtor(subclass);
|
|
|
|
String couldNotInstantiate = "subclass could not be instantiated";
|
|
|
|
if (ctor == null) {
|
|
throw new IllegalArgumentException(couldNotInstantiate);
|
|
}
|
|
try {
|
|
AWTKeyStroke stroke = (AWTKeyStroke)ctor.newInstance((Object[]) null);
|
|
if (stroke == null) {
|
|
throw new IllegalArgumentException(couldNotInstantiate);
|
|
}
|
|
} catch (NoSuchMethodError e) {
|
|
throw new IllegalArgumentException(couldNotInstantiate);
|
|
} catch (ExceptionInInitializerError e) {
|
|
throw new IllegalArgumentException(couldNotInstantiate);
|
|
} catch (InstantiationException e) {
|
|
throw new IllegalArgumentException(couldNotInstantiate);
|
|
} catch (IllegalAccessException e) {
|
|
throw new IllegalArgumentException(couldNotInstantiate);
|
|
} catch (InvocationTargetException e) {
|
|
throw new IllegalArgumentException(couldNotInstantiate);
|
|
}
|
|
|
|
synchronized (AWTKeyStroke.class) {
|
|
AppContext.getAppContext().put(AWTKeyStroke.class, subclass);
|
|
AppContext.getAppContext().remove(APP_CONTEXT_CACHE_KEY);
|
|
AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);
|
|
}
|
|
}
|
|
|
|
/* returns noarg Constructor for class with accessible flag. No security
|
|
threat as accessible flag is set only for this Constructor object,
|
|
not for Class constructor.
|
|
*/
|
|
private static Constructor getCtor(final Class clazz)
|
|
{
|
|
Constructor ctor = AccessController.doPrivileged(new PrivilegedAction<Constructor>() {
|
|
public Constructor run() {
|
|
try {
|
|
Constructor ctor = clazz.getDeclaredConstructor((Class[]) null);
|
|
if (ctor != null) {
|
|
ctor.setAccessible(true);
|
|
}
|
|
return ctor;
|
|
} catch (SecurityException e) {
|
|
} catch (NoSuchMethodException e) {
|
|
}
|
|
return null;
|
|
}
|
|
});
|
|
return (Constructor)ctor;
|
|
}
|
|
|
|
private static synchronized AWTKeyStroke getCachedStroke
|
|
(char keyChar, int keyCode, int modifiers, boolean onKeyRelease)
|
|
{
|
|
Map<AWTKeyStroke, AWTKeyStroke> cache = (Map)AppContext.getAppContext().get(APP_CONTEXT_CACHE_KEY);
|
|
AWTKeyStroke cacheKey = (AWTKeyStroke)AppContext.getAppContext().get(APP_CONTEXT_KEYSTROKE_KEY);
|
|
|
|
if (cache == null) {
|
|
cache = new HashMap<>();
|
|
AppContext.getAppContext().put(APP_CONTEXT_CACHE_KEY, cache);
|
|
}
|
|
|
|
if (cacheKey == null) {
|
|
try {
|
|
Class<AWTKeyStroke> clazz = getAWTKeyStrokeClass();
|
|
cacheKey = (AWTKeyStroke)getCtor(clazz).newInstance((Object[]) null);
|
|
AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey);
|
|
} catch (InstantiationException e) {
|
|
assert(false);
|
|
} catch (IllegalAccessException e) {
|
|
assert(false);
|
|
} catch (InvocationTargetException e) {
|
|
assert(false);
|
|
}
|
|
}
|
|
cacheKey.keyChar = keyChar;
|
|
cacheKey.keyCode = keyCode;
|
|
cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers));
|
|
cacheKey.onKeyRelease = onKeyRelease;
|
|
|
|
AWTKeyStroke stroke = (AWTKeyStroke)cache.get(cacheKey);
|
|
if (stroke == null) {
|
|
stroke = cacheKey;
|
|
cache.put(stroke, stroke);
|
|
AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);
|
|
}
|
|
return stroke;
|
|
}
|
|
|
|
/**
|
|
* Returns a shared instance of an <code>AWTKeyStroke</code>
|
|
* that represents a <code>KEY_TYPED</code> event for the
|
|
* specified character.
|
|
*
|
|
* @param keyChar the character value for a keyboard key
|
|
* @return an <code>AWTKeyStroke</code> object for that key
|
|
*/
|
|
public static AWTKeyStroke getAWTKeyStroke(char keyChar) {
|
|
return getCachedStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);
|
|
}
|
|
|
|
/**
|
|
* Returns a shared instance of an {@code AWTKeyStroke}
|
|
* that represents a {@code KEY_TYPED} event for the
|
|
* specified Character object and a set of modifiers. Note
|
|
* that the first parameter is of type Character rather than
|
|
* char. This is to avoid inadvertent clashes with
|
|
* calls to <code>getAWTKeyStroke(int keyCode, int modifiers)</code>.
|
|
*
|
|
* The modifiers consist of any combination of following:<ul>
|
|
* <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.CTRL_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.META_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
|
|
* </ul>
|
|
* The old modifiers listed below also can be used, but they are
|
|
* mapped to _DOWN_ modifiers. <ul>
|
|
* <li>java.awt.event.InputEvent.SHIFT_MASK
|
|
* <li>java.awt.event.InputEvent.CTRL_MASK
|
|
* <li>java.awt.event.InputEvent.META_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_GRAPH_MASK
|
|
* </ul>
|
|
* also can be used, but they are mapped to _DOWN_ modifiers.
|
|
*
|
|
* Since these numbers are all different powers of two, any combination of
|
|
* them is an integer in which each bit represents a different modifier
|
|
* key. Use 0 to specify no modifiers.
|
|
*
|
|
* @param keyChar the Character object for a keyboard character
|
|
* @param modifiers a bitwise-ored combination of any modifiers
|
|
* @return an <code>AWTKeyStroke</code> object for that key
|
|
* @throws IllegalArgumentException if <code>keyChar</code> is
|
|
* <code>null</code>
|
|
*
|
|
* @see java.awt.event.InputEvent
|
|
*/
|
|
public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers)
|
|
{
|
|
if (keyChar == null) {
|
|
throw new IllegalArgumentException("keyChar cannot be null");
|
|
}
|
|
return getCachedStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED,
|
|
modifiers, false);
|
|
}
|
|
|
|
/**
|
|
* Returns a shared instance of an <code>AWTKeyStroke</code>,
|
|
* given a numeric key code and a set of modifiers, specifying
|
|
* whether the key is activated when it is pressed or released.
|
|
* <p>
|
|
* The "virtual key" constants defined in
|
|
* <code>java.awt.event.KeyEvent</code> can be
|
|
* used to specify the key code. For example:<ul>
|
|
* <li><code>java.awt.event.KeyEvent.VK_ENTER</code>
|
|
* <li><code>java.awt.event.KeyEvent.VK_TAB</code>
|
|
* <li><code>java.awt.event.KeyEvent.VK_SPACE</code>
|
|
* </ul>
|
|
* Alternatively, the key code may be obtained by calling
|
|
* <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar</code>.
|
|
*
|
|
* The modifiers consist of any combination of:<ul>
|
|
* <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.CTRL_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.META_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
|
|
* </ul>
|
|
* The old modifiers <ul>
|
|
* <li>java.awt.event.InputEvent.SHIFT_MASK
|
|
* <li>java.awt.event.InputEvent.CTRL_MASK
|
|
* <li>java.awt.event.InputEvent.META_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_GRAPH_MASK
|
|
* </ul>
|
|
* also can be used, but they are mapped to _DOWN_ modifiers.
|
|
*
|
|
* Since these numbers are all different powers of two, any combination of
|
|
* them is an integer in which each bit represents a different modifier
|
|
* key. Use 0 to specify no modifiers.
|
|
*
|
|
* @param keyCode an int specifying the numeric code for a keyboard key
|
|
* @param modifiers a bitwise-ored combination of any modifiers
|
|
* @param onKeyRelease <code>true</code> if the <code>AWTKeyStroke</code>
|
|
* should represent a key release; <code>false</code> otherwise
|
|
* @return an AWTKeyStroke object for that key
|
|
*
|
|
* @see java.awt.event.KeyEvent
|
|
* @see java.awt.event.InputEvent
|
|
*/
|
|
public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers,
|
|
boolean onKeyRelease) {
|
|
return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers,
|
|
onKeyRelease);
|
|
}
|
|
|
|
/**
|
|
* Returns a shared instance of an <code>AWTKeyStroke</code>,
|
|
* given a numeric key code and a set of modifiers. The returned
|
|
* <code>AWTKeyStroke</code> will correspond to a key press.
|
|
* <p>
|
|
* The "virtual key" constants defined in
|
|
* <code>java.awt.event.KeyEvent</code> can be
|
|
* used to specify the key code. For example:<ul>
|
|
* <li><code>java.awt.event.KeyEvent.VK_ENTER</code>
|
|
* <li><code>java.awt.event.KeyEvent.VK_TAB</code>
|
|
* <li><code>java.awt.event.KeyEvent.VK_SPACE</code>
|
|
* </ul>
|
|
* The modifiers consist of any combination of:<ul>
|
|
* <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.CTRL_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.META_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_DOWN_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
|
|
* </ul>
|
|
* The old modifiers <ul>
|
|
* <li>java.awt.event.InputEvent.SHIFT_MASK
|
|
* <li>java.awt.event.InputEvent.CTRL_MASK
|
|
* <li>java.awt.event.InputEvent.META_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_MASK
|
|
* <li>java.awt.event.InputEvent.ALT_GRAPH_MASK
|
|
* </ul>
|
|
* also can be used, but they are mapped to _DOWN_ modifiers.
|
|
*
|
|
* Since these numbers are all different powers of two, any combination of
|
|
* them is an integer in which each bit represents a different modifier
|
|
* key. Use 0 to specify no modifiers.
|
|
*
|
|
* @param keyCode an int specifying the numeric code for a keyboard key
|
|
* @param modifiers a bitwise-ored combination of any modifiers
|
|
* @return an <code>AWTKeyStroke</code> object for that key
|
|
*
|
|
* @see java.awt.event.KeyEvent
|
|
* @see java.awt.event.InputEvent
|
|
*/
|
|
public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) {
|
|
return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers,
|
|
false);
|
|
}
|
|
|
|
/**
|
|
* Returns an <code>AWTKeyStroke</code> which represents the
|
|
* stroke which generated a given <code>KeyEvent</code>.
|
|
* <p>
|
|
* This method obtains the keyChar from a <code>KeyTyped</code>
|
|
* event, and the keyCode from a <code>KeyPressed</code> or
|
|
* <code>KeyReleased</code> event. The <code>KeyEvent</code> modifiers are
|
|
* obtained for all three types of <code>KeyEvent</code>.
|
|
*
|
|
* @param anEvent the <code>KeyEvent</code> from which to
|
|
* obtain the <code>AWTKeyStroke</code>
|
|
* @throws NullPointerException if <code>anEvent</code> is null
|
|
* @return the <code>AWTKeyStroke</code> that precipitated the event
|
|
*/
|
|
public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent anEvent) {
|
|
int id = anEvent.getID();
|
|
switch(id) {
|
|
case KeyEvent.KEY_PRESSED:
|
|
case KeyEvent.KEY_RELEASED:
|
|
return getCachedStroke(KeyEvent.CHAR_UNDEFINED,
|
|
anEvent.getKeyCode(),
|
|
anEvent.getModifiers(),
|
|
(id == KeyEvent.KEY_RELEASED));
|
|
case KeyEvent.KEY_TYPED:
|
|
return getCachedStroke(anEvent.getKeyChar(),
|
|
KeyEvent.VK_UNDEFINED,
|
|
anEvent.getModifiers(),
|
|
false);
|
|
default:
|
|
// Invalid ID for this KeyEvent
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parses a string and returns an <code>AWTKeyStroke</code>.
|
|
* The string must have the following syntax:
|
|
* <pre>
|
|
* <modifiers>* (<typedID> | <pressedReleasedID>)
|
|
*
|
|
* modifiers := shift | control | ctrl | meta | alt | altGraph
|
|
* typedID := typed <typedKey>
|
|
* typedKey := string of length 1 giving Unicode character.
|
|
* pressedReleasedID := (pressed | released) key
|
|
* key := KeyEvent key code name, i.e. the name following "VK_".
|
|
* </pre>
|
|
* If typed, pressed or released is not specified, pressed is assumed. Here
|
|
* are some examples:
|
|
* <pre>
|
|
* "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0);
|
|
* "control DELETE" => getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);
|
|
* "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);
|
|
* "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);
|
|
* "typed a" => getAWTKeyStroke('a');
|
|
* </pre>
|
|
*
|
|
* @param s a String formatted as described above
|
|
* @return an <code>AWTKeyStroke</code> object for that String
|
|
* @throws IllegalArgumentException if <code>s</code> is <code>null</code>,
|
|
* or is formatted incorrectly
|
|
*/
|
|
public static AWTKeyStroke getAWTKeyStroke(String s) {
|
|
if (s == null) {
|
|
throw new IllegalArgumentException("String cannot be null");
|
|
}
|
|
|
|
final String errmsg = "String formatted incorrectly";
|
|
|
|
StringTokenizer st = new StringTokenizer(s, " ");
|
|
|
|
int mask = 0;
|
|
boolean released = false;
|
|
boolean typed = false;
|
|
boolean pressed = false;
|
|
|
|
synchronized (AWTKeyStroke.class) {
|
|
if (modifierKeywords == null) {
|
|
Map<String, Integer> uninitializedMap = new HashMap<>(8, 1.0f);
|
|
uninitializedMap.put("shift",
|
|
Integer.valueOf(InputEvent.SHIFT_DOWN_MASK
|
|
|InputEvent.SHIFT_MASK));
|
|
uninitializedMap.put("control",
|
|
Integer.valueOf(InputEvent.CTRL_DOWN_MASK
|
|
|InputEvent.CTRL_MASK));
|
|
uninitializedMap.put("ctrl",
|
|
Integer.valueOf(InputEvent.CTRL_DOWN_MASK
|
|
|InputEvent.CTRL_MASK));
|
|
uninitializedMap.put("meta",
|
|
Integer.valueOf(InputEvent.META_DOWN_MASK
|
|
|InputEvent.META_MASK));
|
|
uninitializedMap.put("alt",
|
|
Integer.valueOf(InputEvent.ALT_DOWN_MASK
|
|
|InputEvent.ALT_MASK));
|
|
uninitializedMap.put("altGraph",
|
|
Integer.valueOf(InputEvent.ALT_GRAPH_DOWN_MASK
|
|
|InputEvent.ALT_GRAPH_MASK));
|
|
uninitializedMap.put("button1",
|
|
Integer.valueOf(InputEvent.BUTTON1_DOWN_MASK));
|
|
uninitializedMap.put("button2",
|
|
Integer.valueOf(InputEvent.BUTTON2_DOWN_MASK));
|
|
uninitializedMap.put("button3",
|
|
Integer.valueOf(InputEvent.BUTTON3_DOWN_MASK));
|
|
modifierKeywords =
|
|
Collections.synchronizedMap(uninitializedMap);
|
|
}
|
|
}
|
|
|
|
int count = st.countTokens();
|
|
|
|
for (int i = 1; i <= count; i++) {
|
|
String token = st.nextToken();
|
|
|
|
if (typed) {
|
|
if (token.length() != 1 || i != count) {
|
|
throw new IllegalArgumentException(errmsg);
|
|
}
|
|
return getCachedStroke(token.charAt(0), KeyEvent.VK_UNDEFINED,
|
|
mask, false);
|
|
}
|
|
|
|
if (pressed || released || i == count) {
|
|
if (i != count) {
|
|
throw new IllegalArgumentException(errmsg);
|
|
}
|
|
|
|
String keyCodeName = "VK_" + token;
|
|
int keyCode = getVKValue(keyCodeName);
|
|
|
|
return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
|
|
mask, released);
|
|
}
|
|
|
|
if (token.equals("released")) {
|
|
released = true;
|
|
continue;
|
|
}
|
|
if (token.equals("pressed")) {
|
|
pressed = true;
|
|
continue;
|
|
}
|
|
if (token.equals("typed")) {
|
|
typed = true;
|
|
continue;
|
|
}
|
|
|
|
Integer tokenMask = (Integer)modifierKeywords.get(token);
|
|
if (tokenMask != null) {
|
|
mask |= tokenMask.intValue();
|
|
} else {
|
|
throw new IllegalArgumentException(errmsg);
|
|
}
|
|
}
|
|
|
|
throw new IllegalArgumentException(errmsg);
|
|
}
|
|
|
|
private static VKCollection getVKCollection() {
|
|
if (vks == null) {
|
|
vks = new VKCollection();
|
|
}
|
|
return vks;
|
|
}
|
|
/**
|
|
* Returns the integer constant for the KeyEvent.VK field named
|
|
* <code>key</code>. This will throw an
|
|
* <code>IllegalArgumentException</code> if <code>key</code> is
|
|
* not a valid constant.
|
|
*/
|
|
private static int getVKValue(String key) {
|
|
VKCollection vkCollect = getVKCollection();
|
|
|
|
Integer value = vkCollect.findCode(key);
|
|
|
|
if (value == null) {
|
|
int keyCode = 0;
|
|
final String errmsg = "String formatted incorrectly";
|
|
|
|
try {
|
|
keyCode = KeyEvent.class.getField(key).getInt(KeyEvent.class);
|
|
} catch (NoSuchFieldException nsfe) {
|
|
throw new IllegalArgumentException(errmsg);
|
|
} catch (IllegalAccessException iae) {
|
|
throw new IllegalArgumentException(errmsg);
|
|
}
|
|
value = Integer.valueOf(keyCode);
|
|
vkCollect.put(key, value);
|
|
}
|
|
return value.intValue();
|
|
}
|
|
|
|
/**
|
|
* Returns the character for this <code>AWTKeyStroke</code>.
|
|
*
|
|
* @return a char value
|
|
* @see #getAWTKeyStroke(char)
|
|
* @see KeyEvent#getKeyChar
|
|
*/
|
|
public final char getKeyChar() {
|
|
return keyChar;
|
|
}
|
|
|
|
/**
|
|
* Returns the numeric key code for this <code>AWTKeyStroke</code>.
|
|
*
|
|
* @return an int containing the key code value
|
|
* @see #getAWTKeyStroke(int,int)
|
|
* @see KeyEvent#getKeyCode
|
|
*/
|
|
public final int getKeyCode() {
|
|
return keyCode;
|
|
}
|
|
|
|
/**
|
|
* Returns the modifier keys for this <code>AWTKeyStroke</code>.
|
|
*
|
|
* @return an int containing the modifiers
|
|
* @see #getAWTKeyStroke(int,int)
|
|
*/
|
|
public final int getModifiers() {
|
|
return modifiers;
|
|
}
|
|
|
|
/**
|
|
* Returns whether this <code>AWTKeyStroke</code> represents a key release.
|
|
*
|
|
* @return <code>true</code> if this <code>AWTKeyStroke</code>
|
|
* represents a key release; <code>false</code> otherwise
|
|
* @see #getAWTKeyStroke(int,int,boolean)
|
|
*/
|
|
public final boolean isOnKeyRelease() {
|
|
return onKeyRelease;
|
|
}
|
|
|
|
/**
|
|
* Returns the type of <code>KeyEvent</code> which corresponds to
|
|
* this <code>AWTKeyStroke</code>.
|
|
*
|
|
* @return <code>KeyEvent.KEY_PRESSED</code>,
|
|
* <code>KeyEvent.KEY_TYPED</code>,
|
|
* or <code>KeyEvent.KEY_RELEASED</code>
|
|
* @see java.awt.event.KeyEvent
|
|
*/
|
|
public final int getKeyEventType() {
|
|
if (keyCode == KeyEvent.VK_UNDEFINED) {
|
|
return KeyEvent.KEY_TYPED;
|
|
} else {
|
|
return (onKeyRelease)
|
|
? KeyEvent.KEY_RELEASED
|
|
: KeyEvent.KEY_PRESSED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a numeric value for this object that is likely to be unique,
|
|
* making it a good choice as the index value in a hash table.
|
|
*
|
|
* @return an int that represents this object
|
|
*/
|
|
public int hashCode() {
|
|
return (((int)keyChar) + 1) * (2 * (keyCode + 1)) * (modifiers + 1) +
|
|
(onKeyRelease ? 1 : 2);
|
|
}
|
|
|
|
/**
|
|
* Returns true if this object is identical to the specified object.
|
|
*
|
|
* @param anObject the Object to compare this object to
|
|
* @return true if the objects are identical
|
|
*/
|
|
public final boolean equals(Object anObject) {
|
|
if (anObject instanceof AWTKeyStroke) {
|
|
AWTKeyStroke ks = (AWTKeyStroke)anObject;
|
|
return (ks.keyChar == keyChar && ks.keyCode == keyCode &&
|
|
ks.onKeyRelease == onKeyRelease &&
|
|
ks.modifiers == modifiers);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns a string that displays and identifies this object's properties.
|
|
* The <code>String</code> returned by this method can be passed
|
|
* as a parameter to <code>getAWTKeyStroke(String)</code> to produce
|
|
* a key stroke equal to this key stroke.
|
|
*
|
|
* @return a String representation of this object
|
|
* @see #getAWTKeyStroke(String)
|
|
*/
|
|
public String toString() {
|
|
if (keyCode == KeyEvent.VK_UNDEFINED) {
|
|
return getModifiersText(modifiers) + "typed " + keyChar;
|
|
} else {
|
|
return getModifiersText(modifiers) +
|
|
(onKeyRelease ? "released" : "pressed") + " " +
|
|
getVKText(keyCode);
|
|
}
|
|
}
|
|
|
|
static String getModifiersText(int modifiers) {
|
|
StringBuilder buf = new StringBuilder();
|
|
|
|
if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0 ) {
|
|
buf.append("shift ");
|
|
}
|
|
if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0 ) {
|
|
buf.append("ctrl ");
|
|
}
|
|
if ((modifiers & InputEvent.META_DOWN_MASK) != 0 ) {
|
|
buf.append("meta ");
|
|
}
|
|
if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0 ) {
|
|
buf.append("alt ");
|
|
}
|
|
if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0 ) {
|
|
buf.append("altGraph ");
|
|
}
|
|
if ((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0 ) {
|
|
buf.append("button1 ");
|
|
}
|
|
if ((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0 ) {
|
|
buf.append("button2 ");
|
|
}
|
|
if ((modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {
|
|
buf.append("button3 ");
|
|
}
|
|
|
|
return buf.toString();
|
|
}
|
|
|
|
static String getVKText(int keyCode) {
|
|
VKCollection vkCollect = getVKCollection();
|
|
Integer key = Integer.valueOf(keyCode);
|
|
String name = vkCollect.findName(key);
|
|
if (name != null) {
|
|
return name.substring(3);
|
|
}
|
|
int expected_modifiers =
|
|
(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
|
|
|
|
Field[] fields = KeyEvent.class.getDeclaredFields();
|
|
for (int i = 0; i < fields.length; i++) {
|
|
try {
|
|
if (fields[i].getModifiers() == expected_modifiers
|
|
&& fields[i].getType() == Integer.TYPE
|
|
&& fields[i].getName().startsWith("VK_")
|
|
&& fields[i].getInt(KeyEvent.class) == keyCode)
|
|
{
|
|
name = fields[i].getName();
|
|
vkCollect.put(name, key);
|
|
return name.substring(3);
|
|
}
|
|
} catch (IllegalAccessException e) {
|
|
assert(false);
|
|
}
|
|
}
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
/**
|
|
* Returns a cached instance of <code>AWTKeyStroke</code> (or a subclass of
|
|
* <code>AWTKeyStroke</code>) which is equal to this instance.
|
|
*
|
|
* @return a cached instance which is equal to this instance
|
|
*/
|
|
protected Object readResolve() throws java.io.ObjectStreamException {
|
|
synchronized (AWTKeyStroke.class) {
|
|
if (getClass().equals(getAWTKeyStrokeClass())) {
|
|
return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
private static int mapOldModifiers(int modifiers) {
|
|
if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
|
|
modifiers |= InputEvent.SHIFT_DOWN_MASK;
|
|
}
|
|
if ((modifiers & InputEvent.ALT_MASK) != 0) {
|
|
modifiers |= InputEvent.ALT_DOWN_MASK;
|
|
}
|
|
if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {
|
|
modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
|
|
}
|
|
if ((modifiers & InputEvent.CTRL_MASK) != 0) {
|
|
modifiers |= InputEvent.CTRL_DOWN_MASK;
|
|
}
|
|
if ((modifiers & InputEvent.META_MASK) != 0) {
|
|
modifiers |= InputEvent.META_DOWN_MASK;
|
|
}
|
|
|
|
modifiers &= InputEvent.SHIFT_DOWN_MASK
|
|
| InputEvent.ALT_DOWN_MASK
|
|
| InputEvent.ALT_GRAPH_DOWN_MASK
|
|
| InputEvent.CTRL_DOWN_MASK
|
|
| InputEvent.META_DOWN_MASK
|
|
| InputEvent.BUTTON1_DOWN_MASK
|
|
| InputEvent.BUTTON2_DOWN_MASK
|
|
| InputEvent.BUTTON3_DOWN_MASK;
|
|
|
|
return modifiers;
|
|
}
|
|
|
|
private static int mapNewModifiers(int modifiers) {
|
|
if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) {
|
|
modifiers |= InputEvent.SHIFT_MASK;
|
|
}
|
|
if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) {
|
|
modifiers |= InputEvent.ALT_MASK;
|
|
}
|
|
if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
|
|
modifiers |= InputEvent.ALT_GRAPH_MASK;
|
|
}
|
|
if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) {
|
|
modifiers |= InputEvent.CTRL_MASK;
|
|
}
|
|
if ((modifiers & InputEvent.META_DOWN_MASK) != 0) {
|
|
modifiers |= InputEvent.META_MASK;
|
|
}
|
|
|
|
return modifiers;
|
|
}
|
|
|
|
}
|
|
|
|
class VKCollection {
|
|
Map<Integer, String> code2name;
|
|
Map<String, Integer> name2code;
|
|
|
|
public VKCollection() {
|
|
code2name = new HashMap<>();
|
|
name2code = new HashMap<>();
|
|
}
|
|
|
|
public synchronized void put(String name, Integer code) {
|
|
assert((name != null) && (code != null));
|
|
assert(findName(code) == null);
|
|
assert(findCode(name) == null);
|
|
code2name.put(code, name);
|
|
name2code.put(name, code);
|
|
}
|
|
|
|
public synchronized Integer findCode(String name) {
|
|
assert(name != null);
|
|
return (Integer)name2code.get(name);
|
|
}
|
|
|
|
public synchronized String findName(Integer code) {
|
|
assert(code != null);
|
|
return (String)code2name.get(code);
|
|
}
|
|
}
|