diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java index 313bf41..ec3a22a 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTCanvas.java @@ -62,7 +62,7 @@ import edu.umd.cs.piccolo.util.PStack; /** - * PCanvas is a simple Swing component that can be used to embed Piccolo + * PSWTCanvas is a simple Swing component that can be used to embed Piccolo * into a Java Swing application. Canvas's view the Piccolo scene graph through * a camera. The canvas manages screen updates coming from this camera, and * forwards swing mouse and keyboard events to the camera. diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimer.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimer.java index 9482615..b301193 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimer.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimer.java @@ -36,30 +36,28 @@ import org.eclipse.swt.widgets.Display; /** + * + * * @author Lance Good */ public class SWTTimer extends Timer { - - /** - * - */ private static final long serialVersionUID = 1L; private boolean notify = false; - int initialDelay, delay; - boolean repeats = true, coalesce = true; - - Runnable doPostEvent = null; - - Display display = null; + private int initialDelay; + private int delay; + private boolean repeats = true; + private boolean coalesce = true; + private Runnable doPostEvent = null; + private Display display = null; // These fields are maintained by TimerQueue. // eventQueued can also be reset by the TimerQueue, but will only ever // happen in applet case when TimerQueues thread is destroyed. - long expirationTime; - SWTTimer nextTimer; - boolean running; + private long expirationTime; + private SWTTimer nextTimer; + private boolean running; /** * DoPostEvent is a runnable class that fires actionEvents to the listeners @@ -69,7 +67,6 @@ */ class SWTDoPostEvent implements Runnable { public void run() { - if (notify) { fireActionPerformed(new ActionEvent(SWTTimer.this, 0, null, System.currentTimeMillis(), 0)); if (coalesce) { @@ -86,8 +83,9 @@ /** * Constructor for SWTTimer. * - * @param delay - * @param listener + * @param display display associated with this timer + * @param delay time in milliseconds between firings of this timer + * @param listener action listener to fire when the timer fires */ public SWTTimer(final Display display, final int delay, final ActionListener listener) { super(delay, listener); @@ -145,6 +143,7 @@ * * @see #setDelay * @see #getInitialDelay + * @return delay in milliseconds between firings of this timer */ public int getDelay() { return delay; @@ -171,10 +170,12 @@ } /** - * Returns the Timer's initial delay. + * Returns the Timer's initial delay. By default this is the + * same as the value returned by getDelay. * * @see #setInitialDelay * @see #setDelay + * @return the initial delay of this timer */ public int getInitialDelay() { return initialDelay; @@ -196,6 +197,7 @@ * send an action event to its listeners multiple times. * * @see #setRepeats + * @return true if this timer should repeat when completed */ public boolean isRepeats() { return repeats; @@ -229,6 +231,7 @@ * pending action events. * * @see #setCoalesce + * @return true if this timer coalesces multiple pending action events */ public boolean isCoalesce() { return coalesce; @@ -248,6 +251,7 @@ * Returns true if the Timer is running. * * @see #start + * @return true if this timer is scheduled to run */ public boolean isRunning() { return timerQueue().containsTimer(this); @@ -283,10 +287,45 @@ } synchronized void postOverride() { - if (notify == false || !coalesce) { + if (!notify || !coalesce) { notify = true; display.asyncExec(doPostEvent); } } + /** + * @param expirationTime the expirationTime to set + */ + public void setExpirationTime(final long expirationTime) { + this.expirationTime = expirationTime; + } + + /** + * @return the expirationTime + */ + public long getExpirationTime() { + return expirationTime; + } + + /** + * @param nextTimer the nextTimer to set + */ + void setNextTimer(final SWTTimer nextTimer) { + this.nextTimer = nextTimer; + } + + /** + * @return the nextTimer + */ + SWTTimer getNextTimer() { + return nextTimer; + } + + /** + * @param running the running to set + */ + public void setRunning(boolean running) { + this.running = running; + } + } diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimerQueue.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimerQueue.java index 840b04f..363219d 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimerQueue.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/SWTTimerQueue.java @@ -31,28 +31,40 @@ import org.eclipse.swt.widgets.Display; /** + * The SWTTimerQueue is a queue of timers. It has been implemented as a linked + * list of SWTTimer objects. + * * @author Lance Good */ public class SWTTimerQueue implements Runnable { - static SWTTimerQueue instance; + private static SWTTimerQueue instance; - Display display = null; + private final Display display; - SWTTimer firstTimer; - boolean running; + private SWTTimer firstTimer; + private boolean running; /** - * Constructor for TimerQueue. + * Creates a timer queue that will be attached the the provided display. + * It's Timers are expected to modify only this display, or none. + * + * @param display the display that will get updated by this queue's timers. */ public SWTTimerQueue(final Display display) { - super(); - this.display = display; // Now start the TimerQueue thread. start(); } + /** + * Returns the singleton instance of the SWTTimerQueue. Take note that even + * when called with different displays it will always return the same result + * as the first call. + * + * @param display display to associate with this Timer Queue's Activities + * @return singleton instance of SWTTimerQueue + */ public static SWTTimerQueue sharedInstance(final Display display) { if (instance == null) { instance = new SWTTimerQueue(display); @@ -60,107 +72,148 @@ return instance; } + /** + * Starts the timer queue. If it is already running, a RuntimeException will + * be thrown. + */ synchronized void start() { if (running) { - throw new RuntimeException("Can't start a TimerQueue " + "that is already running"); + throw new RuntimeException("Can't start a TimerQueue that is already running"); } - else { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - final Thread timerThread = new Thread(SWTTimerQueue.this, "TimerQueue"); - timerThread.setDaemon(true); - timerThread.setPriority(Thread.NORM_PRIORITY); - timerThread.start(); - } - }); - running = true; - } + + // Ensures that the Thread will be started from the display thread. + Display.getDefault().asyncExec(new Runnable() { + public void run() { + final Thread timerThread = new Thread(SWTTimerQueue.this, "TimerQueue"); + timerThread.setDaemon(true); + timerThread.setPriority(Thread.NORM_PRIORITY); + timerThread.start(); + } + }); + + running = true; } + /** + * Stops the TimerQueue Thread. + */ synchronized void stop() { running = false; notify(); } + /** + * Adds the provided timer to the queue of scheduled timers. + * + * @param timer timer to add + * @param expirationTime time at which the timer is to be stopped and + * removed from the queue. Given in unix time. + */ synchronized void addTimer(final SWTTimer timer, final long expirationTime) { - SWTTimer previousTimer; - SWTTimer nextTimer; + // If the Timer is already in the queue, then do nothing + if (!timer.isRunning()) { + insertTimer(timer, expirationTime); - // If the Timer is already in the queue, then ignore the add. - if (timer.running) { - return; + timer.setExpirationTime(expirationTime); + + timer.setRunning(true); + notify(); } + } - previousTimer = null; - nextTimer = firstTimer; - - // Insert the Timer into the linked list in the order they will - // expire. If two timers expire at the same time, put the newer entry - // later so they expire in the order they came in. - - while (nextTimer != null) { - if (nextTimer.expirationTime > expirationTime) { - break; - } - - previousTimer = nextTimer; - nextTimer = nextTimer.nextTimer; - } - + /** + * Insert the Timer into the queue in the order they will expire. If + * multiple timers are set to expire at the same time, it will insert it + * after the last one; that way they expire in the order they came in. + * + * @param timer timer to insert into the queue + * @param expirationTime time in UNIX time at which the new timer should + * expire + */ + private void insertTimer(final SWTTimer timer, final long expirationTime) { + SWTTimer previousTimer = findLastTimerExpiringBefore(expirationTime); if (previousTimer == null) { firstTimer = timer; } else { - previousTimer.nextTimer = timer; + timer.setNextTimer(previousTimer.getNextTimer()); + previousTimer.setNextTimer(timer); } - - timer.expirationTime = expirationTime; - timer.nextTimer = nextTimer; - timer.running = true; - notify(); } - synchronized void removeTimer(final SWTTimer timer) { - SWTTimer previousTimer; - SWTTimer nextTimer; - boolean found; + /** + * Finds the last timer that will expire before or at the given expiration + * time. If there are multiple timers expiring at the same time, the last + * one in the queue will be returned. + * + * @param expirationTime expiration to compare against timers in the queue + * @return last timer that will expire before or at the given expiration + * time + */ + private SWTTimer findLastTimerExpiringBefore(final long expirationTime) { + SWTTimer previousTimer = null; + SWTTimer nextTimer = firstTimer; - if (!timer.running) { - return; - } - - previousTimer = null; - nextTimer = firstTimer; - found = false; - - while (nextTimer != null) { - if (nextTimer == timer) { - found = true; - break; - } - + while (nextTimer != null && nextTimer.getExpirationTime() > expirationTime) { previousTimer = nextTimer; - nextTimer = nextTimer.nextTimer; + nextTimer = nextTimer.getNextTimer(); } - if (!found) { + return previousTimer; + + } + + /** + * Removes the provided timer from the Timer Queue. If it is not found, then + * nothing happens. + * + * @param timer timer to remove from the queue + */ + synchronized void removeTimer(final SWTTimer timer) { + if (!timer.isRunning()) { return; } - if (previousTimer == null) { - firstTimer = timer.nextTimer; + if (timer == firstTimer) { + firstTimer = timer.getNextTimer(); } else { - previousTimer.nextTimer = timer.nextTimer; + SWTTimer previousTimer = findLastTimerBefore(timer); + if (previousTimer != null) { + previousTimer.setNextTimer(timer.getNextTimer()); + } } - timer.expirationTime = 0; - timer.nextTimer = null; - timer.running = false; + timer.setExpirationTime(0); + timer.setNextTimer(null); + timer.setRunning(false); + } + + /** + * Finds the timer that immediately precedes the provided timer in the + * queue. + * + * @param timer to search for + * @return timer immediately preceding found timer, or null if not found + */ + private SWTTimer findLastTimerBefore(final SWTTimer timer) { + SWTTimer previousTimer = null; + SWTTimer currentTimer = firstTimer; + + while (currentTimer != null) { + if (currentTimer == timer) { + return previousTimer; + } + + previousTimer = currentTimer; + currentTimer = currentTimer.getNextTimer(); + } + + return null; } synchronized boolean containsTimer(final SWTTimer timer) { - return timer.running; + return timer.isRunning(); } /** @@ -183,7 +236,7 @@ } currentTime = System.currentTimeMillis(); - timeToWait = timer.expirationTime - currentTime; + timeToWait = timer.getExpirationTime() - currentTime; if (timeToWait <= 0) { try { @@ -211,13 +264,14 @@ wait(1); } catch (final InterruptedException e) { + } } } while (timeToWait <= 0); return timeToWait; } - + public synchronized void run() { long timeToWait; @@ -237,13 +291,18 @@ SWTTimer timer = firstTimer; while (timer != null) { timer.cancelEventOverride(); - timer = timer.nextTimer; + timer = timer.getNextTimer(); } display.asyncExec(new SWTTimerQueueRestart(display)); throw td; } } + /** + * Generates a string handy for debugging the contents of the timer queue. + * + * @return String representation of the queue for use in debugging + */ public synchronized String toString() { StringBuffer buf; SWTTimer nextTimer; @@ -255,7 +314,7 @@ while (nextTimer != null) { buf.append(nextTimer.toString()); - nextTimer = nextTimer.nextTimer; + nextTimer = nextTimer.getNextTimer(); if (nextTimer != null) { buf.append(", "); } @@ -270,26 +329,29 @@ * restart. */ protected static class SWTTimerQueueRestart implements Runnable { - boolean attemptedStart; + /** Tracks whether a restart has been attempted. */ + private boolean attemptedStart; - Display display = null; + private final Display display; public SWTTimerQueueRestart(final Display display) { this.display = display; } - public synchronized void run() { - // Only try and restart the q once. - if (!attemptedStart) { - final SWTTimerQueue q = SWTTimerQueue.sharedInstance(display); - - synchronized (q) { - if (!q.running) { - q.start(); - } - } - attemptedStart = true; + public synchronized void run() { + if (attemptedStart) { + return; } + + final SWTTimerQueue q = SWTTimerQueue.sharedInstance(display); + + synchronized (q) { + if (!q.running) { + q.start(); + } + } + + attemptedStart = true; } }