# HG changeset patch
# User Frank Benoit
+ * A list of well-known context identifiers. The context identifiers use the
+ * prefix "dwtx.ui" for historical reasons. These contexts exist as part
+ * of JFace.
+ *
+ * This interface should not be implemented or extended by clients.
+ *
+ * This class is internal to the framework; clients outside JFace should not
+ * use this class.
+ *
+ * For examples of UI components which implement this interface,
+ * see
+ * Runs the given
+ * If
+ * This class is not intended to be subclassed.
+ *
+ * Code in a long-running operation should call this method
+ * regularly so that a request to cancel will be honored.
+ *
+ * Convenience for:
+ * worked
and subtask
+ * calls in the following way by wrapping a standard progress monitor:
+ *
+ *
+ * worked
or subtask
call occurs the first time,
+ * the progress monitor posts a runnable into the asynchronous DWT event queue.
+ * worked
or subtask
do not post
+ * a new runnable as long as a previous runnable still exists in the DWT event
+ * queue. In this case, the progress monitor just updates the internal state of
+ * the runnable that waits in the DWT event queue for its execution. If no runnable
+ * exists, a new one is created and posted into the event queue.
+ * null
if none.
+ */
+ private Collector collector;
+
+ private String currentTask = ""; //$NON-NLS-1$
+
+ private class Collector : Runnable {
+ private String subTask_;
+
+ private double worked_;
+
+ private IProgressMonitor monitor;
+
+ /**
+ * Create a new collector.
+ * @param subTask
+ * @param work
+ * @param monitor
+ */
+ public this(String subTask_, double work, IProgressMonitor monitor) {
+ this.subTask_ = subTask_;
+ this.worked_ = work;
+ this.monitor = monitor;
+ }
+
+ /**
+ * 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.
+ */
+ public void run() {
+ clearCollector(this);
+ if (subTask_ !is 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 DWT display used to forward the calls
+ * to the wrapped progress monitor
+ */
+ public this(IProgressMonitor monitor, Display display) {
+ super(monitor);
+ Assert.isNotNull(display);
+ this.display = display;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IProgressMonitor.
+ */
+ public void beginTask(String name, int totalWork) {
+ synchronized (this) {
+ collector = null;
+ }
+ display.syncExec(new class Runnable {
+ 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 is collectorToClear) {
+ this.collector = null;
+ }
+ }
+
+ /**
+ * Creates a collector object to accumulate work and subtask calls.
+ * @param subTask
+ * @param work
+ */
+ private void createCollector(String subTask, double work) {
+ collector = new Collector(subTask, work, getWrappedProgressMonitor());
+ display.asyncExec(collector);
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IProgressMonitor.
+ */
+ public void done() {
+ synchronized (this) {
+ collector = null;
+ }
+ display.syncExec(new class Runnable {
+ public void run() {
+ getWrappedProgressMonitor().done();
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IProgressMonitor.
+ */
+ public synchronized void internalWorked(double work) {
+ if (collector is null) {
+ createCollector(null, work);
+ } else {
+ collector.worked(work);
+ }
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IProgressMonitor.
+ */
+ public void setTaskName(String name) {
+ synchronized (this) {
+ collector = null;
+ }
+ display.syncExec(new class Runnable {
+ public void run() {
+ currentTask = name;
+ getWrappedProgressMonitor().setTaskName(name);
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IProgressMonitor.
+ */
+ public synchronized void subTask(String name) {
+ if (collector is null) {
+ createCollector(name, 0);
+ } else {
+ collector.subTask(name);
+ }
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IProgressMonitor.
+ */
+ public synchronized void worked(int work) {
+ internalWorked(work);
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.core.runtime.ProgressMonitorWrapper#clearBlocked()
+ */
+ 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.
+ IProgressMonitor pm = getWrappedProgressMonitor();
+ if (!(cast(IProgressMonitorWithBlocking)pm )) {
+ return;
+ }
+
+ display.asyncExec(new class Runnable {
+ IProgressMonitor pm_;
+ this(){ pm_=pm; }
+ /* (non-Javadoc)
+ * @see java.lang.Runnable#run()
+ */
+ public void run() {
+ (cast(IProgressMonitorWithBlocking) pm_).clearBlocked();
+//FIXME
+// Dialog.getBlockedHandler().clearBlocked();
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.core.runtime.ProgressMonitorWrapper#setBlocked(dwtx.core.runtime.IStatus)
+ */
+ public void setBlocked(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.
+ IProgressMonitor pm = getWrappedProgressMonitor();
+ if (!(cast(IProgressMonitorWithBlocking)pm )) {
+ return;
+ }
+
+ display.asyncExec(new class Runnable {
+ IProgressMonitor pm_;
+ this(){ pm_=pm; }
+ /* (non-Javadoc)
+ * @see java.lang.Runnable#run()
+ */
+ public void run() {
+ (cast(IProgressMonitorWithBlocking) pm_).setBlocked(reason);
+ //Do not give a shell as we want it to block until it opens.
+//FIXME
+// Dialog.getBlockedHandler().showBlocked(pm, reason, currentTask);
+ }
+ });
+ }
+}
diff -r 103ab03b77eb -r 1a6747be662d dwtx/jface/operation/IRunnableContext.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/operation/IRunnableContext.d Fri Mar 28 19:31:01 2008 +0100
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ * Frank Benoit IRunnableWithProgress
.
+ * The context is responsible for displaying a progress indicator and Cancel
+ * button to the end user while the operation is in progress; the context
+ * supplies a progress monitor to be used from code running inside the operation.
+ * Note that an IRunnableContext
is not a runnable itself.
+ * ApplicationWindow
, ProgressMonitorDialog
,
+ * and WizardDialog
.
+ * IRunnableWithProgress
in this context.
+ * For example, if this is a ProgressMonitorDialog
then the runnable
+ * is run using this dialog's progress monitor.
+ * fork
is false
, the current thread is used
+ * to run the runnable. Note that if fork
is true
,
+ * it is unspecified whether or not this method blocks until the runnable
+ * has been run. Implementers should document whether the runnable is run
+ * synchronously (blocking) or asynchronously (non-blocking), or if no
+ * assumption can be made about the blocking behaviour.
+ * true
if the runnable should be run in a separate thread,
+ * and false
to run in the same thread
+ * @param cancelable true
to enable the cancelation, and
+ * false
to make the operation uncancellable
+ * @param runnable the runnable to run
+ *
+ * @exception InvocationTargetException wraps any exception or error which occurs
+ * while running the runnable
+ * @exception InterruptedException propagated by the context if the runnable
+ * acknowledges cancelation by throwing this exception. This should not be thrown
+ * if cancelable is false
.
+ */
+ public void run(bool fork, bool cancelable,
+ IRunnableWithProgress runnable);
+}
diff -r 103ab03b77eb -r 1a6747be662d dwtx/jface/operation/IRunnableWithProgress.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/operation/IRunnableWithProgress.d Fri Mar 28 19:31:01 2008 +0100
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ * Frank Benoit IRunnableWithProgress
interface should be implemented by any
+ * class whose instances are intended to be executed as a long-running operation.
+ * Long-running operations are typically presented at the UI via a modal dialog
+ * showing a progress indicator and a Cancel button.
+ * The class must define a run
method that takes a progress monitor.
+ * The run
method is usually not invoked directly, but rather by
+ * passing the IRunnableWithProgress
to the run
method of
+ * an IRunnableContext
, which provides the UI for the progress monitor
+ * and Cancel button.
+ *
+ * @see IRunnableContext
+ */
+public interface IRunnableWithProgress {
+ /**
+ * Runs this operation. Progress should be reported to the given progress monitor.
+ * This method is usually invoked by an IRunnableContext
's run
method,
+ * which supplies the progress monitor.
+ * A request to cancel the operation should be honored and acknowledged
+ * by throwing InterruptedException
.
+ *
+ * @param monitor the progress monitor to use to display progress and receive
+ * requests for cancelation
+ * @exception InvocationTargetException if the run method must propagate a checked exception,
+ * it should wrap it inside an InvocationTargetException
; runtime exceptions are automatically
+ * wrapped in an InvocationTargetException
by the calling context
+ * @exception InterruptedException if the operation detects a request to cancel,
+ * using IProgressMonitor.isCanceled()
, it should exit by throwing
+ * InterruptedException
+ *
+ * @see IRunnableContext#run
+ */
+ public void run(IProgressMonitor monitor);
+}
diff -r 103ab03b77eb -r 1a6747be662d dwtx/jface/operation/IThreadListener.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/operation/IThreadListener.d Fri Mar 28 19:31:01 2008 +0100
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ * Frank Benoit run
method is executed in a
+ * separate thread, depending on the value of the passed fork argument.
+ * If the runnable is executed in a separate thread then the current thread
+ * either waits until the new thread ends or, if the current thread is the
+ * UI thread, it polls the DWT event queue and dispatches each event.
+ * false
by default.
+ */
+ private static bool debug_ = false;
+
+ /**
+ * The number of nested modal runs, or 0 if not inside a modal run.
+ * This is global state.
+ */
+ private static int modalLevel = 0;
+
+ /**
+ * Indicates whether operations should be run in a separate thread.
+ * Defaults to true.
+ * For internal debugging use, set to false to run operations in the calling thread.
+ */
+ private static bool runInSeparateThread = true;
+
+ /**
+ * Thread which runs the modal context.
+ */
+ private static class ModalContextThread : Thread {
+ /**
+ * The operation to be run.
+ */
+ private IRunnableWithProgress runnable;
+
+ /**
+ * The exception thrown by the operation starter.
+ */
+ private Exception throwable;
+
+ /**
+ * The progress monitor used for progress and cancelation.
+ */
+ private IProgressMonitor progressMonitor;
+
+ /**
+ * The display used for event dispatching.
+ */
+ private Display display;
+
+ /**
+ * Indicates whether to continue event queue dispatching.
+ */
+ private /+volatile+/ bool continueEventDispatching = true;
+
+ /**
+ * The thread that forked this modal context thread.
+ *
+ * @since 3.1
+ */
+ private Thread callingThread;
+
+ /**
+ * Creates a new modal context.
+ *
+ * @param operation the runnable to run
+ * @param monitor the progress monitor to use to display progress and receive
+ * requests for cancelation
+ * @param display the display to be used to read and dispatch events
+ */
+ private this(IRunnableWithProgress operation,
+ IProgressMonitor monitor, Display display) {
+ super(/+"ModalContext"+/); //$NON-NLS-1$
+ Assert.isTrue(monitor !is null && display !is null);
+ runnable = operation;
+ progressMonitor = new AccumulatingProgressMonitor(monitor, display);
+ this.display = display;
+ this.callingThread = Thread.getThis();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Thread.
+ */
+ public void run() {
+ try {
+ if (runnable !is null) {
+ runnable.run(progressMonitor);
+ }
+ /+
+ } catch (InvocationTargetException e) {
+ throwable = e;
+ } catch (InterruptedException e) {
+ throwable = e;
+ } catch (RuntimeException e) {
+ throwable = e;
+ } catch (ThreadDeath e) {
+ // Make sure to propagate ThreadDeath, or threads will never fully terminate
+ throw e;
+ +/
+ } catch (/+Error+/Exception e) {
+ throwable = e;
+ } finally {
+ //notify the operation of change of thread of control
+ if ( auto tl = cast(IThreadListener)runnable ) {
+ tl.threadChange(callingThread);
+ }
+
+ // Make sure that all events in the asynchronous event queue
+ // are dispatched.
+ display.syncExec(new class() Runnable {
+ public void run() {
+ // do nothing
+ }
+ });
+
+ // Stop event dispatching
+ continueEventDispatching = false;
+
+ // Force the event loop to return from sleep () so that
+ // it stops event dispatching.
+ display.asyncExec(null);
+ }
+ }
+
+ /**
+ * Processes events or waits until this modal context thread terminates.
+ */
+ public void block() {
+ if (display is Display.getCurrent()) {
+ while (continueEventDispatching) {
+ // Run the event loop. Handle any uncaught exceptions caused
+ // by UI events.
+ try {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ /+
+ // ThreadDeath is a normal error when the thread is dying. We must
+ // propagate it in order for it to properly terminate.
+ catch (ThreadDeath e) {
+ throw (e);
+ }
+ +/
+ // For all other exceptions, log the problem.
+ catch (Exception e) {
+ Stderr.formatln("Unhandled event loop exception during blocked modal context."); //$NON-NLS-1$
+ ExceptionPrintStackTrace(e);
+ }
+ }
+ } else {
+ try {
+ join();
+ } catch (Exception e) {
+ throwable = e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns whether the first progress monitor is the same as, or
+ * a wrapper around, the second progress monitor.
+ *
+ * @param monitor1 the first progress monitor
+ * @param monitor2 the second progress monitor
+ * @return true
if the first is the same as, or
+ * a wrapper around, the second
+ * @see ProgressMonitorWrapper
+ */
+ public static bool canProgressMonitorBeUsed(IProgressMonitor monitor1,
+ IProgressMonitor monitor2) {
+ if (monitor1 is monitor2) {
+ return true;
+ }
+
+ while ( cast(ProgressMonitorWrapper)monitor1 ) {
+ monitor1 = (cast(ProgressMonitorWrapper)monitor1)
+ .getWrappedProgressMonitor();
+ if (monitor1 is monitor2) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks with the given progress monitor and throws
+ * InterruptedException
if it has been canceled.
+ *
+ * if (monitor.isCanceled())
+ * throw new InterruptedException();
+ *
+ *
+ * The modal nesting level increases by one each time the
+ * ModalContext.run
method is called within the
+ * dynamic scope of another call to ModalContext.run
.
+ *
0
if
+ * this method is called outside the dynamic scope of any
+ * invocation of ModalContext.run
+ */
+ public static int getModalLevel() {
+ return modalLevel;
+ }
+
+ /**
+ * Returns whether the given thread is running a modal context.
+ *
+ * @param thread The thread to be checked
+ * @return true
if the given thread is running a modal context, false
if not
+ */
+ public static bool isModalContextThread(Thread thread) {
+ return (cast(ModalContextThread)thread) !is null;
+ }
+
+ /**
+ * Runs the given runnable in a modal context, passing it a progress monitor.
+ * + * The modal nesting level is increased by one from the perspective + * of the given runnable. + *
+ *
+ * If the supplied operation implements IThreadListener
, it
+ * will be notified of any thread changes required to execute the operation.
+ * Specifically, the operation will be notified of the thread that will call its
+ * run
method before it is called, and will be notified of the
+ * change of control back to the thread calling this method when the operation
+ * completes. These thread change notifications give the operation an
+ * opportunity to transfer any thread-local state to the execution thread before
+ * control is transferred to the new thread.
+ *
true
if the runnable should run in a separate thread,
+ * and false
if in the same thread
+ * @param monitor the progress monitor to use to display progress and receive
+ * requests for cancelation
+ * @param display the display to be used to read and dispatch events
+ * @exception InvocationTargetException if the run method must propagate a checked exception,
+ * it should wrap it inside an InvocationTargetException
; runtime exceptions and errors are automatically
+ * wrapped in an InvocationTargetException
by this method
+ * @exception InterruptedException if the operation detects a request to cancel,
+ * using IProgressMonitor.isCanceled()
, it should exit by throwing
+ * InterruptedException
; this method propagates the exception
+ */
+ public static void run(IRunnableWithProgress operation, bool fork,
+ IProgressMonitor monitor, Display display) {
+ Assert.isTrue(operation !is null && monitor !is null);
+
+ modalLevel++;
+ try {
+ if (monitor !is null) {
+ monitor.setCanceled(false);
+ }
+ // Is the runnable supposed to be execute in the same thread.
+ if (!fork || !runInSeparateThread) {
+ runInCurrentThread(operation, monitor);
+ } else {
+ ModalContextThread t = getCurrentModalContextThread();
+ if (t !is null) {
+ Assert.isTrue(canProgressMonitorBeUsed(monitor,
+ t.progressMonitor));
+ runInCurrentThread(operation, monitor);
+ } else {
+ t = new ModalContextThread(operation, monitor, display);
+ if ( auto tl = cast(IThreadListener)operation ) {
+ tl.threadChange(t);
+ }
+ t.start();
+ t.block();
+ Exception throwable = t.throwable;
+ if (throwable !is null) {
+ if (debug_
+ && !(cast(InterruptedException)throwable )
+ && !(cast(OperationCanceledException)throwable )) {
+ Stderr.formatln("Exception in modal context operation:"); //$NON-NLS-1$
+ ExceptionPrintStackTrace(throwable);
+ Stderr.formatln("Called from:"); //$NON-NLS-1$
+ // Don't create the InvocationTargetException on the throwable,
+ // otherwise it will print its stack trace (from the other thread).
+ ExceptionPrintStackTrace( new InvocationTargetException(null));
+ }
+ if (cast(InvocationTargetException)throwable ) {
+ throw cast(InvocationTargetException) throwable;
+ } else if (cast(InterruptedException)throwable ) {
+ throw cast(InterruptedException) throwable;
+ } else if (cast(OperationCanceledException)throwable ) {
+ // See 1GAN3L5: ITPUI:WIN2000 - ModalContext converts OperationCancelException into InvocationTargetException
+ throw new InterruptedException(throwable
+ .msg);
+ } else {
+ throw new InvocationTargetException(throwable);
+ }
+ }
+ }
+ }
+ } finally {
+ modalLevel--;
+ }
+ }
+
+ /**
+ * Run a runnable. Convert all thrown exceptions to
+ * either InterruptedException or InvocationTargetException
+ */
+ private static void runInCurrentThread(IRunnableWithProgress runnable,
+ IProgressMonitor progressMonitor) {
+ try {
+ if (runnable !is null) {
+ runnable.run(progressMonitor);
+ }
+ } catch (InvocationTargetException e) {
+ throw e;
+ } catch (InterruptedException e) {
+ throw e;
+ } catch (OperationCanceledException e) {
+ throw new InterruptedException();
+ /+
+ } catch (ThreadDeath e) {
+ // Make sure to propagate ThreadDeath, or threads will never fully terminate
+ throw e;
+ +/
+ } catch (RuntimeException e) {
+ throw new InvocationTargetException(e);
+ } catch (/+Error+/Exception e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+
+ /**
+ * Sets whether ModalContext is running in debug mode.
+ *
+ * @param debugMode true
for debug mode,
+ * and false
for normal mode (the default)
+ */
+ public static void setDebugMode(bool debugMode) {
+ debug_ = debugMode;
+ }
+
+ /**
+ * Sets whether ModalContext may process events (by calling Display.readAndDispatch()
)
+ * while running operations. By default, ModalContext will process events while running operations.
+ * Use this method to disallow event processing temporarily.
+ * @param allowReadAndDispatch true
(the default) if events may be processed while
+ * running an operation, false
if Display.readAndDispatch() should not be called
+ * from ModalContext.
+ * @since 3.2
+ */
+ public static void setAllowReadAndDispatch(bool allowReadAndDispatch) {
+ // use a separate thread if and only if it is OK to spin the event loop
+ runInSeparateThread = allowReadAndDispatch;
+ }
+}