source: trunk/src/org/expeditee/gio/InputManager.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: 10.9 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.InputEventListener;
16import org.expeditee.gio.input.InputEventToGestureTranslator;
17import org.expeditee.gio.input.InputEvent.InputType;
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()) _translators.put(type, new LinkedList<InputEventToGestureTranslator>());
78
79 // Initialise the list of gesture listeners
80 _gestureListeners = new LinkedList<GestureListener>();
81
82 // Initialise the cursor position
83 _cursorPosition = getRealCursorPosition().clone();
84 }
85
86 /*
87 *
88 * Window-specific events.
89 *
90 */
91
92 /** The types of window events that can be listened for. */
93 public enum WindowEventType {
94 WINDOW_RESIZED,
95 WINDOW_CLOSED,
96 MOUSE_EXITED_WINDOW,
97 MOUSE_ENTERED_WINDOW
98 }
99
100 /** The interface window-event listeners must implement to receive events. */
101 public static interface WindowEventListener
102 {
103 public void onWindowEvent(WindowEventType type);
104 }
105
106 /** The list of window-event listeners. */
107 private List<WindowEventListener> _windowEventListeners;
108
109 /** Adds a new window-event listener. */
110 public void addWindowEventListener(WindowEventListener listener)
111 {
112 _windowEventListeners.add(listener);
113 }
114
115 /**
116 * Should be called by platform-specific input managers to
117 * notify listeners of a window event.
118 */
119 protected void distributeWindowEvent(WindowEventType type)
120 {
121 ListIterator<WindowEventListener> it = _windowEventListeners.listIterator(_windowEventListeners.size());
122
123 // Run event handlers in reverse order
124 while (it.hasPrevious()) it.previous().onWindowEvent(type);
125 }
126
127 /*
128 *
129 * Cursor-control methods.
130 *
131 */
132
133 /** The position of the mouse cursor. */
134 private Point _cursorPosition;
135
136 /** Gets the current position of the cursor inside the Expeditee window. */
137 public Point getCursorPosition()
138 {
139 return _cursorPosition.clone();
140 }
141
142 /** Gets the position of the cursor (for initialisation purposes). */
143 protected abstract Point getRealCursorPosition();
144
145 /** Should be called by the platform-specific input manager to keep the cursor position up-to-date. */
146 protected void updateCursorPosition(int x, int y)
147 {
148 _cursorPosition.set(x, y);
149 }
150
151 /** Moves the cursor to the given position in the window. */
152 public abstract void setCursorPosition(Point position);
153
154 /** Moves the cursor by the given amount. */
155 public final void moveCursor(int x, int y)
156 {
157 setCursorPosition(getCursorPosition().clone().add(x, y));
158 }
159
160 /*
161 *
162 * Timeout-event handling.
163 *
164 */
165
166 /** TODO: Comment. cts16 */
167 private TimeoutQueue _timeoutQueue;
168
169 /** TODO: Comment. cts16 */
170 public TimeoutHandle addTimeout(int milliseconds)
171 {
172 TimeoutHandle handle = _timeoutQueue.addTimeout(milliseconds);
173
174 Long nextTimeout = _timeoutQueue.getNextTimeout();
175
176 if (nextTimeout != null) updateTimer(nextTimeout);
177
178 return handle;
179 }
180
181 /** TODO: Comment. cts16 */
182 public void cancelTimeout(TimeoutHandle handle)
183 {
184 _timeoutQueue.cancelTimeout(handle);
185 }
186
187 /** TODO: Comment. cts16 */
188 protected abstract void updateTimer(long nextTimeout);
189
190 /** TODO: Comment. cts16 */
191 public final void triggerTimeoutEvents()
192 {
193 Set<TimeoutHandle> expiredHandles;
194 while (!(expiredHandles = _timeoutQueue.popExpiredTimeouts()).isEmpty()) {
195 triggerTimeoutEvents(expiredHandles);
196 }
197
198 Long nextTimeout = _timeoutQueue.getNextTimeout();
199
200 if (nextTimeout != null) updateTimer(nextTimeout);
201 }
202
203 /** TODO: Comment. cts16 */
204 protected final void triggerTimeoutEvents(Collection<TimeoutHandle> handles)
205 {
206 if (handles == null) return;
207
208 for (TimeoutHandle handle : handles) triggerTimeoutEvent(handle);
209 }
210
211 /** TODO: Comment. cts16 */
212 protected final void triggerTimeoutEvent(TimeoutHandle handle)
213 {
214 if (handle == null) return;
215
216 TimeoutInputEvent event = new TimeoutInputEvent(handle);
217
218 distributeInputEvent(event);
219 }
220
221 /*
222 *
223 * Native-input-layer distribution methods.
224 *
225 */
226
227 /** The widgets enrolled with the input manager. */
228 protected List<Widget> _widgets;
229
230 /** Registers a widget with the manager. */
231 public void addInteractiveWidget(Widget iw)
232 {
233 if (iw == null) return;
234
235 if (!_widgets.contains(iw)) _widgets.add(iw);
236 }
237
238 /** Unregisters a widget with the manager. */
239 public void removeInteractiveWidget(Widget iw)
240 {
241 if (iw == null) return;
242
243 if (_widgets.contains(iw)) _widgets.remove(iw);
244 }
245
246 /*
247 *
248 * Expeditee-input-layer distribution methods.
249 *
250 */
251
252 /** The registered input event listeners. */
253 private LinkedList<InputEventListener> _inputEventListeners;
254
255 /**
256 * Registers the given input event listener to intercept input events.
257 * Priority of interception is in order of registration.
258 */
259 public final void registerInputEventListener(InputEventListener listener)
260 {
261 if (listener == null) return;
262
263 _inputEventListeners.add(listener);
264 }
265
266 /** Unregisters an input event listener. */
267 public final void removeInputEventListener(InputEventListener listener)
268 {
269 if (listener == null) return;
270
271 _inputEventListeners.remove(listener);
272 }
273
274 /**
275 * Lets any registered input-event listeners view/consume input. If none
276 * consume the input, it gets passed to the gesture-translation system. This method
277 * should be called by the implementation-native input system to get input into
278 * Expeditee.
279 */
280 protected final void distributeInputEvent(InputEvent event)
281 {
282 // Allow listeners to optionally consume input before gesture translation
283 for (InputEventListener listener : _inputEventListeners) {
284 if (listener.onInputEvent(event)) return;
285 }
286
287 // Get the relevant translators
288 List<InputEventToGestureTranslator> translators = getInputEventToGestureTranslators(event.getInputType());
289
290 // Translate the input event in reverse order, only taking the gestures from the first
291 // translator to respond
292 List<Gesture> gestures = null;
293 ListIterator<InputEventToGestureTranslator> it = translators.listIterator(translators.size());
294 while (it.hasPrevious()) {
295 InputEventToGestureTranslator translator = it.previous();
296 List<Gesture> translatorGestures = translator.onInputEvent(event);
297 if (gestures == null && translatorGestures != null && !translatorGestures.isEmpty()) gestures = translatorGestures;
298 }
299
300 // Perform the gestures
301 distributeGestures(gestures, false);
302 }
303
304 /*
305 *
306 * Gesture-layer distribution methods.
307 *
308 */
309
310 /** The registered InputEventToGestureTranslators. */
311 private HashMap<InputType, List<InputEventToGestureTranslator>> _translators;
312
313 /** The registered gesture listeners. */
314 private LinkedList<GestureListener> _gestureListeners;
315
316 /**
317 * Sets the translator to use to generate gestures for a given type of input.
318 * @return True if the input type is supported by this Input Manager, false if not.
319 */
320 public final void addInputEventToGestureTranslator(InputEventToGestureTranslator translator)
321 {
322 if (translator == null) return;
323
324 Set<InputType> types = translator.getMonitoredInputTypes();
325
326 for (InputType type : types) _translators.get(type).add(translator);
327 }
328
329 /**
330 * Gets the input-event -> gesture translator that was registered with the
331 * given input type.
332 */
333 public final List<InputEventToGestureTranslator> getInputEventToGestureTranslators(InputType type)
334 {
335 if (type == null) return null;
336
337 return _translators.get(type);
338 }
339
340 /**
341 * Should return true if the input manager supports the given
342 * type of input and false if not.
343 */
344 protected abstract boolean isInputTypeSupported(InputType type);
345
346 /** Registers a new listener to receive gestures from the input system. */
347 public final void registerGestureListener(GestureListener listener)
348 {
349 if (listener == null) return;
350
351 _gestureListeners.add(listener);
352 }
353
354 /** Notifies all gesture listeners of the given gestures. */
355 public void distributeGestures(List<Gesture> gestures)
356 {
357 distributeGestures(gestures, true);
358 }
359
360 /** Notifies all gesture listeners of the given gestures. */
361 protected void distributeGestures(List<Gesture> gestures, boolean robotic)
362 {
363 if (gestures == null) return;
364
365 for (Gesture gesture : gestures) distributeGesture(gesture, robotic);
366 }
367
368 /** Notifies all gesture listeners of the given gesture. */
369 public void distributeGesture(Gesture gesture)
370 {
371 distributeGesture(gesture, true);
372 }
373
374 /** Notifies all gesture listeners of the given gesture. */
375 protected void distributeGesture(Gesture gesture, boolean robotic)
376 {
377 if (gesture == null) return;
378
379 // Ensure robotic callers don't try to spoof being non-robotic
380 if (robotic) gesture.setRobotic(true);
381
382 for (GestureListener listener : _gestureListeners) {
383 listener.onGesture(gesture.clone());
384 }
385 }
386
387}
Note: See TracBrowser for help on using the repository browser.