+++ /dev/null
-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.
- * <p>
- * How does it work?
- * <ul>
- * <li>it takes an ID and an associated {@link SwingWorker} and will call
- * {@link SwingWorker#execute()} after a small delay (see
- * {@link DelayWorker#DelayWorker(int)})</li>
- * <li>if a second call to {@link DelayWorker#delay(String, SwingWorker)} comes
- * with the same ID before the first one is done, it will be put on a waiting
- * queue</li>
- * <li>if a third call still with the same ID comes, its associated worker will
- * <b>replace</b> the one in the queue (only one worker per ID in the queue,
- * always the latest one)</li>
- * <li>when the first worker is done, it will check the waiting queue and
- * execute that latest worker if any</li>
- * </ul>
- *
- * @author niki
- *
- */
-@SuppressWarnings("rawtypes")
-public class DelayWorker {
- private Map<String, SwingWorker> lazyEnCours;
- private Object lazyEnCoursLock;
-
- private TreeSet<String> 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<String, SwingWorker>();
- lazyEnCoursLock = new Object();
- wip = new TreeSet<String>();
- 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<String, SwingWorker> workers = new HashMap<String, SwingWorker>();
- synchronized (lazyEnCoursLock) {
- for (String key : new ArrayList<String>(
- 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. <b>MUST
- * NOT</b> 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.
- * <p>
- * 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.
- * <p>
- * 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.
- * <p>
- * Note: this is final, you <b>MUST NOT</b> 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();
- }
- }
-}