source: trunk/src/org/expeditee/gio/swing/SwingInputManager.java@ 1097

Last change on this file since 1097 was 1097, checked in by davidb, 6 years ago

Newly structured files from Corey's work on logic/graphics separation

File size: 14.3 KB
Line 
1package org.expeditee.gio.swing;
2
3import java.awt.AWTException;
4import java.awt.Color;
5import java.awt.MouseInfo;
6import java.awt.Robot;
7import java.awt.event.ActionEvent;
8import java.awt.event.ActionListener;
9import java.awt.event.ComponentEvent;
10import java.awt.event.ComponentListener;
11import java.awt.event.KeyEvent;
12import java.awt.event.KeyListener;
13import java.awt.event.MouseEvent;
14import java.awt.event.MouseListener;
15import java.awt.event.MouseMotionListener;
16import java.awt.event.MouseWheelEvent;
17import java.awt.event.MouseWheelListener;
18import java.awt.event.WindowEvent;
19import java.awt.event.WindowListener;
20import java.awt.event.WindowStateListener;
21
22import javax.swing.SwingUtilities;
23import javax.swing.Timer;
24
25import org.expeditee.core.Line;
26import org.expeditee.core.Point;
27import org.expeditee.gio.EcosystemManager;
28import org.expeditee.gio.InputManager;
29import org.expeditee.gio.input.InputEvent;
30import org.expeditee.gio.input.InputEvent.InputType;
31import org.expeditee.gio.input.KBMInputEvent;
32import org.expeditee.gui.Popup;
33import org.expeditee.gui.PopupManager;
34import org.expeditee.items.widgets.Widget;
35
36public class SwingInputManager extends InputManager
37implements ComponentListener,
38 WindowListener,
39 WindowStateListener,
40 KeyListener,
41 MouseListener,
42 MouseMotionListener,
43 MouseWheelListener
44{
45 /** Singleton instance. */
46 private static SwingInputManager _instance;
47
48 /** Singleton constructor. */
49 public static SwingInputManager getInstance()
50 {
51 if (_instance == null) {
52 try {
53 SwingUtilities.invokeAndWait(new Runnable() {
54 @Override
55 public void run() {
56 _instance = new SwingInputManager();
57 }
58 });
59 } catch (Exception e) {
60 System.err.println("Error while initialising InputManager. Aborting...");
61 System.exit(1);
62 }
63 }
64
65 return _instance;
66 }
67
68 /** TODO: What functionality does this object provide? */
69 private MouseEventRouter _mouseEventRouter;
70
71 /** For robotically controlling the mouse position. */
72 private Robot _robot;
73
74 private SwingInputManager()
75 {
76 SwingInputManager swingInputManager = this;
77
78 // Sign up to receive input as events
79 SwingGraphicsManager graphicsManager = SwingMiscManager.getIfUsingSwingGraphicsManager();
80 if (graphicsManager != null) {
81 graphicsManager.addWindowListener(swingInputManager);
82 graphicsManager.addWindowStateListener(swingInputManager);
83 graphicsManager.getContentPane().addKeyListener(swingInputManager); // TODO: Why do we need...
84 graphicsManager.addKeyListener(swingInputManager); // TODO: ...both of these? cts16
85 graphicsManager.addComponentListener(swingInputManager);
86
87 // Sign up to receive mouse events, from the SwingGraphicsManager
88 // TODO: Decide if we will keep using MouseEventRouter. cts16
89 _mouseEventRouter = new MouseEventRouter(graphicsManager.getJMenuBar(), graphicsManager.getContentPane());
90 _mouseEventRouter.addExpediteeMouseListener(swingInputManager);
91 _mouseEventRouter.addExpediteeMouseMotionListener(swingInputManager);
92 _mouseEventRouter.addExpediteeMouseWheelListener(swingInputManager);
93
94 // enable the glasspane-for capturing all mouse events
95 graphicsManager.setGlassPane(_mouseEventRouter);
96 graphicsManager.getGlassPane().setVisible(true);
97 graphicsManager.getContentPane().setBackground(Color.WHITE);
98 graphicsManager.getContentPane().setFocusTraversalKeysEnabled(false);
99 }
100
101 // Set up the robot (for controlling the mouse)
102 // TODO: What to do if robot throws exception??? cts16
103 try {
104 _robot = new Robot();
105 } catch (AWTException e) {
106 e.printStackTrace();
107 }
108
109 // Create the timer for firing timeout events
110 Timer.setLogTimers(false);
111 _timer = new Timer(1, new ActionListener() {
112 @Override
113 public void actionPerformed(ActionEvent e) {
114 swingInputManager.triggerTimeoutEvents();
115 }
116 });
117 _timer.setRepeats(false);
118 }
119
120 @Override
121 protected Point getRealCursorPosition()
122 {
123 Point mouseScreenPos = SwingConversions.fromSwingPoint(MouseInfo.getPointerInfo().getLocation());
124 return screenToWindowPosition(mouseScreenPos);
125 }
126
127 @Override
128 public void setCursorPosition(Point position)
129 {
130 Point screenPosition = windowToScreenPosition(position);
131 _robot.mouseMove(screenPosition.x, screenPosition.y);
132 }
133
134 @Override
135 public void windowStateChanged(WindowEvent e)
136 {
137 // Does nothing. cts16
138 }
139
140 @Override
141 public void windowOpened(WindowEvent e)
142 {
143 // Does nothing. cts16
144 }
145
146 @Override
147 public void windowClosing(WindowEvent e)
148 {
149 // Does nothing. cts16
150 }
151
152 @Override
153 public void windowClosed(WindowEvent e)
154 {
155 SwingGraphicsManager g = SwingMiscManager.getIfUsingSwingGraphicsManager();
156
157 if (g == null || !g.isFullscreenTransitionPending()) {
158 distributeWindowEvent(WindowEventType.WINDOW_CLOSED);
159 }
160 }
161
162 @Override
163 public void windowIconified(WindowEvent e)
164 {
165 // Does nothing. cts16
166 }
167
168 @Override
169 public void windowDeiconified(WindowEvent e)
170 {
171 // Does nothing. cts16
172 }
173
174 @Override
175 public void windowActivated(WindowEvent e)
176 {
177 // Does nothing. cts16
178 }
179
180 @Override
181 public void windowDeactivated(WindowEvent e)
182 {
183 // Does nothing. cts16
184 }
185
186 @Override
187 public void componentResized(ComponentEvent e)
188 {
189 SwingGraphicsManager g = SwingMiscManager.getIfUsingSwingGraphicsManager();
190 if (g != null) g.refreshRootSurface();
191
192 distributeWindowEvent(WindowEventType.WINDOW_RESIZED);
193 }
194
195 @Override
196 public void componentMoved(ComponentEvent e)
197 {
198 // Does nothing. cts16
199
200 }
201
202 @Override
203 public void componentShown(ComponentEvent e)
204 {
205 SwingGraphicsManager g = SwingMiscManager.getIfUsingSwingGraphicsManager();
206
207 if (g != null) g.finishFullscreenTransition();
208 }
209
210 @Override
211 public void componentHidden(ComponentEvent e)
212 {
213 // Does nothing. cts16
214 }
215
216 /** Converts a location given in screen-space to window-space. */
217 private Point screenToWindowPosition(Point screenPosition)
218 {
219 Point windowTopLeft = EcosystemManager.getGraphicsManager().getWindowLocation();
220
221 return Point.difference(screenPosition, windowTopLeft);
222 }
223
224 /** Converts a location given in window-space to screen-space. */
225 private Point windowToScreenPosition(Point windowPosition)
226 {
227 Point windowTopLeft = EcosystemManager.getGraphicsManager().getWindowLocation();
228
229 return windowPosition.clone().add(windowTopLeft);
230 }
231
232 @Override
233 public void mouseWheelMoved(MouseWheelEvent e)
234 {
235 // Give widgets first whack at input
236 distributeNativeInput(e);
237 if (e.isConsumed()) return;
238
239 try {
240 // Create an input event
241 Integer rotation = new Integer(e.getWheelRotation());
242 InputEvent event = new KBMInputEvent(KBMInputEvent.EventType.MOUSE_WHEEL_SCROLL, rotation);
243
244 // Translate and perform the input
245 distributeInputEvent(event);
246 } catch (Exception exception) {
247 System.err.println(exception.getMessage());
248 }
249 }
250
251 @Override
252 public void mouseDragged(MouseEvent e)
253 {
254 mouseMoved(e);
255 }
256
257 @Override
258 public void mouseMoved(MouseEvent e)
259 {
260 // Update the cursor position
261 updateCursorPosition(e.getX(), e.getY());
262
263 // Give widgets first whack at input
264 distributeNativeInput(e);
265 if (e.isConsumed()) return;
266
267 try {
268 // Create an input event
269 Point movedFrom = getCursorPosition();
270 Point movedTo = new Point(e.getX(), e.getY());
271 InputEvent event = new KBMInputEvent(KBMInputEvent.EventType.MOUSE_MOVE, new Line(movedFrom, movedTo));
272
273 // Translate and perform the input
274 distributeInputEvent(event);
275 } catch (Exception exception) {
276 System.err.println(exception.getMessage());
277 }
278 }
279
280 @Override
281 public void mouseClicked(MouseEvent e)
282 {
283 // Give widgets first whack at input
284 distributeNativeInput(e);
285 if (e.isConsumed()) return;
286
287 // Do nothing else (handled by mousePressed and mouseReleased)
288 }
289
290 @Override
291 public void mousePressed(MouseEvent e)
292 {
293 mouseAction(e, true);
294 }
295
296 @Override
297 public void mouseReleased(MouseEvent e)
298 {
299 mouseAction(e, false);
300 }
301
302 /** Handles mouse pressed/released events. */
303 private void mouseAction(MouseEvent e, boolean down)
304 {
305 // Give widgets first whack at input
306 distributeNativeInput(e);
307 if (e.isConsumed()) return;
308
309 // Work out which button was pressed
310 KBMInputEvent.MouseButton button;
311 switch (e.getButton()) {
312 case MouseEvent.BUTTON1:
313 button = KBMInputEvent.MouseButton.LEFT;
314 break;
315 case MouseEvent.BUTTON2:
316 button = KBMInputEvent.MouseButton.MIDDLE;
317 break;
318 case MouseEvent.BUTTON3:
319 button = KBMInputEvent.MouseButton.RIGHT;
320 break;
321 default:
322 return;
323 }
324
325 try {
326 // Create an input event
327 KBMInputEvent.EventType type = down ? KBMInputEvent.EventType.MOUSE_BUTTON_DOWN : KBMInputEvent.EventType.MOUSE_BUTTON_UP;
328 InputEvent event = new KBMInputEvent(type, button);
329
330 // Translate and perform the input
331 distributeInputEvent(event);
332 } catch (Exception exception) {
333 System.err.println(exception.getMessage());
334 }
335 }
336
337 /** Prevents forwarding mouse-entered-window events when coming back from a widget. */
338 private boolean _inWindow = false;
339
340 @Override
341 public void mouseEntered(MouseEvent e)
342 {
343 if (!_inWindow) {
344 _inWindow = true;
345 distributeWindowEvent(WindowEventType.MOUSE_ENTERED_WINDOW);
346 }
347 }
348
349 @Override
350 public void mouseExited(MouseEvent e)
351 {
352 _inWindow = false;
353 distributeWindowEvent(WindowEventType.MOUSE_EXITED_WINDOW);
354 }
355
356 @Override
357 public void keyTyped(KeyEvent e)
358 {
359 // Give widgets first whack at input
360 distributeNativeInput(e);
361 if (e.isConsumed()) return;
362
363 try {
364 // Ignore escape character and control characters
365 if (e.getKeyChar() == KeyEvent.VK_ESCAPE || e.isControlDown() || e.isAltDown()) return;
366
367 // Create an input event
368 Character character = new Character(e.getKeyChar());
369 InputEvent event = new KBMInputEvent(KBMInputEvent.EventType.CHAR_TYPED, character);
370
371 // Translate and perform the input
372 distributeInputEvent(event);
373 } catch (Exception exception) {
374 System.err.println(exception.getMessage());
375 }
376 }
377
378 @Override
379 public void keyPressed(KeyEvent e)
380 {
381 keyAction(e, true);
382 }
383
384 @Override
385 public void keyReleased(KeyEvent e)
386 {
387 keyAction(e, false);
388 }
389
390 /** Handles key pressed/released events. */
391 private void keyAction(KeyEvent e, boolean down)
392 {
393 // Give widgets first whack at input
394 distributeNativeInput(e);
395 if (e.isConsumed()) return;
396
397 KBMInputEvent.Key key = SwingConversions.fromSwingVirtualKey(e.getKeyCode());
398 if (key == null) return;
399
400 try {
401 // Create an input event
402 KBMInputEvent.EventType type = down ? KBMInputEvent.EventType.KEY_DOWN : KBMInputEvent.EventType.KEY_UP;
403 InputEvent event = new KBMInputEvent(type, key);
404
405 // Translate and perform the input
406 distributeInputEvent(event);
407 } catch (Exception exception) {
408 System.err.println(exception.getMessage());
409 }
410 }
411
412 @Override
413 protected final boolean isInputTypeSupported(InputType type)
414 {
415 // Only keyboard/mouse input is currently supported
416 if (type == InputEvent.InputType.KBM) return true;
417
418 return false;
419 }
420
421 /** Forwards the given input event to the listener if it is listening for it. */
422 private void forwardNativeInputToListener(Object listener, java.awt.event.InputEvent event)
423 {
424 if (listener == null || event == null) return;
425
426 if (listener instanceof KeyListener && event instanceof KeyEvent) {
427
428 KeyListener keyListener = (KeyListener) listener;
429 KeyEvent ke = (KeyEvent) event;
430
431 switch (event.getID()) {
432 case KeyEvent.KEY_PRESSED:
433 keyListener.keyPressed(ke);
434 return;
435 case KeyEvent.KEY_RELEASED:
436 keyListener.keyReleased(ke);
437 return;
438 case KeyEvent.KEY_TYPED:
439 keyListener.keyTyped(ke);
440 return;
441 }
442
443 } else if (event instanceof MouseEvent) {
444
445 MouseEvent me = (MouseEvent) event;
446
447 if (listener instanceof MouseListener) {
448 MouseListener mouseListener = (MouseListener) listener;
449 switch (event.getID()) {
450 case MouseEvent.MOUSE_CLICKED:
451 mouseListener.mouseClicked(me);
452 return;
453 case MouseEvent.MOUSE_PRESSED:
454 mouseListener.mousePressed(me);
455 return;
456 case MouseEvent.MOUSE_RELEASED:
457 mouseListener.mouseReleased(me);
458 return;
459 case MouseEvent.MOUSE_ENTERED:
460 mouseListener.mouseEntered(me);
461 return;
462 case MouseEvent.MOUSE_EXITED:
463 mouseListener.mouseExited(me);
464 return;
465 }
466 }
467
468 if (listener instanceof MouseMotionListener) {
469 MouseMotionListener motionListener = (MouseMotionListener) listener;
470 switch (event.getID()) {
471 case MouseEvent.MOUSE_MOVED:
472 motionListener.mouseMoved(me);
473 return;
474 case MouseEvent.MOUSE_DRAGGED:
475 motionListener.mouseDragged(me);
476 return;
477 }
478 }
479
480 if (listener instanceof MouseWheelListener && me instanceof MouseWheelEvent) {
481 MouseWheelListener wheelListener = (MouseWheelListener) listener;
482 MouseWheelEvent mwe = (MouseWheelEvent) me;
483 switch (event.getID()) {
484 case MouseEvent.MOUSE_WHEEL:
485 wheelListener.mouseWheelMoved(mwe);
486 return;
487 }
488 }
489 }
490 }
491
492 /** Distributes the native input to all pop-ups and widgets. */
493 private void distributeNativeInput(java.awt.event.InputEvent event)
494 {
495 // Distribute to pop-ups first
496 distributeNativeInputToPopups(event);
497 if (event.isConsumed()) return;
498
499 // Then distribute to widgets
500 distributeNativeInputToWidgets(event);
501 }
502
503 /** Distributes the input event to registered widgets. */
504 private void distributeNativeInputToPopups(java.awt.event.InputEvent event)
505 {
506 if (event == null) return;
507
508 for (Popup popup : PopupManager.getInstance().getPopups()) {
509 forwardNativeInputToListener(popup, event);
510
511 if (event.isConsumed()) return;
512 }
513 }
514
515 /** Distributes the input event to registered widgets. */
516 private void distributeNativeInputToWidgets(java.awt.event.InputEvent event)
517 {
518 if (event == null) return;
519
520 for (Widget iw : _widgets) {
521 java.awt.event.InputEvent e = respecInputEventForWidget(event, iw);
522
523 forwardNativeInputToListener(iw, e);
524
525 if (e != event && e.isConsumed()) event.consume();
526
527 if (event.isConsumed()) return;
528 }
529 }
530
531 /** Modifies the event so that it is relative to the given widget. */
532 private java.awt.event.InputEvent respecInputEventForWidget(java.awt.event.InputEvent event, Widget iw)
533 {
534 // TODO: Complete. cts16
535 return event;
536 }
537
538 /** The timer which fires timeout events for the Swing input manager. */
539 private Timer _timer;
540
541 @Override
542 protected void updateTimer(long nextTimeout)
543 {
544 if (nextTimeout < 0) nextTimeout = 0;
545 _timer.stop();
546 _timer.setInitialDelay((int) nextTimeout);
547 _timer.start();
548
549 }
550}
Note: See TracBrowser for help on using the repository browser.