1 /* 2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene.input; 27 28 import javafx.beans.NamedArg; 29 30 31 // PENDING_DOC_REVIEW 32 /** 33 * This class represents a key combination in which the main key is specified 34 * by its {@code KeyCode}. Such key combination is independent on the keyboard 35 * functional layout configured by the user at the time of key combination 36 * matching. 37 * @since JavaFX 2.0 38 */ 39 public final class KeyCodeCombination extends KeyCombination { 40 /** The key code associated with this key combination. */ 41 private KeyCode code; 42 43 /** 44 * Gets the key code associated with this key combination. 45 * @return The key code associated with this key combination 46 */ 47 public final KeyCode getCode() { 48 return code; 49 } 50 51 /** 52 * Constructs a {@code KeyCodeCombination} for the specified main key and 53 * with an explicit specification of all modifier keys. Each modifier key 54 * can be set to {@code PRESSED}, {@code RELEASED} or {@code IGNORED}. 55 * 56 * @param code the key code of the main key 57 * @param shift the value of the {@code shift} modifier key 58 * @param control the value of the {@code control} modifier key 59 * @param alt the value of the {@code alt} modifier key 60 * @param meta the value of the {@code meta} modifier key 61 * @param shortcut the value of the {@code shortcut} modifier key 62 */ 63 public KeyCodeCombination(final @NamedArg("code") KeyCode code, 64 final @NamedArg("shift") ModifierValue shift, 65 final @NamedArg("control") ModifierValue control, 66 final @NamedArg("alt") ModifierValue alt, 67 final @NamedArg("meta") ModifierValue meta, 68 final @NamedArg("shortcut") ModifierValue shortcut) { 69 super(shift, control, alt, meta, shortcut); 70 71 validateKeyCode(code); 72 this.code = code; 73 } 74 75 /** 76 * Constructs a {@code KeyCodeCombination} for the specified main key and 77 * with the specified list of modifiers. All modifier keys which are not 78 * explicitly listed are set to the default {@code RELEASED} value. 79 * <p> 80 * All possible modifiers which change the default modifier value are 81 * defined as constants in the {@code KeyCombination} class. 82 * 83 * @param code the key code of the main key 84 * @param modifiers the list of modifier keys and their corresponding values 85 */ 86 public KeyCodeCombination(final @NamedArg("code") KeyCode code, 87 final @NamedArg("modifiers") Modifier... modifiers) { 88 super(modifiers); 89 90 validateKeyCode(code); 91 this.code = code; 92 } 93 94 /** 95 * Tests whether this key combination matches the key combination in the 96 * given {@code KeyEvent}. It uses only the key code and the state of the 97 * modifier keys from the {@code KeyEvent} in the test. This means that the 98 * method can return {@code true} only for {@code KEY_PRESSED} and 99 * {@code KEY_RELEASED} events, but not for {@code KEY_TYPED} events, which 100 * don't have valid key codes. 101 * 102 * @param event the key event 103 * @return {@code true} if the key combinations match, {@code false} 104 * otherwise 105 */ 106 @Override 107 public boolean match(final KeyEvent event) { 108 return (event.getCode() == getCode()) && super.match(event); 109 } 110 111 /** 112 * Returns a string representation of this {@code KeyCodeCombination}. 113 * <p> 114 * The string representation consists of sections separated by plus 115 * characters. Each section specifies either a modifier key or the main key. 116 * <p> 117 * A modifier key section contains the {@code KeyCode} name of a modifier 118 * key. It can be prefixed with the {@code Ignored} keyword. A non-prefixed 119 * modifier key implies its {@code PRESSED} value while the prefixed version 120 * implies the {@code IGNORED} value. If some modifier key is not specified 121 * in the string at all, it means it has the default {@code RELEASED} value. 122 * <p> 123 * The main key section contains the key code name of the main key and is 124 * the last section in the returned string. 125 * 126 * @return the string representation of this {@code KeyCodeCombination} 127 */ 128 @Override 129 public String getName() { 130 StringBuilder sb = new StringBuilder(); 131 132 sb.append(super.getName()); 133 134 if (sb.length() > 0) { 135 sb.append("+"); 136 } 137 138 return sb.append(code.getName()).toString(); 139 } 140 141 /** {@inheritDoc} */ 142 @Override 143 public String getDisplayText() { 144 StringBuilder sb = new StringBuilder(); 145 sb.append(super.getDisplayText()); 146 final int initialLength = sb.length(); 147 148 char c = getSingleChar(code); 149 if (c != 0) { 150 sb.append(c); 151 return sb.toString(); 152 } 153 154 // Compute a name based on the enum name, e.g. F13 becomes F13 and 155 // NUM_LOCK becomes Num Lock 156 String name = code.toString(); 157 158 // We only want the first letter to be upper-case, so we convert 'ENTER' 159 // to 'Enter' -- and we also convert multiple words separated by _ 160 // such that each individual word is capitalized and the underline replaced 161 // by spaces. 162 163 String[] words = com.sun.javafx.util.Utils.split(name, "_"); 164 for (String word : words) { 165 if (sb.length() > initialLength) { 166 sb.append(' '); 167 } 168 sb.append(word.charAt(0)); 169 sb.append(word.substring(1).toLowerCase()); 170 } 171 return sb.toString(); 172 } 173 174 /** 175 * Tests whether this {@code KeyCodeCombination} equals to the specified 176 * object. 177 * 178 * @param obj the object to compare to 179 * @return {@code true} if the objects are equal, {@code false} otherwise 180 */ 181 @Override 182 public boolean equals(final Object obj) { 183 if (this == obj) { 184 return true; 185 } 186 187 if (!(obj instanceof KeyCodeCombination)) { 188 return false; 189 } 190 191 return (this.getCode() == ((KeyCodeCombination) obj).getCode()) 192 && super.equals(obj); 193 } 194 195 /** 196 * Returns a hash code value for this {@code KeyCodeCombination}. 197 * 198 * @return the hash code value 199 */ 200 @Override 201 public int hashCode() { 202 return 23 * super.hashCode() + code.hashCode(); 203 } 204 205 private static void validateKeyCode(final KeyCode keyCode) { 206 if (keyCode == null) { 207 throw new NullPointerException("Key code must not be null!"); 208 } 209 210 if (getModifier(keyCode.getName()) != null) { 211 throw new IllegalArgumentException( 212 "Key code must not match modifier key!"); 213 } 214 215 if (keyCode == KeyCode.UNDEFINED) { 216 throw new IllegalArgumentException( 217 "Key code must differ from undefined value!"); 218 } 219 } 220 221 /** Compute a single suitable char summarizing the code, if any, and 0 otherwise. */ 222 private static char getSingleChar(KeyCode code) { 223 switch (code) { 224 case ENTER: return '\u21B5'; 225 case LEFT: return '\u2190'; 226 case UP: return '\u2191'; 227 case RIGHT: return '\u2192'; 228 case DOWN: return '\u2193'; 229 case COMMA: return ','; 230 case MINUS: return '-'; 231 case PERIOD: return '.'; 232 case SLASH: return '/'; 233 case SEMICOLON: return ';'; 234 case EQUALS: return '='; 235 case OPEN_BRACKET: return '['; 236 case BACK_SLASH: return '\\'; 237 case CLOSE_BRACKET: return ']'; 238 case MULTIPLY: return '*'; 239 case ADD: return '+'; 240 case SUBTRACT: return '-'; 241 case DECIMAL: return '.'; 242 case DIVIDE: return '/'; 243 case BACK_QUOTE: return '`'; 244 case QUOTE: return '"'; 245 case AMPERSAND: return '&'; 246 case ASTERISK: return '*'; 247 case LESS: return '<'; 248 case GREATER: return '>'; 249 case BRACELEFT: return '{'; 250 case BRACERIGHT: return '}'; 251 case AT: return '@'; 252 case COLON: return ':'; 253 case CIRCUMFLEX: return '^'; 254 case DOLLAR: return '$'; 255 case EURO_SIGN: return '\u20AC'; 256 case EXCLAMATION_MARK: return '!'; 257 case LEFT_PARENTHESIS: return '('; 258 case NUMBER_SIGN: return '#'; 259 case PLUS: return '+'; 260 case RIGHT_PARENTHESIS: return ')'; 261 case UNDERSCORE: return '_'; 262 case DIGIT0: return '0'; 263 case DIGIT1: return '1'; 264 case DIGIT2: return '2'; 265 case DIGIT3: return '3'; 266 case DIGIT4: return '4'; 267 case DIGIT5: return '5'; 268 case DIGIT6: return '6'; 269 case DIGIT7: return '7'; 270 case DIGIT8: return '8'; 271 case DIGIT9: return '9'; 272 default: 273 break; 274 } 275 276 /* 277 ** On Mac we display these unicode symbols, 278 ** otherwise we default to the Text version of the char. 279 */ 280 if (com.sun.javafx.PlatformUtil.isMac()) { 281 switch (code) { 282 case BACK_SPACE: return '\u232B'; 283 case ESCAPE: return '\u238B'; 284 case DELETE: return '\u2326'; 285 default: 286 break; 287 } 288 } 289 return 0; 290 } 291 }