package edu.ysu.itrace; import java.awt.Dimension; import java.awt.Toolkit; import java.io.IOException; import java.util.HashMap; import java.util.LinkedList; import java.util.Queue; import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.ITextOperationTarget; import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; import org.osgi.service.event.Event; import org.osgi.service.event.EventHandler; import edu.ysu.itrace.exceptions.CalibrationException; import edu.ysu.itrace.exceptions.EyeTrackerConnectException; import edu.ysu.itrace.gaze.IGazeHandler; import edu.ysu.itrace.gaze.IGazeResponse; import edu.ysu.itrace.gaze.IStyledTextGazeResponse; import edu.ysu.itrace.preferences.PluginPreferences; import edu.ysu.itrace.solvers.ISolver; import edu.ysu.itrace.solvers.JSONGazeExportSolver; import edu.ysu.itrace.solvers.XMLGazeExportSolver; import edu.ysu.itrace.trackers.IEyeTracker; /** * The activator class controls the plug-in life cycle */ public class ITrace extends AbstractUIPlugin implements EventHandler { // The plug-in ID public static final String PLUGIN_ID = "edu.ysu.itrace"; //$NON-NLS-1$ public long sessionStartTime; public Rectangle monitorBounds; // The shared instance private static ITrace plugin; private IEditorPart activeEditor; private HashMap<IEditorPart,TokenHighlighter> tokenHighlighters = new HashMap<IEditorPart,TokenHighlighter>(); private boolean showTokenHighlights = false; private IEyeTracker tracker = null; private volatile boolean recording; private JSONGazeExportSolver jsonSolver; private XMLGazeExportSolver xmlSolver; private boolean jsonOutput = true; private boolean xmlOutput = true; private IActionBars actionBars; private IStatusLineManager statusLineManager; private long registerTime = 2000; private IEventBroker eventBroker; private SessionInfoHandler sessionInfo = new SessionInfoHandler(); private Shell rootShell; /** * The constructor */ public ITrace() { IEditorPart editorPart = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor(); /* if(editorPart != null){ StyledText styledText = (StyledText) editorPart.getAdapter(Control.class); if(styledText != null){ ITextOperationTarget t = (ITextOperationTarget) activeEditor.getAdapter(ITextOperationTarget.class); if(t instanceof ProjectionViewer){ ProjectionViewer projectionViewer = (ProjectionViewer)t; tokenHighlighters.put(activeEditor, new TokenHighlighter(styledText, showTokenHighlights, projectionViewer)); } } } */ eventBroker = PlatformUI.getWorkbench().getService(IEventBroker.class); eventBroker.subscribe("iTrace/newgaze", this); jsonSolver = new JSONGazeExportSolver(); xmlSolver = new XMLGazeExportSolver(); eventBroker.subscribe("iTrace/jsonOutput", jsonSolver); eventBroker.subscribe("iTrace/xmlOutput", xmlSolver); } /* * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) */ public void start(BundleContext context) throws Exception { super.start(context); plugin = this; IPreferenceStore prefStore = getDefault().getPreferenceStore(); EyeTrackerFactory.setTrackerType(EyeTrackerFactory.TrackerType.valueOf( prefStore.getString(PluginPreferences.EYE_TRACKER_TYPE))); activeEditor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor(); } /* * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext context) throws Exception { if (recording) { stopTracking(); } plugin = null; super.stop(context); } /** * Returns the shared instance * * @return the shared instance */ public static ITrace getDefault() { return plugin; } public IEyeTracker getTracker(){ return tracker; } public void setTrackerXDrift(int drift){ tracker.setXDrift(drift); } public void setTrackerYDrift(int drift){ tracker.setYDrift(drift); } public Shell getRootShell(){ return rootShell; } public void setRootShell(Shell shell){ rootShell = shell; } public void setActionBars(IActionBars bars){ actionBars = bars; statusLineManager = actionBars.getStatusLineManager(); } public void setLineManager(IStatusLineManager manager){ statusLineManager = manager; } public void setJsonOutput(boolean value){ jsonOutput = value; } public void displayJsonExportFile(){ jsonSolver.displayExportFile(); } public void setXmlOutput(boolean value){ xmlOutput = value; } public void displayXmlExportFile(){ xmlSolver.displayExportFile(); } public boolean sessionInfoConfigured(){ return sessionInfo.isConfigured(); } public void calibrateTracker(){ requestTracker(); if (tracker != null) { try { tracker.calibrate(); } catch (CalibrationException e1) { eventBroker.post("iTrace/error", e1.getMessage()); } } else { // If tracker is none, requestTracker() would have already // raised an error. } } public boolean startTracking() { if (recording) { eventBroker.post("iTrace/error", "Tracking is already in progress."); return recording; } if (!requestTracker()) { // Error handling occurs in requestTracker(). Just return and // pretend // nothing happened. return recording; } //eventBroker.subscribe("iTrace/newgaze", this); if (!sessionInfo.isConfigured()) { eventBroker.post("iTrace/error", "You have not configured your Session Info."); return recording; } try { sessionInfo.export(); } catch(IOException e) { System.out.println(e.getMessage()); } ITrace.getDefault().sessionStartTime = System.nanoTime(); recording = true; return recording; } public boolean stopTracking() { if (!recording) { eventBroker.post("iTrace/error", "Tracking is not in progress."); return false; } sessionInfo.reset(); xmlSolver.dispose(); jsonSolver.dispose(); if (tracker != null) { } else { // If there is no tracker, tracking should not be occurring anyways. System.out.println("No Tracker"); } statusLineManager.setMessage(""); recording = false; return true; } public boolean toggleTracking(){ if(recording) return stopTracking(); else return startTracking(); } public boolean displayCrosshair(boolean display){ if (tracker == null) requestTracker(); if (tracker != null) { tracker.displayCrosshair(display); return display; } return !display; } public void configureSessionInfo(){ sessionInfo.config(); if (sessionInfo.isConfigured()) { xmlSolver.config(sessionInfo.getSessionID(), sessionInfo.getDevUsername()); jsonSolver.config(sessionInfo.getSessionID(), sessionInfo.getDevUsername()); } } public void setActiveEditor(IEditorPart editorPart){ activeEditor = editorPart; if(activeEditor == null) return; if(!tokenHighlighters.containsKey(editorPart)){ StyledText styledText = (StyledText) editorPart.getAdapter(Control.class); if(styledText != null){ ITextOperationTarget t = (ITextOperationTarget) activeEditor.getAdapter(ITextOperationTarget.class); if(t instanceof ProjectionViewer){ ProjectionViewer projectionViewer = (ProjectionViewer)t; tokenHighlighters.put(activeEditor, new TokenHighlighter(styledText, showTokenHighlights, projectionViewer)); } } } } public void displayEyeStatus(){ if (tracker == null) requestTracker(); if (tracker != null){ EyeStatusWindow statusWindow = new EyeStatusWindow(); statusWindow.setVisible(true); } } public void activateHighlights(){ if (tracker == null) requestTracker(); if (tracker != null){ showTokenHighLights(); } } public void removeHighlighter(IEditorPart editorPart){ tokenHighlighters.remove(editorPart); } public void showTokenHighLights(){ showTokenHighlights = !showTokenHighlights; if(activeEditor == null) return; if(!tokenHighlighters.containsKey(activeEditor)){ StyledText styledText = (StyledText) activeEditor.getAdapter(Control.class); if(styledText != null){ ITextOperationTarget t = (ITextOperationTarget) activeEditor.getAdapter(ITextOperationTarget.class); if(t instanceof ProjectionViewer){ ProjectionViewer projectionViewer = (ProjectionViewer)t; tokenHighlighters.put(activeEditor, new TokenHighlighter(styledText, showTokenHighlights, projectionViewer)); } } } for(TokenHighlighter tokenHighlighter: tokenHighlighters.values()){ tokenHighlighter.setShow(showTokenHighlights); } } private boolean requestTracker() { if (tracker != null) { // Already have a tracker. Don't need another. return true; } try { tracker = EyeTrackerFactory.getConcreteEyeTracker(); if (tracker != null) { tracker.startTracking(); return true; }else{ return false; } } catch (EyeTrackerConnectException e) { return false; } catch (IOException e) { return false; } } /** * Finds the control under the specified screen coordinates and calls its * gaze handler on the localized point. Returns the gaze response or null if * the gaze is not handled. */ private IGazeResponse handleGaze(int screenX, int screenY, Gaze gaze){ Queue<Control[]> childrenQueue = new LinkedList<Control[]>(); childrenQueue.add(rootShell.getChildren()); Rectangle monitorBounds = rootShell.getMonitor().getBounds(); while (!childrenQueue.isEmpty()) { for (Control child : childrenQueue.remove()) { Rectangle childScreenBounds = child.getBounds(); Point screenPos = child.toDisplay(0, 0); childScreenBounds.x = screenPos.x - monitorBounds.x; childScreenBounds.y = screenPos.y - monitorBounds.y; if (childScreenBounds.contains(screenX, screenY)) { if (child instanceof Composite) { Control[] nextChildren = ((Composite) child).getChildren(); if (nextChildren.length > 0 && nextChildren[0] != null) { childrenQueue.add(nextChildren); } } IGazeHandler handler = (IGazeHandler) child .getData(HandlerBindManager.KEY_HANDLER); if (child.isVisible() && handler != null) { return handler.handleGaze(screenX, screenY, screenX - childScreenBounds.x, screenY - childScreenBounds.y, gaze); } } } } return null; } @Override public void handleEvent(Event event) { if(event.getTopic() == "iTrace/newgaze"){ String[] propertyNames = event.getPropertyNames(); Gaze g = (Gaze)event.getProperty(propertyNames[0]); if (g != null) { if(!rootShell.isDisposed()){ Rectangle monitorBounds = rootShell.getMonitor().getBounds(); int screenX = (int) (g.getX() * monitorBounds.width); int screenY = (int) (g.getY() * monitorBounds.height); IGazeResponse response; response = handleGaze(screenX, screenY, g); if (response != null) { if(recording){ statusLineManager .setMessage(String.valueOf(response.getGaze().getSessionTime())); registerTime = System.currentTimeMillis(); if(xmlOutput) eventBroker.post("iTrace/xmlOutput", response); if(jsonOutput) eventBroker.post("iTrace/jsonOutput", response); } if(response instanceof IStyledTextGazeResponse && response != null && showTokenHighlights){ IStyledTextGazeResponse styledTextResponse = (IStyledTextGazeResponse)response; eventBroker.post("iTrace/newstresponse", styledTextResponse); } } }else{ if((System.currentTimeMillis()-registerTime) > 2000){ statusLineManager.setMessage(""); } } } } } }