package goko.handlers; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IProgressMonitorWithBlocking; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ProgressMonitorWrapper; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.swt.widgets.Display; /** * A progress monitor that accumulates <code>setTaskName</code>, * <code>worked</code> and <code>subtask</code> calls in the following way by * wrapping a standard progress monitor: * <ul> * <li>When a <code>setTaskName</code>, <code>worked</code> or * <code>subtask</code> call occurs the first time, the progress monitor posts a * runnable into the asynchronous SWT event queue.</li> * <li>Subsequent calls to <code>setTaskName</code>, <code>worked</code> or * <code>subtask</code> do not post a new runnable as long as a previous * runnable still exists in the SWT event queue. In this case, the progress * monitor just updates the internal state of the runnable that waits in the SWT * event queue for its execution. If no runnable exists, a new one is created * and posted into the event queue. * </ul> * <p> * This class is internal to the framework; clients outside JFace should not use * this class. * </p> */ class AccumulatingProgressMonitor extends ProgressMonitorWrapper { /** * The display. */ private Display display; /** * The collector, or <code>null</code> if none. */ private Collector collector; private String currentTask = ""; //$NON-NLS-1$ private class Collector implements Runnable { private String taskName; private String subTask; private double worked; private IProgressMonitor monitor; /** * Create a new collector. * * @param taskName * @param subTask * @param work * @param monitor */ public Collector(String taskName, String subTask, double work, IProgressMonitor monitor) { this.taskName = taskName; this.subTask = subTask; this.worked = work; this.monitor = monitor; } /** * Set the task name * * @param name */ public void setTaskName(String name) { this.taskName = name; } /** * Add worked to the work. * @param workedIncrement */ public void worked(double workedIncrement) { this.worked = this.worked + workedIncrement; } /** * Set the subTask name. * @param subTaskName */ public void subTask(String subTaskName) { this.subTask = subTaskName; } /** * Run the collector. */ @Override public void run() { clearCollector(this); if (taskName != null) { monitor.setTaskName(taskName); } if (subTask != null) { monitor.subTask(subTask); } if (worked > 0) { monitor.internalWorked(worked); } } } /** * Creates an accumulating progress monitor wrapping the given one * that uses the given display. * * @param monitor the actual progress monitor to be wrapped * @param display the SWT display used to forward the calls * to the wrapped progress monitor */ public AccumulatingProgressMonitor(IProgressMonitor monitor, Display display) { super(monitor); Assert.isNotNull(display); this.display = display; } @Override public void beginTask(final String name, final int totalWork) { synchronized (this) { collector = null; } display.asyncExec(new Runnable() { @Override public void run() { currentTask = name; getWrappedProgressMonitor().beginTask(name, totalWork); } }); } /** * Clears the collector object used to accumulate work and subtask calls if * it matches the given one. * * @param collectorToClear */ private synchronized void clearCollector(Collector collectorToClear) { // Check if the accumulator is still using the given collector. // If not, don't clear it. if (this.collector == collectorToClear) { this.collector = null; } } /** * Creates a collector object to accumulate work and subtask calls. * * @param subTask * @param work */ private void createCollector(String taskName, String subTask, double work) { collector = new Collector(taskName, subTask, work, getWrappedProgressMonitor()); display.asyncExec(collector); } @Override public void done() { synchronized (this) { collector = null; } display.asyncExec(new Runnable() { @Override public void run() { getWrappedProgressMonitor().done(); } }); } @Override public synchronized void internalWorked(final double work) { if (collector == null) { createCollector(null, null, work); } else { collector.worked(work); } } @Override public synchronized void setTaskName(final String name) { currentTask = name; if (collector == null) { createCollector(name, null, 0); } else { collector.setTaskName(name); } } @Override public synchronized void subTask(final String name) { if (collector == null) { createCollector(null, name, 0); } else { collector.subTask(name); } } @Override public synchronized void worked(int work) { internalWorked(work); } @Override public void clearBlocked() { //If this is a monitor that can report blocking do so. //Don't bother with a collector as this should only ever //happen once and prevent any more progress. final IProgressMonitor pm = getWrappedProgressMonitor(); if (!(pm instanceof IProgressMonitorWithBlocking)) { return; } display.asyncExec(new Runnable() { @Override public void run() { ((IProgressMonitorWithBlocking) pm).clearBlocked(); Dialog.getBlockedHandler().clearBlocked(); } }); } @Override public void setBlocked(final IStatus reason) { //If this is a monitor that can report blocking do so. //Don't bother with a collector as this should only ever //happen once and prevent any more progress. final IProgressMonitor pm = getWrappedProgressMonitor(); if (!(pm instanceof IProgressMonitorWithBlocking)) { return; } display.asyncExec(new Runnable() { @Override public void run() { ((IProgressMonitorWithBlocking) pm).setBlocked(reason); //Do not give a shell as we want it to block until it opens. Dialog.getBlockedHandler().showBlocked(pm, reason, currentTask); } }); } }