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

Last change on this file since 1144 was 1144, checked in by bln4, 6 years ago

Used Eclipse refactoring to encapsulate Point.X and Point.Y

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