001 /*
002 * $URL: file:///mit/6.170/groups/se64/repository/trunk/gizmoball/SwingKeyboardInput.java $
003 * $Id: SwingKeyboardInput.java 574 2007-05-14 09:48:02Z billmag $
004 */
005
006 package gizmoball;
007
008 import gizmoball.staffui.MagicKeyListener;
009
010 import java.awt.event.KeyEvent;
011 import java.awt.event.KeyListener;
012 import java.util.Collections;
013 import java.util.HashMap;
014 import java.util.HashSet;
015 import java.util.Map;
016 import java.util.Set;
017
018
019 /**
020 * A <code>KeyboardInput</code> implementation backed by keyboard events
021 * generated by AWT.
022 * <p>
023 * Note that this class by itself does not connect its
024 * <code>KeyListener</code> to any AWT components. Instead, clients of
025 * this class should take the <code>KeyListener</code> returned by
026 * {@link #getKeyListener} and pass it to {@link
027 * java.awt.Component#addKeyListener} for all the compenents whose
028 * keyboard events they want this class to capture.
029 *
030 * @specfield keyboardTriggerGenerators :
031 * map from pairs of integers and keyboard event types to
032 * <code>KeyboardTriggerGenerator</code>s -
033 * inherited from KeyboardInput; a mapping that maps each keyboard
034 * event, specified by an ASCII character code and whether that
035 * event is a key up or key down event, to the
036 * <code>KeyboardTriggerGenerator</code> that is responsible for
037 * triggering on that event
038 */
039 public final class SwingKeyboardInput implements KeyboardInput {
040
041 // Abstraction Function:
042 // keyboardTriggerGenerators =
043 // map of (a, e) => k where
044 // a : an ASCII character code
045 // e : a key event type
046 // k : keyDownTriggerGenerators[vk(a)],
047 // if e is a key down event
048 // keyUpTriggerGenerators[a],
049 // if e is a key up event
050 // vk(a) : virtualKeyForAscii[a]
051 // except betware that the k are lazily created as needed
052
053 // Representation Invariant:
054 // keyListener != null
055 // keyDownTriggerGenerators != null
056 // keyUpTriggerGenerators != null
057 // allKeyTriggerGenerators != null
058 // The union of the sets of KeyboardTriggerGenerators in
059 // keyDownTriggerGenerators and keyUpTriggerGenerators is the
060 // same as the set allKeyTriggerGenerators.
061
062
063 /*------------------------------------------------------------------
064 * Fields
065 *----------------------------------------------------------------*/
066
067 // The KeyListener through which this SwingKeyboardInput receives
068 // its notifications about keyboard events
069 private final KeyListener keyListener;
070
071 // A mapping from KeyEvent virtual key codes to
072 // KeyboardTriggerGenerators that trigger on the key down event of
073 // that virtual key code.
074 private final Map<Integer, KeyboardTriggerGenerator>
075 keyDownTriggerGenerators;
076
077 // A mapping from KeyEvent virtual key codes to
078 // KeyboardTriggerGenerators that trigger on the key up event of
079 // that virtual key code.
080 private final Map<Integer, KeyboardTriggerGenerator>
081 keyUpTriggerGenerators;
082
083 // All the KeyboardTriggerGenerators that this KeyboardInput is
084 // managing
085 private final Set<KeyboardTriggerGenerator> allKeyTriggerGenerators;
086
087 // The mapping from ASCII character codes to KeyEvent.VK_* virtual
088 // key codes that they correspond to. To find the appropriate
089 // virtual key code for ASCII character code a, use
090 // virtualKeyForAscii[a]. Note that ASCII character codes that
091 // aren't mapped to any actual key are mapped to VK_UNDEFINED.
092 private static final int[] virtualKeyForAscii = {
093 KeyEvent.VK_UNDEFINED, // 0: null character
094 KeyEvent.VK_UNDEFINED, // 1: start of header
095 KeyEvent.VK_UNDEFINED, // 2: start of text
096 KeyEvent.VK_UNDEFINED, // 3: end of text
097 KeyEvent.VK_UNDEFINED, // 4: end of transmission
098 KeyEvent.VK_UNDEFINED, // 5: enquiry
099 KeyEvent.VK_UNDEFINED, // 6: acknowledgment
100 KeyEvent.VK_UNDEFINED, // 7: bell
101 KeyEvent.VK_BACK_SPACE, // 8: backspace
102 KeyEvent.VK_TAB, // 9: horizontal tab
103 // XXX: Tab doesn't work because it's used to change the
104 // control that has keyboard focus...
105 KeyEvent.VK_ENTER, // 10: line feed
106 KeyEvent.VK_UNDEFINED, // 11: vertical tab
107 KeyEvent.VK_UNDEFINED, // 12: form feed
108 KeyEvent.VK_ENTER, // 13: carriage return
109 KeyEvent.VK_UNDEFINED, // 14: shift out
110 KeyEvent.VK_UNDEFINED, // 15: shift in
111 KeyEvent.VK_UNDEFINED, // 16: data link escape
112 KeyEvent.VK_UNDEFINED, // 17: device control 1
113 KeyEvent.VK_UNDEFINED, // 18: device control 2
114 KeyEvent.VK_UNDEFINED, // 19: device control 3
115 KeyEvent.VK_UNDEFINED, // 20: device control 4
116 KeyEvent.VK_UNDEFINED, // 21: negative acknowledgment
117 KeyEvent.VK_UNDEFINED, // 22: synchronous idle
118 KeyEvent.VK_UNDEFINED, // 23: end of transmission block
119 KeyEvent.VK_UNDEFINED, // 24: cancel
120 KeyEvent.VK_UNDEFINED, // 25: end of medium
121 KeyEvent.VK_UNDEFINED, // 26: substitute
122 KeyEvent.VK_ESCAPE, // 27: escape
123 KeyEvent.VK_UNDEFINED, // 28: file separator
124 KeyEvent.VK_UNDEFINED, // 29: group separator
125 KeyEvent.VK_UNDEFINED, // 30: record separator
126 KeyEvent.VK_UNDEFINED, // 31: unit separator
127 KeyEvent.VK_SPACE, // 32: space
128 KeyEvent.VK_1, // 33: !
129 KeyEvent.VK_QUOTE, // 34: "
130 KeyEvent.VK_2, // 35: #
131 KeyEvent.VK_4, // 36: $
132 KeyEvent.VK_5, // 37: %
133 KeyEvent.VK_7, // 38: &
134 KeyEvent.VK_QUOTE, // 39: '
135 KeyEvent.VK_9, // 40: (
136 KeyEvent.VK_0, // 41: )
137 KeyEvent.VK_8, // 42: *
138 KeyEvent.VK_PLUS, // 43: +
139 KeyEvent.VK_COMMA, // 44: ,
140 KeyEvent.VK_MINUS, // 45: -
141 KeyEvent.VK_PERIOD, // 46: .
142 KeyEvent.VK_SLASH, // 47: /
143 KeyEvent.VK_0, // 48: 0
144 KeyEvent.VK_1, // 49: 1
145 KeyEvent.VK_2, // 50: 2
146 KeyEvent.VK_3, // 51: 3
147 KeyEvent.VK_4, // 52: 4
148 KeyEvent.VK_5, // 53: 5
149 KeyEvent.VK_6, // 54: 6
150 KeyEvent.VK_7, // 55: 7
151 KeyEvent.VK_8, // 56: 8
152 KeyEvent.VK_9, // 57: 9
153 KeyEvent.VK_SEMICOLON, // 58: :
154 KeyEvent.VK_SEMICOLON, // 59: ;
155 KeyEvent.VK_COMMA, // 60: <
156 KeyEvent.VK_EQUALS, // 61: =
157 KeyEvent.VK_PERIOD, // 62: >
158 KeyEvent.VK_SLASH, // 63: ?
159 KeyEvent.VK_2, // 64: @
160 KeyEvent.VK_A, // 65: A
161 KeyEvent.VK_B, // 66: B
162 KeyEvent.VK_C, // 67: C
163 KeyEvent.VK_D, // 68: D
164 KeyEvent.VK_E, // 69: E
165 KeyEvent.VK_F, // 70: F
166 KeyEvent.VK_G, // 71: G
167 KeyEvent.VK_H, // 72: H
168 KeyEvent.VK_I, // 73: I
169 KeyEvent.VK_J, // 74: J
170 KeyEvent.VK_K, // 75: K
171 KeyEvent.VK_L, // 76: L
172 KeyEvent.VK_M, // 77: M
173 KeyEvent.VK_N, // 78: N
174 KeyEvent.VK_O, // 79: O
175 KeyEvent.VK_P, // 80: P
176 KeyEvent.VK_Q, // 81: Q
177 KeyEvent.VK_R, // 82: R
178 KeyEvent.VK_S, // 83: S
179 KeyEvent.VK_T, // 84: T
180 KeyEvent.VK_U, // 85: U
181 KeyEvent.VK_V, // 86: V
182 KeyEvent.VK_W, // 87: W
183 KeyEvent.VK_X, // 88: X
184 KeyEvent.VK_Y, // 89: Y
185 KeyEvent.VK_Z, // 90: Z
186 KeyEvent.VK_OPEN_BRACKET, // 91: [
187 KeyEvent.VK_BACK_SLASH, // 92: \
188 KeyEvent.VK_CLOSE_BRACKET, // 93: ]
189 KeyEvent.VK_6, // 94: ^
190 KeyEvent.VK_MINUS, // 95: _
191 KeyEvent.VK_BACK_QUOTE, // 96: `
192 KeyEvent.VK_A, // 97: a
193 KeyEvent.VK_B, // 98: b
194 KeyEvent.VK_C, // 99: c
195 KeyEvent.VK_D, // 100: d
196 KeyEvent.VK_E, // 101: e
197 KeyEvent.VK_F, // 102: f
198 KeyEvent.VK_G, // 103: g
199 KeyEvent.VK_H, // 104: h
200 KeyEvent.VK_I, // 105: i
201 KeyEvent.VK_J, // 106: j
202 KeyEvent.VK_K, // 107: k
203 KeyEvent.VK_L, // 108: l
204 KeyEvent.VK_M, // 109: m
205 KeyEvent.VK_N, // 110: n
206 KeyEvent.VK_O, // 111: o
207 KeyEvent.VK_P, // 112: p
208 KeyEvent.VK_Q, // 113: q
209 KeyEvent.VK_R, // 114: r
210 KeyEvent.VK_S, // 115: s
211 KeyEvent.VK_T, // 116: t
212 KeyEvent.VK_U, // 117: u
213 KeyEvent.VK_V, // 118: v
214 KeyEvent.VK_W, // 119: w
215 KeyEvent.VK_X, // 120: x
216 KeyEvent.VK_Y, // 121: y
217 KeyEvent.VK_Z, // 122: z
218 KeyEvent.VK_OPEN_BRACKET, // 123: {
219 KeyEvent.VK_BACK_SLASH, // 124: |
220 KeyEvent.VK_CLOSE_BRACKET, // 125: }
221 KeyEvent.VK_BACK_QUOTE, // 126: ~
222 KeyEvent.VK_DELETE, // 127: delete
223 };
224
225 // The mapping from KeyEvent.VK_* virtual key codes to the canonical
226 // ASCII character code that is associated with that key
227 private static final Map<Integer, Integer> asciiForVirtualKey;
228 static {
229 asciiForVirtualKey = new HashMap<Integer, Integer>();
230 asciiForVirtualKey.put(KeyEvent.VK_UNDEFINED, 0);
231 asciiForVirtualKey.put(KeyEvent.VK_BACK_SPACE, (int)'\b');
232 asciiForVirtualKey.put(KeyEvent.VK_TAB, (int)'\t');
233 asciiForVirtualKey.put(KeyEvent.VK_ENTER, (int)'\n');
234 asciiForVirtualKey.put(KeyEvent.VK_ESCAPE, 27);
235 asciiForVirtualKey.put(KeyEvent.VK_SPACE, (int)' ');
236 asciiForVirtualKey.put(KeyEvent.VK_QUOTE, (int)'\'');
237 asciiForVirtualKey.put(KeyEvent.VK_PLUS, (int)'+');
238 asciiForVirtualKey.put(KeyEvent.VK_COMMA, (int)',');
239 asciiForVirtualKey.put(KeyEvent.VK_MINUS, (int)'-');
240 asciiForVirtualKey.put(KeyEvent.VK_PERIOD, (int)'.');
241 asciiForVirtualKey.put(KeyEvent.VK_SLASH, (int)'/');
242 asciiForVirtualKey.put(KeyEvent.VK_0, (int)'0');
243 asciiForVirtualKey.put(KeyEvent.VK_1, (int)'1');
244 asciiForVirtualKey.put(KeyEvent.VK_2, (int)'2');
245 asciiForVirtualKey.put(KeyEvent.VK_3, (int)'3');
246 asciiForVirtualKey.put(KeyEvent.VK_4, (int)'4');
247 asciiForVirtualKey.put(KeyEvent.VK_5, (int)'5');
248 asciiForVirtualKey.put(KeyEvent.VK_6, (int)'6');
249 asciiForVirtualKey.put(KeyEvent.VK_7, (int)'7');
250 asciiForVirtualKey.put(KeyEvent.VK_8, (int)'8');
251 asciiForVirtualKey.put(KeyEvent.VK_9, (int)'9');
252 asciiForVirtualKey.put(KeyEvent.VK_SEMICOLON, (int)';');
253 asciiForVirtualKey.put(KeyEvent.VK_EQUALS, (int)'=');
254 asciiForVirtualKey.put(KeyEvent.VK_A, (int)'A');
255 asciiForVirtualKey.put(KeyEvent.VK_B, (int)'B');
256 asciiForVirtualKey.put(KeyEvent.VK_C, (int)'C');
257 asciiForVirtualKey.put(KeyEvent.VK_D, (int)'D');
258 asciiForVirtualKey.put(KeyEvent.VK_E, (int)'E');
259 asciiForVirtualKey.put(KeyEvent.VK_F, (int)'F');
260 asciiForVirtualKey.put(KeyEvent.VK_G, (int)'G');
261 asciiForVirtualKey.put(KeyEvent.VK_H, (int)'H');
262 asciiForVirtualKey.put(KeyEvent.VK_I, (int)'I');
263 asciiForVirtualKey.put(KeyEvent.VK_J, (int)'J');
264 asciiForVirtualKey.put(KeyEvent.VK_K, (int)'K');
265 asciiForVirtualKey.put(KeyEvent.VK_L, (int)'L');
266 asciiForVirtualKey.put(KeyEvent.VK_M, (int)'M');
267 asciiForVirtualKey.put(KeyEvent.VK_N, (int)'N');
268 asciiForVirtualKey.put(KeyEvent.VK_O, (int)'O');
269 asciiForVirtualKey.put(KeyEvent.VK_P, (int)'P');
270 asciiForVirtualKey.put(KeyEvent.VK_Q, (int)'Q');
271 asciiForVirtualKey.put(KeyEvent.VK_R, (int)'R');
272 asciiForVirtualKey.put(KeyEvent.VK_S, (int)'S');
273 asciiForVirtualKey.put(KeyEvent.VK_T, (int)'T');
274 asciiForVirtualKey.put(KeyEvent.VK_U, (int)'U');
275 asciiForVirtualKey.put(KeyEvent.VK_V, (int)'V');
276 asciiForVirtualKey.put(KeyEvent.VK_W, (int)'W');
277 asciiForVirtualKey.put(KeyEvent.VK_X, (int)'X');
278 asciiForVirtualKey.put(KeyEvent.VK_Y, (int)'Y');
279 asciiForVirtualKey.put(KeyEvent.VK_Z, (int)'Z');
280 asciiForVirtualKey.put(KeyEvent.VK_OPEN_BRACKET, (int)'[');
281 asciiForVirtualKey.put(KeyEvent.VK_BACK_SLASH, (int)'\\');
282 asciiForVirtualKey.put(KeyEvent.VK_CLOSE_BRACKET, (int)']');
283 asciiForVirtualKey.put(KeyEvent.VK_BACK_QUOTE, (int)'`');
284 asciiForVirtualKey.put(KeyEvent.VK_DELETE, 127);
285 }
286
287 // Debugging output level. Follows the semantics defined by
288 // DebugUtils.printMessage.
289 private static final int debug = 0;
290
291 // Whether to run expensive checkRep() tests.
292 private static final boolean runExpensiveCheckRepTests = false;
293
294 /**
295 * Class revision identifier. <code>rcsid</code> contains an RCS Id
296 * keyword string that may be used to identify which revision of
297 * <code>SwingKeyboardInput.java</code> was used to generate an
298 * instance of <code>SwingKeyboardInput.class</code>. To identify
299 * the revision of a <code>SwingKeyboardInput.class</code> file, run
300 * "<code>ident SwingKeyboardInput.class</code>" (<code>ident</code>
301 * is part of the <a
302 * href="http://www.cs.purdue.edu/homes/trinkle/RCS/">RCS</a>
303 * software package).
304 */
305 public static final String rcsid = "$Id: SwingKeyboardInput.java 574 2007-05-14 09:48:02Z billmag $";
306
307
308 /*------------------------------------------------------------------
309 * Constructors
310 *----------------------------------------------------------------*/
311
312 /**
313 * Constructs a new <code>SwingKeyboardInput</code> object.
314 */
315 public SwingKeyboardInput() {
316 keyListener = new MagicKeyListener(new MyKeyListener());
317 keyDownTriggerGenerators =
318 new HashMap<Integer, KeyboardTriggerGenerator>();
319 keyUpTriggerGenerators =
320 new HashMap<Integer, KeyboardTriggerGenerator>();
321 allKeyTriggerGenerators = new HashSet<KeyboardTriggerGenerator>();
322
323 assert checkRep();
324 }
325
326
327 /*------------------------------------------------------------------
328 * checkRep()
329 *----------------------------------------------------------------*/
330
331 // Checks that the representation invariant has not been violated.
332 // Returns true if the representation invariant holds. Throws a
333 // RuntimeException if the representation invariant does not hold.
334 private boolean checkRep() {
335 if (keyListener == null) {
336 throw new RuntimeException("keyListener was null");
337 } else if (keyDownTriggerGenerators == null) {
338 throw new RuntimeException("keyDownTriggerGenerators was null");
339 } else if (keyUpTriggerGenerators == null) {
340 throw new RuntimeException("keyUpTriggerGenerators was null");
341 } else if (allKeyTriggerGenerators == null) {
342 throw new RuntimeException("allKeyTriggerGenerators was null");
343 }
344
345 if (runExpensiveCheckRepTests) {
346 if (!allKeyTriggerGenerators.containsAll(
347 keyDownTriggerGenerators.values())) {
348 throw new RuntimeException(
349 "keyDownTriggerGenerators contains " +
350 "KeyboardTriggerGenerators not in " +
351 "allKeyTriggerGenerators");
352 } else if (!allKeyTriggerGenerators.containsAll(
353 keyDownTriggerGenerators.values())) {
354 throw new RuntimeException(
355 "keyUpTriggerGenerators contains " +
356 "KeyboardTriggerGenerators not in " +
357 "allKeyTriggerGenerators");
358 }
359
360 for (KeyboardTriggerGenerator k : allKeyTriggerGenerators) {
361 if (!(keyDownTriggerGenerators.containsValue(k) ||
362 keyUpTriggerGenerators.containsValue(k))) {
363 throw new RuntimeException(k + " not found in either map");
364 }
365 }
366 }
367
368 return true;
369 }
370
371
372 /*------------------------------------------------------------------
373 * Methods inherited from KeyboardInput
374 *----------------------------------------------------------------*/
375
376 /**
377 * {@inheritDoc}
378 */
379 public KeyboardTriggerGenerator getKeyboardTriggerGenerator(
380 int asciiCode, EventType eventType) {
381 assert checkRep();
382
383 if (asciiCode < 0 || asciiCode > 127) {
384 throw new IllegalArgumentException(asciiCode +
385 " passed as argument asciiCode is out of range (0-127)");
386 } else if (eventType == null) {
387 throw new NullPointerException(
388 "null passed as argument eventType");
389 }
390
391 Map<Integer, KeyboardTriggerGenerator> ktgMapOfInterest;
392 if (eventType == EventType.KEY_DOWN) {
393 ktgMapOfInterest = keyDownTriggerGenerators;
394 } else {
395 assert eventType == EventType.KEY_UP;
396 ktgMapOfInterest = keyUpTriggerGenerators;
397 }
398
399 int virtualKeyCode = virtualKeyForAscii[asciiCode];
400
401 KeyboardTriggerGenerator ktg = ktgMapOfInterest.get(virtualKeyCode);
402 if (ktg == null) {
403 // This KTG not used before; need to create a new one
404 ktg = new KeyboardTriggerGenerator(
405 asciiForVirtualKey.get(virtualKeyCode), eventType);
406 ktgMapOfInterest.put(virtualKeyCode, ktg);
407 allKeyTriggerGenerators.add(ktg);
408 }
409
410 assert checkRep();
411 return ktg;
412 }
413
414 /**
415 * {@inheritDoc}
416 */
417 public Set<KeyboardTriggerGenerator> getAllKeyboardTriggerGenerators() {
418 assert checkRep();
419
420 Set<KeyboardTriggerGenerator> returnSet =
421 Collections.unmodifiableSet(allKeyTriggerGenerators);
422
423 assert checkRep();
424 return returnSet;
425 }
426
427
428 /*------------------------------------------------------------------
429 * Methods specific to SwingKeyboardInput
430 *----------------------------------------------------------------*/
431
432 /**
433 * Returns the <code>KeyListener</code> through which this
434 * <code>SwingKeyboardInput</code> expects to receive keyboard
435 * events. Clients of this class should pass the returned
436 * <code>KeyListener</code> to the {@link
437 * java.awt.Component#addKeyListener} method of any AWT component
438 * whose key events this <code>SwingKeyboardInput</code> should
439 * receive.
440 *
441 * @return the <code>KeyListener</code> through which this
442 * <code>SwingKeyboardInput</code> expects to receive
443 * keyboard events.
444 */
445 public KeyListener getKeyListener() {
446 assert checkRep();
447 return keyListener;
448 }
449
450
451 /*------------------------------------------------------------------
452 * private class MyKeyListener
453 *----------------------------------------------------------------*/
454
455 // A KeyListener that receives keyboard events and dispatches them
456 // to the propoer KeyboardTriggerGenerator.
457 private class MyKeyListener implements KeyListener {
458
459 // Activates the KeyboardTriggerGenerator associated with the
460 // KeyEvent e, if one exists. Called on key down events.
461 public void keyPressed(KeyEvent e) {
462 if (debug >= 2) {
463 DebugUtils.printMessage(this, 2, "Pressed event registered " +
464 "with key code + " + e.getKeyCode());
465 }
466
467 KeyboardTriggerGenerator ktg =
468 keyDownTriggerGenerators.get(e.getKeyCode());
469
470 if (ktg != null) {
471 ktg.activate();
472 }
473 }
474
475 // Activates the KeyboardTriggerGenerator associated with the
476 // KeyEvent e, if one exists. Called on key up events.
477 public void keyReleased(KeyEvent e) {
478 if (debug >= 2) {
479 DebugUtils.printMessage(this, 2, "Released event registered " +
480 "with key code + " + e.getKeyCode());
481 }
482
483 KeyboardTriggerGenerator ktg =
484 keyUpTriggerGenerators.get(e.getKeyCode());
485
486 if (ktg != null) {
487 ktg.activate();
488 }
489 }
490
491 // Does nothing, since we're interested in key down and key up
492 // events only, not key typed events
493 public void keyTyped(KeyEvent e) {
494 // Do nothing
495 }
496 }
497 }
498
499
500 /*
501 Indentation spec for emacs:
502 Local Variables:
503 c-basic-offset:4
504 End:
505 */