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

Last change on this file since 1131 was 1131, checked in by bln4, 6 years ago
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 //System.err.println("Input manager position to: " + x + "," + y);
158 setCursorPosition(getCursorPosition().clone().add(x, y));
159 }
160
161 /*
162 *
163 * Timeout-event handling.
164 *
165 */
166
167 /** TODO: Comment. cts16 */
168 private TimeoutQueue _timeoutQueue;
169
170 /** TODO: Comment. cts16 */
171 public TimeoutHandle addTimeout(int milliseconds)
172 {
173 TimeoutHandle handle = _timeoutQueue.addTimeout(milliseconds);
174
175 Long nextTimeout = _timeoutQueue.getNextTimeout();
176
177 if (nextTimeout != null) updateTimer(nextTimeout);
178
179 return handle;
180 }
181
182 /** TODO: Comment. cts16 */
183 public void cancelTimeout(TimeoutHandle handle)
184 {
185 _timeoutQueue.cancelTimeout(handle);
186 }
187
188 /** TODO: Comment. cts16 */
189 protected abstract void updateTimer(long nextTimeout);
190
191 /** TODO: Comment. cts16 */
192 public final void triggerTimeoutEvents()
193 {
194 Set<TimeoutHandle> expiredHandles;
195 while (!(expiredHandles = _timeoutQueue.popExpiredTimeouts()).isEmpty()) {
196 triggerTimeoutEvents(expiredHandles);
197 }
198
199 Long nextTimeout = _timeoutQueue.getNextTimeout();
200
201 if (nextTimeout != null) updateTimer(nextTimeout);
202 }
203
204 /** TODO: Comment. cts16 */
205 protected final void triggerTimeoutEvents(Collection<TimeoutHandle> handles)
206 {
207 if (handles == null) return;
208
209 for (TimeoutHandle handle : handles) triggerTimeoutEvent(handle);
210 }
211
212 /** TODO: Comment. cts16 */
213 protected final void triggerTimeoutEvent(TimeoutHandle handle)
214 {
215 if (handle == null) return;
216
217 TimeoutInputEvent event = new TimeoutInputEvent(handle);
218
219 distributeInputEvent(event);
220 }
221
222 /*
223 *
224 * Native-input-layer distribution methods.
225 *
226 */
227
228 /** The widgets enrolled with the input manager. */
229 protected List<Widget> _widgets;
230
231 /** Registers a widget with the manager. */
232 public void addInteractiveWidget(Widget iw)
233 {
234 if (iw == null) return;
235
236 if (!_widgets.contains(iw)) _widgets.add(iw);
237 }
238
239 /** Unregisters a widget with the manager. */
240 public void removeInteractiveWidget(Widget iw)
241 {
242 if (iw == null) return;
243
244 if (_widgets.contains(iw)) _widgets.remove(iw);
245 }
246
247 /*
248 *
249 * Expeditee-input-layer distribution methods.
250 *
251 */
252
253 /** The registered input event listeners. */
254 private LinkedList<InputEventListener> _inputEventListeners;
255
256 /**
257 * Registers the given input event listener to intercept input events.
258 * Priority of interception is in order of registration.
259 */
260 public final void registerInputEventListener(InputEventListener listener)
261 {
262 if (listener == null) return;
263
264 _inputEventListeners.add(listener);
265 }
266
267 /** Unregisters an input event listener. */
268 public final void removeInputEventListener(InputEventListener listener)
269 {
270 if (listener == null) return;
271
272 _inputEventListeners.remove(listener);
273 }
274
275 /**
276 * Lets any registered input-event listeners view/consume input. If none
277 * consume the input, it gets passed to the gesture-translation system. This method
278 * should be called by the implementation-native input system to get input into
279 * Expeditee.
280 */
281 protected final void distributeInputEvent(InputEvent event)
282 {
283 // Allow listeners to optionally consume input before gesture translation
284 for (InputEventListener listener : _inputEventListeners) {
285 if (listener.onInputEvent(event)) return;
286 }
287
288 // Get the relevant translators
289 List<InputEventToGestureTranslator> translators = getInputEventToGestureTranslators(event.getInputType());
290
291 // Translate the input event in reverse order, only taking the gestures from the first
292 // translator to respond
293 List<Gesture> gestures = null;
294 ListIterator<InputEventToGestureTranslator> it = translators.listIterator(translators.size());
295 while (it.hasPrevious()) {
296 InputEventToGestureTranslator translator = it.previous();
297 List<Gesture> translatorGestures = translator.onInputEvent(event);
298 if (gestures == null && translatorGestures != null && !translatorGestures.isEmpty()) gestures = translatorGestures;
299 }
300
301 // Perform the gestures
302 distributeGestures(gestures, false);
303 }
304
305 /*
306 *
307 * Gesture-layer distribution methods.
308 *
309 */
310
311 /** The registered InputEventToGestureTranslators. */
312 private HashMap<InputType, List<InputEventToGestureTranslator>> _translators;
313
314 /** The registered gesture listeners. */
315 private LinkedList<GestureListener> _gestureListeners;
316
317 /**
318 * Sets the translator to use to generate gestures for a given type of input.
319 * @return True if the input type is supported by this Input Manager, false if not.
320 */
321 public final void addInputEventToGestureTranslator(InputEventToGestureTranslator translator)
322 {
323 if (translator == null) return;
324
325 Set<InputType> types = translator.getMonitoredInputTypes();
326
327 for (InputType type : types) _translators.get(type).add(translator);
328 }
329
330 /**
331 * Gets the input-event -> gesture translator that was registered with the
332 * given input type.
333 */
334 public final List<InputEventToGestureTranslator> getInputEventToGestureTranslators(InputType type)
335 {
336 if (type == null) return null;
337
338 return _translators.get(type);
339 }
340
341 /**
342 * Should return true if the input manager supports the given
343 * type of input and false if not.
344 */
345 protected abstract boolean isInputTypeSupported(InputType type);
346
347 /** Registers a new listener to receive gestures from the input system. */
348 public final void registerGestureListener(GestureListener listener)
349 {
350 if (listener == null) return;
351
352 _gestureListeners.add(listener);
353 }
354
355 /** Notifies all gesture listeners of the given gestures. */
356 public void distributeGestures(List<Gesture> gestures)
357 {
358 distributeGestures(gestures, true);
359 }
360
361 /** Notifies all gesture listeners of the given gestures. */
362 protected void distributeGestures(List<Gesture> gestures, boolean robotic)
363 {
364 if (gestures == null) return;
365
366 for (Gesture gesture : gestures) distributeGesture(gesture, robotic);
367 }
368
369 /** Notifies all gesture listeners of the given gesture. */
370 public void distributeGesture(Gesture gesture)
371 {
372 distributeGesture(gesture, true);
373 }
374
375 /** Notifies all gesture listeners of the given gesture. */
376 protected void distributeGesture(Gesture gesture, boolean robotic)
377 {
378 if (gesture == null) return;
379
380 // Ensure robotic callers don't try to spoof being non-robotic
381 if (robotic) gesture.setRobotic(true);
382
383 for (GestureListener listener : _gestureListeners) {
384 listener.onGesture(gesture.clone());
385 }
386 }
387
388}
Note: See TracBrowser for help on using the repository browser.