EventEngine.java

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                 // make an event object so that someone can send it to the right event handler
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                         // huh? We don't have a record for this pen...
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); // decrement from one to zero
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                 // properties of the pen
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                                 // a pendown generated through a real pen listener should be saved
00177                                 // so that future sessions can replay the stream of events
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                                 // save the pen up also!
00193                                 // do this before setting the location
00194                                 // the location will be determined later, when the event is resent
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                 // System.out.println("Dispatching Event for pen #" + penID + " " + sample);
00224                 mostRecentEventHandlers.clear();
00225 
00226                 synchronized (patternToSheetMaps) {
00227 
00228                         boolean eventHandledAtLeastOnce = false;
00229 
00230                         // for each sample, we first have to convert it to a location on the sheet.
00231                         // THEN, we will be able to make more interesting events...
00232                         for (final PatternLocationToSheetLocationMapping pmap : patternToSheetMaps) {
00233 
00234                                 // this is a key step!
00235                                 // the event engine figures out which pattern region contains
00236                                 // this sample. This determines the set of event handlers the event
00237                                 // should be sent to
00238                                 final PatternCoordinateConverter coordinateConverter = pmap
00239                                                 .getCoordinateConverterForSample(penEvent.getOriginalSample());
00240 
00241                                 // this map doesn't know about this sample; next!
00242                                 if (coordinateConverter == null) {
00243                                         continue;
00244                                 }
00245 
00246                                 // which sheet are we on?
00247                                 final Sheet sheet = pmap.getSheet();
00248 
00249                                 // which region are we on?
00250                                 final String regionName = coordinateConverter.getRegionName();
00251                                 final Region region = sheet.getRegion(regionName);
00252 
00253                                 // where are we on this region?
00254                                 final PercentageCoordinates relativeLocation = coordinateConverter
00255                                                 .getRelativeLocation(penEvent.getStreamedPatternCoordinate());
00256                                 penEvent.setPercentageLocation(relativeLocation);
00257 
00258                                 lastKnownLocation = relativeLocation;
00259 
00260                                 // does this region have any event handler?
00261                                 // if not, just go onto the next region
00262                                 final List<EventHandler> eventHandlers = region.getEventHandlers();
00263                                 // send the event to every event handler!
00264                                 // so long as the event is not consumed
00265                                 for (EventHandler eh : eventHandlers) {
00266                                         eh.handleEvent(penEvent);
00267                                         eventHandledAtLeastOnce = true;
00268                                         mostRecentEventHandlers.add(eh);
00269                                         if (penEvent.isConsumed()) {
00270                                                 // we are done handling this event
00271                                                 // look at no more event handlers
00272                                                 // look at no more pattern maps
00273                                                 // DebugUtils.println("Event Consumed");
00274                                                 return;
00275                                         }
00276                                 } // check the next event handler
00277 
00278                         } // check the next pattern map
00279 
00280                         if (!eventHandledAtLeastOnce) {
00281 
00282                                 // send the event to our "catch-all" event handlers...
00283                                 for (EventHandler eh : catchAllHandlers) {
00284                                         eventHandledAtLeastOnce = true;
00285                                         eh.handleEvent(penEvent);
00286                                         mostRecentEventHandlers.add(eh);
00287                                 } // check the next event handler
00288 
00289                                 if (!eventHandledAtLeastOnce) {
00290                                         // if still, no one has had a chance to deal with this event
00291                                         // it becomes TRASH!
00292 
00293                                         // there is no pattern map that matches the incoming pen event!
00294                                         // we dump it in a trash bin, and notify the user of this situation
00295                                         // perhaps the developer would like to register a mapping at run time?
00296                                         if (numTrashedPenEvents % 29 == 0) {
00297                                                 // print out this warning once in a while...
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); // incremented from zero to one
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                 // get the old listener, if it exists
00349                 PenListener listener = penToListener.get(pen);
00350                 if (listener != null) {
00351                         removePenFromInternalLists(pen, listener);
00352                 }
00353                 // this pen has never been registered, or
00354                 // we just removed the old listener...
00355 
00356                 // add a new listener
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 }

Generated on Sat Apr 14 18:21:34 2007 for R3 Paper Toolkit by  doxygen 1.4.7