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(String id, SwingWorker worker) { synchronized (lazyEnCoursLock) { lazyEnCours.put(id, worker); } wakeup(); } /** * Wake up the loop thread. */ private void wakeup() { synchronized (waiter) { waiter.notifyAll(); } } }