| /* |
| * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| 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.lang.reflect.Modifier; |
| import java.lang.reflect.Field; |
| import sun.swing.SwingAccessor; |
| |
| /** |
| * An {@code AWTKeyStroke} represents a key action on the |
| * keyboard, or equivalent input device. {@code AWTKeyStroke}s |
| * can correspond to only a press or release of a |
| * particular key, just as {@code KEY_PRESSED} and |
| * {@code KEY_RELEASED KeyEvent}s do; |
| * alternately, they can correspond to typing a specific Java character, just |
| * as {@code KEY_TYPED KeyEvent}s do. |
| * In all cases, {@code AWTKeyStroke}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} are immutable, and are intended |
| * to be unique. Client code should never create an |
| * {@code AWTKeyStroke} on its own, but should instead use |
| * a variant of {@code getAWTKeyStroke}. Client use of these factory |
| * methods allows the {@code AWTKeyStroke} 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(); |
| |
| 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} with default values. |
| * The default values used are: |
| * |
| * <table class="striped"> |
| * <caption>AWTKeyStroke default values</caption> |
| * <thead> |
| * <tr><th>Property</th><th>Default Value</th></tr> |
| * </thead> |
| * <tbody> |
| * <tr> |
| * <td>Key Char</td> |
| * <td>{@code KeyEvent.CHAR_UNDEFINED}</td> |
| * </tr> |
| * <tr> |
| * <td>Key Code</td> |
| * <td>{@code KeyEvent.VK_UNDEFINED}</td> |
| * </tr> |
| * <tr> |
| * <td>Modifiers</td> |
| * <td>none</td> |
| * </tr> |
| * <tr> |
| * <td>On key release?</td> |
| * <td>{@code false}</td> |
| * </tr> |
| * </tbody> |
| * </table> |
| * |
| * {@code AWTKeyStroke}s should not be constructed |
| * by client code. Use a variant of {@code getAWTKeyStroke} |
| * instead. |
| * |
| * @see #getAWTKeyStroke |
| */ |
| protected AWTKeyStroke() { |
| } |
| |
| /** |
| * Constructs an {@code AWTKeyStroke} with the specified |
| * values. {@code AWTKeyStroke}s should not be constructed |
| * by client code. Use a variant of {@code getAWTKeyStroke} |
| * instead. |
| * |
| * @param keyChar the character value for a keyboard key |
| * @param keyCode the key code for this {@code AWTKeyStroke} |
| * @param modifiers a bitwise-ored combination of any modifiers |
| * @param onKeyRelease {@code true} if this |
| * {@code AWTKeyStroke} corresponds |
| * to a key release; {@code false} 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; |
| } |
| |
| /** |
| * The method has no effect and is only left present to avoid introducing |
| * a binary incompatibility. |
| * |
| * @param subclass the new Class of which the factory methods should create |
| * instances |
| * @deprecated |
| */ |
| @Deprecated |
| protected static void registerSubclass(Class<?> subclass) { |
| } |
| |
| private static synchronized AWTKeyStroke getCachedStroke |
| (char keyChar, int keyCode, int modifiers, boolean onKeyRelease) |
| { |
| @SuppressWarnings("unchecked") |
| 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) { |
| cacheKey = SwingAccessor.getKeyStrokeAccessor().create(); |
| AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey); |
| } |
| |
| cacheKey.keyChar = keyChar; |
| cacheKey.keyCode = keyCode; |
| cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers)); |
| cacheKey.onKeyRelease = onKeyRelease; |
| |
| AWTKeyStroke stroke = 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} |
| * that represents a {@code KEY_TYPED} event for the |
| * specified character. |
| * |
| * @param keyChar the character value for a keyboard key |
| * @return an {@code AWTKeyStroke} 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)}. |
| * |
| * 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} object for that key |
| * @throws IllegalArgumentException if {@code keyChar} is |
| * {@code null} |
| * |
| * @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}, |
| * 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} can be |
| * used to specify the key code. For example:<ul> |
| * <li>{@code java.awt.event.KeyEvent.VK_ENTER} |
| * <li>{@code java.awt.event.KeyEvent.VK_TAB} |
| * <li>{@code java.awt.event.KeyEvent.VK_SPACE} |
| * </ul> |
| * Alternatively, the key code may be obtained by calling |
| * {@code java.awt.event.KeyEvent.getExtendedKeyCodeForChar}. |
| * |
| * 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} if the {@code AWTKeyStroke} |
| * should represent a key release; {@code false} 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}, |
| * given a numeric key code and a set of modifiers. The returned |
| * {@code AWTKeyStroke} will correspond to a key press. |
| * <p> |
| * The "virtual key" constants defined in |
| * {@code java.awt.event.KeyEvent} can be |
| * used to specify the key code. For example:<ul> |
| * <li>{@code java.awt.event.KeyEvent.VK_ENTER} |
| * <li>{@code java.awt.event.KeyEvent.VK_TAB} |
| * <li>{@code java.awt.event.KeyEvent.VK_SPACE} |
| * </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} 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} which represents the |
| * stroke which generated a given {@code KeyEvent}. |
| * <p> |
| * This method obtains the keyChar from a {@code KeyTyped} |
| * event, and the keyCode from a {@code KeyPressed} or |
| * {@code KeyReleased} event. The {@code KeyEvent} modifiers are |
| * obtained for all three types of {@code KeyEvent}. |
| * |
| * @param anEvent the {@code KeyEvent} from which to |
| * obtain the {@code AWTKeyStroke} |
| * @throws NullPointerException if {@code anEvent} is null |
| * @return the {@code AWTKeyStroke} that precipitated the event |
| */ |
| @SuppressWarnings("deprecation") |
| 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}. |
| * 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} object for that String |
| * @throws IllegalArgumentException if {@code s} is {@code null}, |
| * or is formatted incorrectly |
| */ |
| @SuppressWarnings("deprecation") |
| 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 = 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}. This will throw an |
| * {@code IllegalArgumentException} if {@code key} 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}. |
| * |
| * @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}. |
| * |
| * @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}. |
| * |
| * @return an int containing the modifiers |
| * @see #getAWTKeyStroke(int,int) |
| */ |
| public final int getModifiers() { |
| return modifiers; |
| } |
| |
| /** |
| * Returns whether this {@code AWTKeyStroke} represents a key release. |
| * |
| * @return {@code true} if this {@code AWTKeyStroke} |
| * represents a key release; {@code false} otherwise |
| * @see #getAWTKeyStroke(int,int,boolean) |
| */ |
| public final boolean isOnKeyRelease() { |
| return onKeyRelease; |
| } |
| |
| /** |
| * Returns the type of {@code KeyEvent} which corresponds to |
| * this {@code AWTKeyStroke}. |
| * |
| * @return {@code KeyEvent.KEY_PRESSED}, |
| * {@code KeyEvent.KEY_TYPED}, |
| * or {@code KeyEvent.KEY_RELEASED} |
| * @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} returned by this method can be passed |
| * as a parameter to {@code getAWTKeyStroke(String)} 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} (or a subclass of |
| * {@code AWTKeyStroke}) which is equal to this instance. |
| * |
| * @return a cached instance which is equal to this instance |
| * @throws java.io.ObjectStreamException if a serialization problem occurs |
| */ |
| protected Object readResolve() throws java.io.ObjectStreamException { |
| synchronized (AWTKeyStroke.class) { |
| |
| return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease); |
| } |
| } |
| |
| @SuppressWarnings("deprecation") |
| 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; |
| } |
| |
| @SuppressWarnings("deprecation") |
| 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 name2code.get(name); |
| } |
| |
| public synchronized String findName(Integer code) { |
| assert(code != null); |
| return code2name.get(code); |
| } |
| } |