source: trunk/src/org/expeditee/gio/InputManager.java@ 1164

Last change on this file since 1164 was 1164, checked in by bln4, 6 years ago
File size: 11.3 KB
Line 
1package org.expeditee.gio;
2
3import java.util.Collection;
4import java.util.HashMap;
5import java.util.LinkedList;
6import java.util.List;
7import java.util.ListIterator;
8import java.util.Set;
9
10import org.expeditee.core.Point;
11import org.expeditee.gio.TimeoutQueue.TimeoutHandle;
12import org.expeditee.gio.gesture.Gesture;
13import org.expeditee.gio.gesture.GestureListener;
14import org.expeditee.gio.input.InputEvent;
15import org.expeditee.gio.input.InputEvent.InputType;
16import org.expeditee.gio.input.InputEventListener;
17import org.expeditee.gio.input.InputEventToGestureTranslator;
18import org.expeditee.gio.input.TimeoutInputEvent;
19import org.expeditee.items.widgets.Widget;
20
21/**
22 * Receives input events from the system and distributes them within Expeditee.
23 * Abstract as platform-specific implementation must be provided to handle receiving
24 * input events from the system.
25 *
26 * Native Input
27 * |
28 * | /---------\
29 * `------->| Widgets |
30 * ,--------| |
31 * | \---------/
32 * v
33 * /-------------\
34 * | Input |
35 * | Translation |
36 * \-------------/
37 * |
38 * Expeditee Input
39 * |
40 * | /-----------\
41 * `------->| Input |
42 * ,--------| Listeners |
43 * | \-----------/
44 * |
45 * v
46 * /-------------\
47 * Context---->| Gesture |
48 * | Translation |
49 * \-------------/
50 * |
51 * Gestures
52 * | /-----------\
53 * `------->| Gesture |
54 * | Listeners |
55 * \-----------/
56 *
57 * @author cts16
58 */
59public abstract class InputManager {
60
61 protected InputManager()
62 {
63 // Initialise the list of window-event listeners
64 _windowEventListeners = new LinkedList<WindowEventListener>();
65
66 // Initialise the timeout queue
67 _timeoutQueue = new TimeoutQueue();
68
69 // Initialise the list of widgets
70 _widgets = new LinkedList<Widget>();
71
72 // Initialise the list of input event listeners
73 _inputEventListeners = new LinkedList<InputEventListener>();
74
75 // Initialise the map of input-event -> gesture translators
76 _translators = new HashMap<InputType, List<InputEventToGestureTranslator>>(InputType.values().length);
77 for (InputType type : InputType.values()) {
78 _translators.put(type, new LinkedList<InputEventToGestureTranslator>());
79 }
80
81 // Initialise the list of gesture listeners
82 _gestureListeners = new LinkedList<GestureListener>();
83
84 // Initialise the cursor position
85 _cursorPosition = getRealCursorPosition().clone();
86 }
87
88 /*
89 *
90 * Window-specific events.
91 *
92 */
93
94 /** The types of window events that can be listened for. */
95 public enum WindowEventType {
96 WINDOW_RESIZED,
97 WINDOW_CLOSED,
98 MOUSE_EXITED_WINDOW,
99 MOUSE_ENTERED_WINDOW
100 }
101
102 /** The interface window-event listeners must implement to receive events. */
103 public static interface WindowEventListener
104 {
105 public void onWindowEvent(WindowEventType type);
106 }
107
108 /** The list of window-event listeners. */
109 private List<WindowEventListener> _windowEventListeners;
110
111 /** Adds a new window-event listener. */
112 public void addWindowEventListener(WindowEventListener listener)
113 {
114 _windowEventListeners.add(listener);
115 }
116
117 /**
118 * Should be called by platform-specific input managers to
119 * notify listeners of a window event.
120 */
121 protected void distributeWindowEvent(WindowEventType type)
122 {
123 ListIterator<WindowEventListener> it = _windowEventListeners.listIterator(_windowEventListeners.size());
124
125 // Run event handlers in reverse order
126 while (it.hasPrevious()) {
127 it.previous().onWindowEvent(type);
128 }
129 }
130
131 /*
132 *
133 * Cursor-control methods.
134 *
135 */
136
137 /** The position of the mouse cursor. */
138 private Point _cursorPosition;
139
140 /** Gets the current position of the cursor inside the Expeditee window. */
141 public Point getCursorPosition()
142 {
143 return _cursorPosition.clone();
144 }
145
146 /** Gets the position of the cursor (for initialisation purposes). */
147 protected abstract Point getRealCursorPosition();
148
149 /** Should be called by the platform-specific input manager to keep the cursor position up-to-date. */
150 protected void updateCursorPosition(int x, int y)
151 {
152 _cursorPosition.set(x, y);
153 }
154
155 /** Moves the cursor to the given position in the window. */
156 public abstract void setCursorPosition(Point position);
157
158 /** Moves the cursor by the given amount. */
159 public final void moveCursor(int x, int y)
160 {
161 setCursorPosition(getCursorPosition().clone().add(x, y));
162 }
163
164 /*
165 *
166 * Timeout-event handling.
167 *
168 */
169
170 /** TODO: Comment. cts16 */
171 private TimeoutQueue _timeoutQueue;
172
173 /** TODO: Comment. cts16 */
174 public TimeoutHandle addTimeout(int milliseconds)
175 {
176 TimeoutHandle handle = _timeoutQueue.addTimeout(milliseconds);
177
178 Long nextTimeout = _timeoutQueue.getNextTimeout();
179
180 if (nextTimeout != null) {
181 updateTimer(nextTimeout);
182 }
183
184 return handle;
185 }
186
187 /** TODO: Comment. cts16 */
188 public void cancelTimeout(TimeoutHandle handle)
189 {
190 _timeoutQueue.cancelTimeout(handle);
191 }
192
193 /** TODO: Comment. cts16 */
194 protected abstract void updateTimer(long nextTimeout);
195
196 /** TODO: Comment. cts16 */
197 public final void triggerTimeoutEvents()
198 {
199 Set<TimeoutHandle> expiredHandles;
200 while (!(expiredHandles = _timeoutQueue.popExpiredTimeouts()).isEmpty()) {
201 triggerTimeoutEvents(expiredHandles);
202 }
203
204 Long nextTimeout = _timeoutQueue.getNextTimeout();
205
206 if (nextTimeout != null) {
207 updateTimer(nextTimeout);
208 }
209 }
210
211 /** TODO: Comment. cts16 */
212 protected final void triggerTimeoutEvents(Collection<TimeoutHandle> handles)
213 {
214 if (handles == null) {
215 return;
216 }
217
218 for (TimeoutHandle handle : handles) {
219 triggerTimeoutEvent(handle);
220 }
221 }
222
223 /** TODO: Comment. cts16 */
224 protected final void triggerTimeoutEvent(TimeoutHandle handle)
225 {
226 if (handle == null) {
227 return;
228 }
229
230 TimeoutInputEvent event = new TimeoutInputEvent(handle);
231
232 distributeInputEvent(event);
233 }
234
235 /*
236 *
237 * Native-input-layer distribution methods.
238 *
239 */
240
241 /** The widgets enrolled with the input manager. */
242 protected List<Widget> _widgets;
243
244 /** Registers a widget with the manager. */
245 public void addInteractiveWidget(Widget iw)
246 {
247 if (iw == null) {
248 return;
249 }
250
251 if (!_widgets.contains(iw)) {
252 _widgets.add(iw);
253 }
254 }
255
256 /** Unregisters a widget with the manager. */
257 public void removeInteractiveWidget(Widget iw)
258 {
259 if (iw == null) {
260 return;
261 }
262
263 if (_widgets.contains(iw)) {
264 _widgets.remove(iw);
265 }
266 }
267
268 /*
269 *
270 * Expeditee-input-layer distribution methods.
271 *
272 */
273
274 /** The registered input event listeners. */
275 private LinkedList<InputEventListener> _inputEventListeners;
276
277 /**
278 * Registers the given input event listener to intercept input events.
279 * Priority of interception is in order of registration.
280 */
281 public final void registerInputEventListener(InputEventListener listener)
282 {
283 if (listener == null) {
284 return;
285 }
286
287 _inputEventListeners.add(listener);
288 }
289
290 /** Unregisters an input event listener. */
291 public final void removeInputEventListener(InputEventListener listener)
292 {
293 if (listener == null) {
294 return;
295 }
296
297 _inputEventListeners.remove(listener);
298 }
299
300 /**
301 * Lets any registered input-event listeners view/consume input. If none
302 * consume the input, it gets passed to the gesture-translation system. This method
303 * should be called by the implementation-native input system to get input into
304 * Expeditee.
305 */
306 protected final void distributeInputEvent(InputEvent event)
307 {
308 // Allow listeners to optionally consume input before gesture translation
309 for (InputEventListener listener : _inputEventListeners) {
310 if (listener.onInputEvent(event)) {
311 return;
312 }
313 }
314
315 // Get the relevant translators
316 List<InputEventToGestureTranslator> translators = getInputEventToGestureTranslators(event.getInputType());
317
318 // Translate the input event in reverse order, only taking the gestures from the first
319 // translator to respond
320 List<Gesture> gestures = null;
321 ListIterator<InputEventToGestureTranslator> it = translators.listIterator(translators.size());
322 while (it.hasPrevious()) {
323 InputEventToGestureTranslator translator = it.previous();
324 List<Gesture> translatorGestures = translator.onInputEvent(event);
325 if (gestures == null && translatorGestures != null && !translatorGestures.isEmpty()) {
326 gestures = translatorGestures;
327 }
328 }
329
330 // Perform the gestures
331 distributeGestures(gestures, false);
332 }
333
334 /*
335 *
336 * Gesture-layer distribution methods.
337 *
338 */
339
340 /** The registered InputEventToGestureTranslators. */
341 private HashMap<InputType, List<InputEventToGestureTranslator>> _translators;
342
343 /** The registered gesture listeners. */
344 private LinkedList<GestureListener> _gestureListeners;
345
346 /**
347 * Sets the translator to use to generate gestures for a given type of input.
348 * @return True if the input type is supported by this Input Manager, false if not.
349 */
350 public final void addInputEventToGestureTranslator(InputEventToGestureTranslator translator)
351 {
352 if (translator == null) {
353 return;
354 }
355
356 Set<InputType> types = translator.getMonitoredInputTypes();
357
358 for (InputType type : types) {
359 _translators.get(type).add(translator);
360 }
361 }
362
363 /**
364 * Gets the input-event -> gesture translator that was registered with the
365 * given input type.
366 */
367 public final List<InputEventToGestureTranslator> getInputEventToGestureTranslators(InputType type)
368 {
369 if (type == null) {
370 return null;
371 }
372
373 return _translators.get(type);
374 }
375
376 /**
377 * Should return true if the input manager supports the given
378 * type of input and false if not.
379 */
380 protected abstract boolean isInputTypeSupported(InputType type);
381
382 /** Registers a new listener to receive gestures from the input system. */
383 public final void registerGestureListener(GestureListener listener)
384 {
385 if (listener == null) {
386 return;
387 }
388
389 _gestureListeners.add(listener);
390 }
391
392 /** Notifies all gesture listeners of the given gestures. */
393 public void distributeGestures(List<Gesture> gestures)
394 {
395 distributeGestures(gestures, true);
396 }
397
398 /** Notifies all gesture listeners of the given gestures. */
399 protected void distributeGestures(List<Gesture> gestures, boolean robotic)
400 {
401 if (gestures == null) {
402 return;
403 }
404
405 for (Gesture gesture : gestures) {
406 distributeGesture(gesture, robotic);
407 }
408 }
409
410 /** Notifies all gesture listeners of the given gesture. */
411 public void distributeGesture(Gesture gesture)
412 {
413 distributeGesture(gesture, true);
414 }
415
416 /** Notifies all gesture listeners of the given gesture. */
417 protected void distributeGesture(final Gesture gesture, final boolean robotic)
418 {
419 if (gesture == null) {
420 return;
421 }
422
423 // Ensure robotic callers don't try to spoof being non-robotic
424 if (robotic) {
425 gesture.setRobotic(true);
426 }
427
428 final Gesture clone = gesture.clone();
429
430 for(final GestureListener listener : _gestureListeners) {
431 listener.preGesture(clone);
432 }
433
434 for (final GestureListener listener : _gestureListeners) {
435 listener.onGesture(clone);
436 }
437
438 for (final GestureListener listener : _gestureListeners) {
439 listener.postGesture(clone);
440 }
441 }
442
443}
Note: See TracBrowser for help on using the repository browser.