Change build scripts
[jvcard.git] / src / com / googlecode / lanterna / input / KeyStroke.java
1 /*
2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
3 *
4 * lanterna is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Copyright (C) 2010-2015 Martin
18 */
19 package com.googlecode.lanterna.input;
20
21 import java.util.ArrayList;
22 import java.util.Arrays;
23
24 /**
25 * Represents the user pressing a key on the keyboard. If the user held down ctrl and/or alt before pressing the key,
26 * this may be recorded in this class, depending on the terminal implementation and if such information in available.
27 * KeyStroke objects are normally constructed by a KeyDecodingProfile, which works off a character stream that likely
28 * coming from the system's standard input. Because of this, the class can only represent what can be read and
29 * interpreted from the input stream; for example, certain key-combinations like ctrl+i is indistinguishable from a tab
30 * key press.
31 * <p>
32 * Use the <tt>keyType</tt> field to determine what kind of key was pressed. For ordinary letters, numbers and symbols, the
33 * <tt>keyType</tt> will be <tt>KeyType.Character</tt> and the actual character value of the key is in the
34 * <tt>character</tt> field. Please note that return (\n) and tab (\t) are not sorted under type <tt>KeyType.Character</tt>
35 * but <tt>KeyType.Enter</tt> and <tt>KeyType.Tab</tt> instead.
36 * @author martin
37 */
38 public class KeyStroke {
39 private final KeyType keyType;
40 private final Character character;
41 private final boolean ctrlDown;
42 private final boolean altDown;
43 private final boolean shiftDown;
44 private final long eventTime;
45
46 /**
47 * Constructs a KeyStroke based on a supplied keyType; character will be null and both ctrl and alt will be
48 * considered not pressed. If you try to construct a KeyStroke with type KeyType.Character with this constructor, it
49 * will always throw an exception; use another overload that allows you to specify the character value instead.
50 * @param keyType Type of the key pressed by this keystroke
51 */
52 public KeyStroke(KeyType keyType) {
53 this(keyType, false, false);
54 }
55
56 /**
57 * Constructs a KeyStroke based on a supplied keyType; character will be null.
58 * If you try to construct a KeyStroke with type KeyType.Character with this constructor, it
59 * will always throw an exception; use another overload that allows you to specify the character value instead.
60 * @param keyType Type of the key pressed by this keystroke
61 * @param ctrlDown Was ctrl held down when the main key was pressed?
62 * @param altDown Was alt held down when the main key was pressed?
63 */
64 public KeyStroke(KeyType keyType, boolean ctrlDown, boolean altDown) {
65 this(keyType, null, ctrlDown, altDown, false);
66 }
67
68 /**
69 * Constructs a KeyStroke based on a supplied keyType; character will be null.
70 * If you try to construct a KeyStroke with type KeyType.Character with this constructor, it
71 * will always throw an exception; use another overload that allows you to specify the character value instead.
72 * @param keyType Type of the key pressed by this keystroke
73 * @param ctrlDown Was ctrl held down when the main key was pressed?
74 * @param altDown Was alt held down when the main key was pressed?
75 * @param shiftDown Was shift held down when the main key was pressed?
76 */
77 public KeyStroke(KeyType keyType, boolean ctrlDown, boolean altDown, boolean shiftDown) {
78 this(keyType, null, ctrlDown, altDown, shiftDown);
79 }
80
81 /**
82 * Constructs a KeyStroke based on a supplied character, keyType is implicitly KeyType.Character.
83 * <p>
84 * A character-based KeyStroke does not support the shiftDown flag, as the shift state has
85 * already been accounted for in the character itself, depending on user's keyboard layout.
86 * @param character Character that was typed on the keyboard
87 * @param ctrlDown Was ctrl held down when the main key was pressed?
88 * @param altDown Was alt held down when the main key was pressed?
89 */
90 public KeyStroke(Character character, boolean ctrlDown, boolean altDown) {
91 this(KeyType.Character, character, ctrlDown, altDown, false);
92 }
93
94 private KeyStroke(KeyType keyType, Character character, boolean ctrlDown, boolean altDown, boolean shiftDown) {
95 if(keyType == KeyType.Character && character == null) {
96 throw new IllegalArgumentException("Cannot construct a KeyStroke with type KeyType.Character but no character information");
97 }
98 //Enforce character for some key types
99 switch(keyType) {
100 case Backspace:
101 character = '\b';
102 break;
103 case Enter:
104 character = '\n';
105 break;
106 case Tab:
107 character = '\t';
108 break;
109 default:
110 }
111 this.keyType = keyType;
112 this.character = character;
113 this.shiftDown = shiftDown;
114 this.ctrlDown = ctrlDown;
115 this.altDown = altDown;
116 this.eventTime = System.currentTimeMillis();
117 }
118
119 /**
120 * Type of key that was pressed on the keyboard, as represented by the KeyType enum. If the value if
121 * KeyType.Character, you need to call getCharacter() to find out which letter, number or symbol that was actually
122 * pressed.
123 * @return Type of key on the keyboard that was pressed
124 */
125 public KeyType getKeyType() {
126 return keyType;
127 }
128
129 /**
130 * For keystrokes of ordinary keys (letters, digits, symbols), this method returns the actual character value of the
131 * key. For all other key types, it returns null.
132 * @return Character value of the key pressed, or null if it was a special key
133 */
134 public Character getCharacter() {
135 return character;
136 }
137
138 /**
139 * @return Returns true if ctrl was help down while the key was typed (depending on terminal implementation)
140 */
141 public boolean isCtrlDown() {
142 return ctrlDown;
143 }
144
145 /**
146 * @return Returns true if alt was help down while the key was typed (depending on terminal implementation)
147 */
148 public boolean isAltDown() {
149 return altDown;
150 }
151
152 /**
153 * @return Returns true if shift was help down while the key was typed (depending on terminal implementation)
154 */
155 public boolean isShiftDown() {
156 return shiftDown;
157 }
158
159 /**
160 * Gets the time when the keystroke was recorded. This isn't necessarily the time the keystroke happened, but when
161 * Lanterna received the event, so it may not be accurate down to the millisecond.
162 * @return The unix time of when the keystroke happened, in milliseconds
163 */
164 public long getEventTime() {
165 return eventTime;
166 }
167
168 @Override
169 public String toString() {
170 return "KeyStroke{" + "keyType=" + keyType + ", character=" + character +
171 ", ctrlDown=" + ctrlDown +
172 ", altDown=" + altDown +
173 ", shiftDown=" + shiftDown + '}';
174 }
175
176 @Override
177 public int hashCode() {
178 int hash = 3;
179 hash = 41 * hash + (this.keyType != null ? this.keyType.hashCode() : 0);
180 hash = 41 * hash + (this.character != null ? this.character.hashCode() : 0);
181 hash = 41 * hash + (this.ctrlDown ? 1 : 0);
182 hash = 41 * hash + (this.altDown ? 1 : 0);
183 hash = 41 * hash + (this.shiftDown ? 1 : 0);
184 return hash;
185 }
186
187 @SuppressWarnings("SimplifiableIfStatement")
188 @Override
189 public boolean equals(Object obj) {
190 if (obj == null) {
191 return false;
192 }
193 if (getClass() != obj.getClass()) {
194 return false;
195 }
196 final KeyStroke other = (KeyStroke) obj;
197 if (this.keyType != other.keyType) {
198 return false;
199 }
200 if (this.character != other.character && (this.character == null || !this.character.equals(other.character))) {
201 return false;
202 }
203 return this.ctrlDown == other.ctrlDown &&
204 this.altDown == other.altDown &&
205 this.shiftDown == other.shiftDown;
206 }
207
208 /**
209 * Creates a Key from a string representation in Vim's key notation.
210 *
211 * @param keyStr the string representation of this key
212 * @return the created {@link KeyType}
213 */
214 public static KeyStroke fromString(String keyStr) {
215 String keyStrLC = keyStr.toLowerCase();
216 KeyStroke k;
217 if (keyStr.length() == 1) {
218 k = new KeyStroke(KeyType.Character, keyStr.charAt(0), false, false, false);
219 } else if (keyStr.startsWith("<") && keyStr.endsWith(">")) {
220 if (keyStrLC.equals("<s-tab>")) {
221 k = new KeyStroke(KeyType.ReverseTab);
222 } else if (keyStr.contains("-")) {
223 ArrayList<String> segments = new ArrayList<String>(Arrays.asList(keyStr.substring(1, keyStr.length() - 1).split("-")));
224 if (segments.size() < 2) {
225 throw new IllegalArgumentException("Invalid vim notation: " + keyStr);
226 }
227 String characterStr = segments.remove(segments.size() - 1);
228 boolean altPressed = false;
229 boolean ctrlPressed = false;
230 for (String modifier : segments) {
231 if ("c".equals(modifier.toLowerCase())) {
232 ctrlPressed = true;
233 } else if ("a".equals(modifier.toLowerCase())) {
234 altPressed = true;
235 } else if ("s".equals(modifier.toLowerCase())) {
236 characterStr = characterStr.toUpperCase();
237 }
238 }
239 k = new KeyStroke(characterStr.charAt(0), ctrlPressed, altPressed);
240 } else {
241 if (keyStrLC.startsWith("<esc")) {
242 k = new KeyStroke(KeyType.Escape);
243 } else if (keyStrLC.equals("<cr>") || keyStrLC.equals("<enter>") || keyStrLC.equals("<return>")) {
244 k = new KeyStroke(KeyType.Enter);
245 } else if (keyStrLC.equals("<bs>")) {
246 k = new KeyStroke(KeyType.Backspace);
247 } else if (keyStrLC.equals("<tab>")) {
248 k = new KeyStroke(KeyType.Tab);
249 } else if (keyStrLC.equals("<space>")) {
250 k = new KeyStroke(' ', false, false);
251 } else if (keyStrLC.equals("<up>")) {
252 k = new KeyStroke(KeyType.ArrowUp);
253 } else if (keyStrLC.equals("<down>")) {
254 k = new KeyStroke(KeyType.ArrowDown);
255 } else if (keyStrLC.equals("<left>")) {
256 k = new KeyStroke(KeyType.ArrowLeft);
257 } else if (keyStrLC.equals("<right>")) {
258 k = new KeyStroke(KeyType.ArrowRight);
259 } else if (keyStrLC.equals("<insert>")) {
260 k = new KeyStroke(KeyType.Insert);
261 } else if (keyStrLC.equals("<del>")) {
262 k = new KeyStroke(KeyType.Delete);
263 } else if (keyStrLC.equals("<home>")) {
264 k = new KeyStroke(KeyType.Home);
265 } else if (keyStrLC.equals("<end>")) {
266 k = new KeyStroke(KeyType.End);
267 } else if (keyStrLC.equals("<pageup>")) {
268 k = new KeyStroke(KeyType.PageUp);
269 } else if (keyStrLC.equals("<pagedown>")) {
270 k = new KeyStroke(KeyType.PageDown);
271 } else if (keyStrLC.equals("<f1>")) {
272 k = new KeyStroke(KeyType.F1);
273 } else if (keyStrLC.equals("<f2>")) {
274 k = new KeyStroke(KeyType.F2);
275 } else if (keyStrLC.equals("<f3>")) {
276 k = new KeyStroke(KeyType.F3);
277 } else if (keyStrLC.equals("<f4>")) {
278 k = new KeyStroke(KeyType.F4);
279 } else if (keyStrLC.equals("<f5>")) {
280 k = new KeyStroke(KeyType.F5);
281 } else if (keyStrLC.equals("<f6>")) {
282 k = new KeyStroke(KeyType.F6);
283 } else if (keyStrLC.equals("<f7>")) {
284 k = new KeyStroke(KeyType.F7);
285 } else if (keyStrLC.equals("<f8>")) {
286 k = new KeyStroke(KeyType.F8);
287 } else if (keyStrLC.equals("<f9>")) {
288 k = new KeyStroke(KeyType.F9);
289 } else if (keyStrLC.equals("<f10>")) {
290 k = new KeyStroke(KeyType.F10);
291 } else if (keyStrLC.equals("<f11>")) {
292 k = new KeyStroke(KeyType.F11);
293 } else if (keyStrLC.equals("<f12>")) {
294 k = new KeyStroke(KeyType.F12);
295 } else {
296 throw new IllegalArgumentException("Invalid vim notation: " + keyStr);
297 }
298 }
299 } else {
300 throw new IllegalArgumentException("Invalid vim notation: " + keyStr);
301 }
302 return k;
303 }
304 }