/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package javafx.scene.input; import com.sun.javafx.tk.Toolkit; import java.util.ArrayList; import java.util.List; import java.util.Locale; // PENDING_DOC_REVIEW /** * Represents a combination of keys which are used in keyboard shortcuts. * A key combination consists of a main key and a set of modifier keys. The main * key can be specified by its key code - {@code KeyCodeCombination} or key * character - {@code KeyCharacterCombination}. A modifier key is {@code shift}, * {@code control}, {@code alt}, {@code meta} or {@code shortcut} and can be * defined as {@code DOWN}, {@code UP} or {@code ANY}. *
* The {@code shortcut} modifier is used to represent the modifier key which is * used commonly in keyboard shortcuts on the host platform. This is for * example {@code control} on Windows and {@code meta} (command key) on Mac. * By using {@code shortcut} key modifier developers can create platform * independent shortcuts. So the "Shortcut+C" key combination is handled * internally as "Ctrl+C" on Windows and "Meta+C" on Mac. * @since JavaFX 2.0 */ public abstract class KeyCombination { /** Modifier which specifies that the {@code shift} key must be down. */ public static final Modifier SHIFT_DOWN = new Modifier(KeyCode.SHIFT, ModifierValue.DOWN); /** * Modifier which specifies that the {@code shift} key can be either up or * down. */ public static final Modifier SHIFT_ANY = new Modifier(KeyCode.SHIFT, ModifierValue.ANY); /** Modifier which specifies that the {@code control} key must be down. */ public static final Modifier CONTROL_DOWN = new Modifier(KeyCode.CONTROL, ModifierValue.DOWN); /** * Modifier which specifies that the {@code control} key can be either up or * down. */ public static final Modifier CONTROL_ANY = new Modifier(KeyCode.CONTROL, ModifierValue.ANY); /** Modifier which specifies that the {@code alt} key must be down. */ public static final Modifier ALT_DOWN = new Modifier(KeyCode.ALT, ModifierValue.DOWN); /** * Modifier which specifies that the {@code alt} key can be either up or * down. */ public static final Modifier ALT_ANY = new Modifier(KeyCode.ALT, ModifierValue.ANY); /** Modifier which specifies that the {@code meta} key must be down. */ public static final Modifier META_DOWN = new Modifier(KeyCode.META, ModifierValue.DOWN); /** * Modifier which specifies that the {@code meta} key can be either up or * down. */ public static final Modifier META_ANY = new Modifier(KeyCode.META, ModifierValue.ANY); /** Modifier which specifies that the {@code shortcut} key must be down. */ public static final Modifier SHORTCUT_DOWN = new Modifier(KeyCode.SHORTCUT, ModifierValue.DOWN); /** * Modifier which specifies that the {@code shortcut} key can be either up * or down. */ public static final Modifier SHORTCUT_ANY = new Modifier(KeyCode.SHORTCUT, ModifierValue.ANY); private static final Modifier[] POSSIBLE_MODIFIERS = { SHIFT_DOWN, SHIFT_ANY, CONTROL_DOWN, CONTROL_ANY, ALT_DOWN, ALT_ANY, META_DOWN, META_ANY, SHORTCUT_DOWN, SHORTCUT_ANY }; /** * A KeyCombination that will match with no events. */ public static final KeyCombination NO_MATCH = new KeyCombination() { @Override public boolean match(KeyEvent e) { return false; } }; /** The state of the {@code shift} key in this key combination. */ private final ModifierValue shift; /** * The state of the {@code shift} key in this key combination. * @return The state of the {@code shift} key in this key combination */ public final ModifierValue getShift() { return shift; } /** The state of the {@code control} key in this key combination. */ private final ModifierValue control; /** * The state of the {@code control} key in this key combination. * @return The state of the {@code control} key in this key combination */ public final ModifierValue getControl() { return control; } /** The state of the {@code alt} key in this key combination. */ private final ModifierValue alt; /** * The state of the {@code alt} key in this key combination. * @return The state of the {@code alt} key in this key combination. */ public final ModifierValue getAlt() { return alt; } /** The state of the {@code meta} key in this key combination. */ private final ModifierValue meta; /** * The state of the {@code meta} key in this key combination. * @return The state of the {@code meta} key in this key combination */ public final ModifierValue getMeta() { return meta; } /** The state of the {@code shortcut} key in this key combination. */ private final ModifierValue shortcut; /** * The state of the {@code shortcut} key in this key combination. * @return The state of the {@code shortcut} key in this key combination */ public final ModifierValue getShortcut() { return shortcut; } /** * Constructs a {@code KeyCombination} with an explicit specification * of all modifier keys. Each modifier key can be set to {@code DOWN}, * {@code UP} or {@code ANY}. * * @param shift the value of the {@code shift} modifier key * @param control the value of the {@code control} modifier key * @param alt the value of the {@code alt} modifier key * @param meta the value of the {@code meta} modifier key * @param shortcut the value of the {@code shortcut} modifier key */ protected KeyCombination(final ModifierValue shift, final ModifierValue control, final ModifierValue alt, final ModifierValue meta, final ModifierValue shortcut) { if ((shift == null) || (control == null) || (alt == null) || (meta == null) || (shortcut == null)) { throw new NullPointerException("Modifier value must not be null!"); } this.shift = shift; this.control = control; this.alt = alt; this.meta = meta; this.shortcut = shortcut; } /** * Constructs a {@code KeyCombination} with the specified list of modifiers. * All modifier keys which are not explicitly listed are set to the * default {@code UP} value. *
* All possible modifiers which change the default modifier value are * defined as constants in the {@code KeyCombination} class. * * @param modifiers the list of modifier keys and their corresponding values */ protected KeyCombination(final Modifier... modifiers) { this(getModifierValue(modifiers, KeyCode.SHIFT), getModifierValue(modifiers, KeyCode.CONTROL), getModifierValue(modifiers, KeyCode.ALT), getModifierValue(modifiers, KeyCode.META), getModifierValue(modifiers, KeyCode.SHORTCUT)); } /** * Tests whether this key combination matches the combination in the given * {@code KeyEvent}. *
* The implementation of this method in the {@code KeyCombination} class * does only a partial test with the modifier keys. This method is * overridden in subclasses to include the main key in the test. * * @param event the key event * @return {@code true} if the key combinations match, {@code false} * otherwise */ public boolean match(final KeyEvent event) { final KeyCode shortcutKey = Toolkit.getToolkit().getPlatformShortcutKey(); return test(KeyCode.SHIFT, shift, shortcutKey, shortcut, event.isShiftDown()) && test(KeyCode.CONTROL, control, shortcutKey, shortcut, event.isControlDown()) && test(KeyCode.ALT, alt, shortcutKey, shortcut, event.isAltDown()) && test(KeyCode.META, meta, shortcutKey, shortcut, event.isMetaDown()); } /** * Returns a string representation of this {@code KeyCombination}. *
* The string representation consists of sections separated by plus * characters. Each section specifies either a modifier key or the main key. *
* A modifier key section contains the {@code KeyCode} name of a modifier * key. It can be prefixed with the {@code Ignored} keyword. A non-prefixed * modifier key implies its {@code DOWN} value while the prefixed version * implies the {@code ANY} (ignored) value. If some modifier key is not * specified in the string at all, it means it has the default {@code UP} * value. *
* The format of the main key section of the key combination string depends * on the {@code KeyCombination} subclass. It is either the key code name * for {@code KeyCodeCombination} or the single quoted key character for * {@code KeyCharacterCombination}. *
* Examples of {@code KeyCombination} string representations:
"Ctrl+Alt+Q" "Ignore Shift+Ctrl+A" "Alt+'w'"* @return the string representation of this {@code KeyCombination} */ public String getName() { StringBuilder sb = new StringBuilder(); addModifiersIntoString(sb); return sb.toString(); } /** * Returns a string representation of this {@code KeyCombination} that is * suitable for display in a user interface (for example, beside a menu item). * * @return A string representation of this {@code KeyCombination}, suitable * for display in a user interface. * @since JavaFX 8u20 */ public String getDisplayText() { StringBuilder stringBuilder = new StringBuilder(); if (com.sun.javafx.PlatformUtil.isMac()) { // Macs have a different convention for keyboard accelerators - // no pluses to separate modifiers, and special symbols for // each modifier (in a particular order), etc if (getControl() == KeyCombination.ModifierValue.DOWN) { stringBuilder.append("\u2303"); } if (getAlt() == KeyCombination.ModifierValue.DOWN) { stringBuilder.append("\u2325"); } if (getShift() == KeyCombination.ModifierValue.DOWN) { stringBuilder.append("\u21e7"); } if (getMeta() == KeyCombination.ModifierValue.DOWN || getShortcut() == KeyCombination.ModifierValue.DOWN) { stringBuilder.append("\u2318"); } // TODO refer to RT-14486 for remaining glyphs } else { if (getControl() == KeyCombination.ModifierValue.DOWN || getShortcut() == KeyCombination.ModifierValue.DOWN ) { stringBuilder.append("Ctrl+"); } if (getAlt() == KeyCombination.ModifierValue.DOWN) { stringBuilder.append("Alt+"); } if (getShift() == KeyCombination.ModifierValue.DOWN) { stringBuilder.append("Shift+"); } if (getMeta() == KeyCombination.ModifierValue.DOWN) { stringBuilder.append("Meta+"); } } return stringBuilder.toString(); } /** * Tests whether this {@code KeyCombination} equals to the specified object. * * @param obj the object to compare to * @return {@code true} if the objects are equal, {@code false} otherwise */ @Override public boolean equals(final Object obj) { if (!(obj instanceof KeyCombination)) { return false; } final KeyCombination other = (KeyCombination) obj; return (shift == other.shift) && (control == other.control) && (alt == other.alt) && (meta == other.meta) && (shortcut == other.shortcut); } /** * Returns a hash code value for this {@code KeyCombination}. * * @return the hash code value */ @Override public int hashCode() { int hash = 7; hash = 23 * hash + shift.hashCode(); hash = 23 * hash + control.hashCode(); hash = 23 * hash + alt.hashCode(); hash = 23 * hash + meta.hashCode(); hash = 23 * hash + shortcut.hashCode(); return hash; } /** * Returns a string representation of this object. Implementation returns * the result of the {@code getName()} call. * * @return the string representation of this {@code KeyCombination} */ @Override public String toString() { return getName(); } /** * Constructs a new {@code KeyCombination} from the specified string. The * string should be in the same format as produced by the {@code getName} * method. *
* If the main key section string is quoted in single quotes the method
* creates a new {@code KeyCharacterCombination} for the unquoted substring.
* Otherwise it finds the key code which name corresponds to the main key
* section string and creates a {@code KeyCodeCombination} for it. If this
* can't be done, it falls back to the {@code KeyCharacterCombination}.
*
* @param value the string which represents the requested key combination
* @return the constructed {@code KeyCombination}
* @since JavaFX 2.1
*/
public static KeyCombination valueOf(String value) {
final List