X-Git-Url: http://git.nikiroo.be/?p=fanfix.git;a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Futils%2Fui%2FDelayWorker.java;fp=src%2Fbe%2Fnikiroo%2Futils%2Fui%2FDelayWorker.java;h=3618b89e51e25e28c6a549181065e89b59d67468;hp=0000000000000000000000000000000000000000;hb=f19b48e27a56ebab18687debd9ef52581a03f06d;hpb=dfa4091ccd9f46687c7326aa6bf2eaf588a7cb83 diff --git a/src/be/nikiroo/utils/ui/DelayWorker.java b/src/be/nikiroo/utils/ui/DelayWorker.java new file mode 100644 index 0000000..3618b89 --- /dev/null +++ b/src/be/nikiroo/utils/ui/DelayWorker.java @@ -0,0 +1,220 @@ +package be.nikiroo.utils.ui; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeSet; + +import javax.swing.SwingWorker; + +/** + * This class helps you delay some graphical actions and execute the most recent + * ones when under contention. + *

+ * How does it work? + *

+ * + * @author niki + * + */ +@SuppressWarnings("rawtypes") +public class DelayWorker { + private Map lazyEnCours; + private Object lazyEnCoursLock; + + private TreeSet wip; + + private Object waiter; + + private boolean cont; + private boolean paused; + private Thread loop; + + /** + * Create a new {@link DelayWorker} with the given delay (in milliseconds) + * before each drain of the queue. + * + * @param delayMs + * the delay in milliseconds (can be 0, cannot be negative) + */ + public DelayWorker(final int delayMs) { + if (delayMs < 0) { + throw new IllegalArgumentException( + "A waiting delay cannot be negative"); + } + + lazyEnCours = new HashMap(); + lazyEnCoursLock = new Object(); + wip = new TreeSet(); + waiter = new Object(); + cont = true; + paused = false; + + loop = new Thread(new Runnable() { + @Override + public void run() { + while (cont) { + try { + Thread.sleep(delayMs); + } catch (InterruptedException e) { + } + + Map workers = new HashMap(); + synchronized (lazyEnCoursLock) { + for (String key : new ArrayList( + lazyEnCours.keySet())) { + if (!wip.contains(key)) { + workers.put(key, lazyEnCours.remove(key)); + } + } + } + + for (final String key : workers.keySet()) { + SwingWorker worker = workers.get(key); + + synchronized (lazyEnCoursLock) { + wip.add(key); + } + + worker.addPropertyChangeListener( + new PropertyChangeListener() { + @Override + public void propertyChange( + PropertyChangeEvent evt) { + synchronized (lazyEnCoursLock) { + wip.remove(key); + } + wakeup(); + } + }); + + // Start it, at last + worker.execute(); + } + + synchronized (waiter) { + do { + try { + if (cont) + waiter.wait(); + } catch (InterruptedException e) { + } + } while (cont && paused); + } + } + } + }); + + loop.setDaemon(true); + loop.setName("Loop for DelayWorker"); + } + + /** + * Start the internal loop that will drain the processing queue. MUST + * NOT be started twice (but see {@link DelayWorker#pause()} and + * {@link DelayWorker#resume()} instead). + */ + public void start() { + loop.start(); + } + + /** + * Pause the system until {@link DelayWorker#resume()} is called -- note + * that it will still continue on the processes currently scheduled to run, + * but will pause after that. + *

+ * Can be called even if already paused, will just do nothing in that + * context. + */ + public void pause() { + paused = true; + } + + /** + * Check if the {@link DelayWorker} is currently paused. + * + * @return TRUE if it is + */ + public boolean isPaused() { + return paused; + } + + /** + * Resume the system after a pause. + *

+ * Can be called even if already running, will just do nothing in that + * context. + */ + public void resume() { + synchronized (waiter) { + paused = false; + wakeup(); + } + } + + /** + * Stop the system. + *

+ * Note: this is final, you MUST NOT call {@link DelayWorker#start()} + * a second time (but see {@link DelayWorker#pause()} and + * {@link DelayWorker#resume()} instead). + */ + public void stop() { + synchronized (waiter) { + cont = false; + wakeup(); + } + } + + /** + * Clear all the processes that were put on the queue but not yet scheduled + * to be executed -- note that it will still continue on the processes + * currently scheduled to run. + */ + public void clear() { + synchronized (lazyEnCoursLock) { + lazyEnCours.clear(); + wip.clear(); + } + } + + /** + * Put a new process in the delay queue. + * + * @param id + * the ID of this process (if you want to skip workers when they + * are superseded by a new one, you need to use the same ID key) + * @param worker + * the process to delay + */ + public void delay(final String id, final SwingWorker worker) { + synchronized (lazyEnCoursLock) { + lazyEnCours.put(id, worker); + } + + wakeup(); + } + + /** + * Wake up the loop thread. + */ + private void wakeup() { + synchronized (waiter) { + waiter.notifyAll(); + } + } +}