00001 package edu.stanford.hci.r3.events;
00002
00003 import java.util.ArrayList;
00004 import java.util.Collection;
00005 import java.util.Collections;
00006 import java.util.HashMap;
00007 import java.util.List;
00008 import java.util.Map;
00009
00010 import edu.stanford.hci.r3.events.PenEvent.PenEventModifier;
00011 import edu.stanford.hci.r3.events.replay.EventReplayManager;
00012 import edu.stanford.hci.r3.paper.Region;
00013 import edu.stanford.hci.r3.paper.Sheet;
00014 import edu.stanford.hci.r3.pattern.coordinates.PatternLocationToSheetLocationMapping;
00015 import edu.stanford.hci.r3.pattern.coordinates.conversion.PatternCoordinateConverter;
00016 import edu.stanford.hci.r3.pen.PenInput;
00017 import edu.stanford.hci.r3.pen.PenSample;
00018 import edu.stanford.hci.r3.pen.streaming.listeners.PenListener;
00019 import edu.stanford.hci.r3.units.coordinates.PercentageCoordinates;
00020 import edu.stanford.hci.r3.util.DebugUtils;
00021
00038 public class EventEngine {
00039
00043 private List<EventHandler> catchAllHandlers = new ArrayList<EventHandler>();
00044
00048 private PercentageCoordinates lastKnownLocation;
00049
00054 private List<EventHandler> mostRecentEventHandlers = new ArrayList<EventHandler>();
00055
00060 private int numTrashedPenEvents = 0;
00061
00066 private List<PatternLocationToSheetLocationMapping> patternToSheetMaps = Collections
00067 .synchronizedList(new ArrayList<PatternLocationToSheetLocationMapping>());
00068
00073 private Map<PenInput, Integer> penRegistrationCount = new HashMap<PenInput, Integer>();
00074
00078 private List<PenInput> pensCurrentlyMonitoring = new ArrayList<PenInput>();
00079
00083 private Map<PenInput, PenListener> penToListener = new HashMap<PenInput, PenListener>();
00084
00088 private EventReplayManager replayManager;
00089
00094 public EventEngine() {
00095 replayManager = new EventReplayManager(this);
00096 }
00097
00103 public void addEventHandlerForUnmappedEvents(EventHandler handler) {
00104 catchAllHandlers.add(handler);
00105 }
00106
00111 private void addPenToInternalLists(PenInput pen, PenListener listener) {
00112 penToListener.put(pen, listener);
00113 pen.addLivePenListener(listener);
00114 }
00115
00122 private PenEvent createPenEvent(String penName, int penID, PenSample sample) {
00123
00124 final PenEvent event = new PenEvent(penID, penName, System.currentTimeMillis(), sample);
00125 return event;
00126 }
00127
00132 private int decrementPenRegistrationCount(PenInput pen) {
00133 Integer count = penRegistrationCount.get(pen);
00134 if (count == null) {
00135
00136 DebugUtils.println("We do not have a record for this pen, and "
00137 + "cannot decrement the registration count.");
00138 return 0;
00139 } else if (count == 1) {
00140 penRegistrationCount.remove(pen);
00141 return 0;
00142 } else {
00143 penRegistrationCount.put(pen, count - 1);
00144 return count - 1;
00145 }
00146 }
00147
00151 public EventReplayManager getEventReplayManager() {
00152 return replayManager;
00153 }
00154
00160 private PenListener getNewPenListener(final PenInput penInputDevice) {
00161 pensCurrentlyMonitoring.add(penInputDevice);
00162
00163
00164 final int penID = pensCurrentlyMonitoring.indexOf(penInputDevice);
00165 final String penName = penInputDevice.getName();
00166
00167 return new PenListener() {
00168
00172 public void penDown(PenSample sample) {
00173 final PenEvent event = createPenEvent(penName, penID, sample);
00174 event.setModifier(PenEventModifier.DOWN);
00175
00176
00177
00178 replayManager.saveEvent(event);
00179 handlePenEvent(event);
00180 }
00181
00188 public void penUp(PenSample sample) {
00189 final PenEvent event = createPenEvent(penName, penID, sample);
00190 event.setModifier(PenEventModifier.UP);
00191
00192
00193
00194
00195 replayManager.saveEvent(event);
00196 handlePenUpEvent(event);
00197 }
00198
00202 public void sample(PenSample sample) {
00203 final PenEvent event = createPenEvent(penName, penID, sample);
00204 replayManager.saveEvent(event);
00205 handlePenEvent(event);
00206 }
00207 };
00208 }
00209
00222 public void handlePenEvent(PenEvent penEvent) {
00223
00224 mostRecentEventHandlers.clear();
00225
00226 synchronized (patternToSheetMaps) {
00227
00228 boolean eventHandledAtLeastOnce = false;
00229
00230
00231
00232 for (final PatternLocationToSheetLocationMapping pmap : patternToSheetMaps) {
00233
00234
00235
00236
00237
00238 final PatternCoordinateConverter coordinateConverter = pmap
00239 .getCoordinateConverterForSample(penEvent.getOriginalSample());
00240
00241
00242 if (coordinateConverter == null) {
00243 continue;
00244 }
00245
00246
00247 final Sheet sheet = pmap.getSheet();
00248
00249
00250 final String regionName = coordinateConverter.getRegionName();
00251 final Region region = sheet.getRegion(regionName);
00252
00253
00254 final PercentageCoordinates relativeLocation = coordinateConverter
00255 .getRelativeLocation(penEvent.getStreamedPatternCoordinate());
00256 penEvent.setPercentageLocation(relativeLocation);
00257
00258 lastKnownLocation = relativeLocation;
00259
00260
00261
00262 final List<EventHandler> eventHandlers = region.getEventHandlers();
00263
00264
00265 for (EventHandler eh : eventHandlers) {
00266 eh.handleEvent(penEvent);
00267 eventHandledAtLeastOnce = true;
00268 mostRecentEventHandlers.add(eh);
00269 if (penEvent.isConsumed()) {
00270
00271
00272
00273
00274 return;
00275 }
00276 }
00277
00278 }
00279
00280 if (!eventHandledAtLeastOnce) {
00281
00282
00283 for (EventHandler eh : catchAllHandlers) {
00284 eventHandledAtLeastOnce = true;
00285 eh.handleEvent(penEvent);
00286 mostRecentEventHandlers.add(eh);
00287 }
00288
00289 if (!eventHandledAtLeastOnce) {
00290
00291
00292
00293
00294
00295
00296 if (numTrashedPenEvents % 29 == 0) {
00297
00298 System.err
00299 .println("The event engine cannot map these pen events to any active region. "
00300 + "We'll trash these events for now, but perhaps you should investigate "
00301 + "attaching a pattern mapping (even at runtime) to your regions.");
00302 }
00303 numTrashedPenEvents++;
00304 DebugUtils.println("Cannot Map " + penEvent);
00305 }
00306 }
00307 }
00308 }
00309
00315 public void handlePenUpEvent(PenEvent event) {
00316 event.setPercentageLocation(lastKnownLocation);
00317 for (EventHandler h : mostRecentEventHandlers) {
00318 h.handleEvent(event);
00319 }
00320 }
00321
00327 private void incrementPenRegistrationCount(PenInput pen) {
00328 Integer count = penRegistrationCount.get(pen);
00329 if (count == null) {
00330 penRegistrationCount.put(pen, 1);
00331 } else {
00332 penRegistrationCount.put(pen, count + 1);
00333 }
00334 DebugUtils.println("We have registered " + penRegistrationCount.get(pen) + " pens in total.");
00335 }
00336
00347 public void register(PenInput pen) {
00348
00349 PenListener listener = penToListener.get(pen);
00350 if (listener != null) {
00351 removePenFromInternalLists(pen, listener);
00352 }
00353
00354
00355
00356
00357 listener = getNewPenListener(pen);
00358 addPenToInternalLists(pen, listener);
00359 incrementPenRegistrationCount(pen);
00360 }
00361
00368 public void registerPatternMapForEventHandling(PatternLocationToSheetLocationMapping mapping) {
00369 DebugUtils.println("Registering A Pattern Location to Sheet Location Map");
00370 if (patternToSheetMaps.contains(mapping)) {
00371 DebugUtils.println("EventEngine is already aware of this pattern map.");
00372 }
00373 patternToSheetMaps.add(mapping);
00374 }
00375
00381 public void registerPatternMapsForEventHandling(
00382 Collection<PatternLocationToSheetLocationMapping> patternMaps) {
00383 DebugUtils
00384 .println("Registering the (Pattern Location --> Sheet Location) Maps [" + patternMaps + "]");
00385 patternToSheetMaps.addAll(patternMaps);
00386 DebugUtils.println("Registered " + patternMaps.size() + " New Maps");
00387 }
00388
00392 public void removeEventHandlerForUnmappedEvents(EventHandler handler) {
00393 catchAllHandlers.remove(handler);
00394 }
00395
00401 private void removePenFromInternalLists(PenInput pen, PenListener listener) {
00402 penToListener.remove(pen);
00403 pen.removeLivePenListener(listener);
00404 pensCurrentlyMonitoring.remove(pen);
00405 }
00406
00410 public void unregisterAllPatternMaps() {
00411 patternToSheetMaps.clear();
00412 }
00413
00417 public void unregisterPatternMapForEventHandling(PatternLocationToSheetLocationMapping patternMap) {
00418 patternToSheetMaps.remove(patternMap);
00419 }
00420
00426 public void unregisterPatternMapsForEventHandling(
00427 Collection<PatternLocationToSheetLocationMapping> patternMaps) {
00428 patternToSheetMaps.removeAll(patternMaps);
00429 }
00430
00436 public void unregisterPen(PenInput pen) {
00437 int newCount = decrementPenRegistrationCount(pen);
00438 if (newCount == 0) {
00439 DebugUtils.println("Count is at Zero. Let's remove the pen and its listener...");
00440 PenListener listener = penToListener.get(pen);
00441 removePenFromInternalLists(pen, listener);
00442 }
00443 }
00444 }