Mercurial > projects > dwt-addons
changeset 6:1a6747be662d
Jface operations
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Fri, 28 Mar 2008 19:31:01 +0100 |
parents | 103ab03b77eb |
children | 8a302fdb4140 |
files | dwtx/jface/contexts/IContextIds.d dwtx/jface/internal/InternalPolicy.d dwtx/jface/operation/AccumulatingProgressMonitor.d dwtx/jface/operation/IRunnableContext.d dwtx/jface/operation/IRunnableWithProgress.d dwtx/jface/operation/IThreadListener.d dwtx/jface/operation/ModalContext.d |
diffstat | 7 files changed, 929 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/contexts/IContextIds.d Fri Mar 28 19:31:01 2008 +0100 @@ -0,0 +1,48 @@ +/******************************************************************************* + * 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 <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.contexts.IContextIds; + +import dwt.dwthelper.utils; + +/** + * <p> + * 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. + * </p> + * <p> + * This interface should not be implemented or extended by clients. + * </p> + * + * @since 3.1 + */ +public interface IContextIds { + + /** + * The identifier for the context that is active when a shell registered as + * a dialog. + */ + public static const String CONTEXT_ID_DIALOG = "dwtx.ui.contexts.dialog"; //$NON-NLS-1$ + + /** + * The identifier for the context that is active when a shell is registered + * as either a window or a dialog. + */ + public static const String CONTEXT_ID_DIALOG_AND_WINDOW = "dwtx.ui.contexts.dialogAndWindow"; //$NON-NLS-1$ + + /** + * The identifier for the context that is active when a shell is registered + * as a window. + */ + public static const String CONTEXT_ID_WINDOW = "dwtx.ui.contexts.window"; //$NON-NLS-1$ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/internal/InternalPolicy.d Fri Mar 28 19:31:01 2008 +0100 @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2007 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 <benoit@tionex.de> + ******************************************************************************/ +module dwtx.jface.internal.InternalPolicy; + +import tango.util.collection.model.Map; + +/** + * Internal class used for non-API debug flags. + * + * @since 3.3 + */ +public class InternalPolicy { + + /** + * (NON-API) A flag to indicate whether reentrant viewer calls should always be + * logged. If false, only the first reentrant call will cause a log entry. + * + * @since 3.3 + */ + public static bool DEBUG_LOG_REENTRANT_VIEWER_CALLS = false; + + /** + * (NON-API) Instead of logging current conflicts they can be + * held here. If there is a problem, they can be reported then. + */ + public static Map!(Object/+???+/,Object/+???+/) currentConflicts = null; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/operation/AccumulatingProgressMonitor.d Fri Mar 28 19:31:01 2008 +0100 @@ -0,0 +1,272 @@ +/******************************************************************************* + * 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 <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.operation.AccumulatingProgressMonitor; + + +import dwt.widgets.Display; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.IProgressMonitor; +import dwtx.core.runtime.IProgressMonitorWithBlocking; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.ProgressMonitorWrapper; +//FIXME +// import dwtx.jface.dialogs.Dialog; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * A progress monitor that accumulates <code>worked</code> and <code>subtask</code> + * calls in the following way by wrapping a standard progress monitor: + * <ul> + * <li> When a <code>worked</code> or <code>subtask</code> call occurs the first time, + * the progress monitor posts a runnable into the asynchronous DWT event queue. + * </li> + * <li> Subsequent calls to <code>worked</code> or <code>subtask</code> 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. + * </ul> + * <p> + * This class is internal to the framework; clients outside JFace should not + * use this class. + * </p> + */ +/* package */class AccumulatingProgressMonitor : 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 : 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); + } + }); + } +}
--- /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 <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.operation.IRunnableContext; + +import dwtx.jface.operation.IRunnableWithProgress; + +import dwt.dwthelper.utils; + +/** + * Interface for UI components which can execute a long-running operation + * in the form of an <code>IRunnableWithProgress</code>. + * 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 <code>IRunnableContext</code> is not a runnable itself. + * <p> + * For examples of UI components which implement this interface, + * see <code>ApplicationWindow</code>, <code>ProgressMonitorDialog</code>, + * and <code>WizardDialog</code>. + * </p> + * + * @see IRunnableWithProgress + * @see dwtx.jface.window.ApplicationWindow + * @see dwtx.jface.dialogs.ProgressMonitorDialog + * @see dwtx.jface.wizard.WizardDialog + */ +public interface IRunnableContext { + /** + * <p> + * Runs the given <code>IRunnableWithProgress</code> in this context. + * For example, if this is a <code>ProgressMonitorDialog</code> then the runnable + * is run using this dialog's progress monitor. + * </p> + * <p> + * If <code>fork</code> is <code>false</code>, the current thread is used + * to run the runnable. Note that if <code>fork</code> is <code>true</code>, + * 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. + * </p> + * + * @param fork <code>true</code> if the runnable should be run in a separate thread, + * and <code>false</code> to run in the same thread + * @param cancelable <code>true</code> to enable the cancelation, and + * <code>false</code> 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 <code>false</code>. + */ + public void run(bool fork, bool cancelable, + IRunnableWithProgress runnable); +}
--- /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 <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.operation.IRunnableWithProgress; + +import dwtx.core.runtime.IProgressMonitor; + +import dwt.dwthelper.utils; + +/** + * The <code>IRunnableWithProgress</code> 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 <code>run</code> method that takes a progress monitor. + * The <code>run</code> method is usually not invoked directly, but rather by + * passing the <code>IRunnableWithProgress</code> to the <code>run</code> method of + * an <code>IRunnableContext</code>, 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 <code>IRunnableContext</code>'s <code>run</code> method, + * which supplies the progress monitor. + * A request to cancel the operation should be honored and acknowledged + * by throwing <code>InterruptedException</code>. + * + * @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 <code>InvocationTargetException</code>; runtime exceptions are automatically + * wrapped in an <code>InvocationTargetException</code> by the calling context + * @exception InterruptedException if the operation detects a request to cancel, + * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing + * <code>InterruptedException</code> + * + * @see IRunnableContext#run + */ + public void run(IProgressMonitor monitor); +}
--- /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 <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.operation.IThreadListener; + +import dwt.dwthelper.utils; +import tango.core.Thread; + +/** + * A thread listener is an object that is interested in receiving notifications + * of thread changes. For example, a thread listener can be used to notify a + * runnable of the thread that will execute it, allowing the runnable to transfer + * thread-local state from the calling thread before control passes to the new thread. + * + * @since 3.1 + */ +public interface IThreadListener { + /** + * Notification that a thread change is occurring. + * + * @param thread The new thread + */ + public void threadChange(Thread thread); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/operation/ModalContext.d Fri Mar 28 19:31:01 2008 +0100 @@ -0,0 +1,420 @@ +/******************************************************************************* + * 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 <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.operation.ModalContext; + +import dwt.widgets.Display; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.IProgressMonitor; +import dwtx.core.runtime.OperationCanceledException; +import dwtx.core.runtime.ProgressMonitorWrapper; + +import dwtx.jface.operation.IThreadListener; +import dwtx.jface.operation.IRunnableWithProgress; +import dwtx.jface.operation.AccumulatingProgressMonitor; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; +import tango.core.Thread; +import tango.io.Stdout; + +/** + * Utility class for supporting modal operations. + * The runnable passed to the <code>run</code> 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. + * <p> + * This class is not intended to be subclassed. + * </p> + */ +public class ModalContext { + + /** + * Indicated whether ModalContext is in debug mode; + * <code>false</code> 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 <code>true</code> 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 + * <code>InterruptedException</code> if it has been canceled. + * <p> + * Code in a long-running operation should call this method + * regularly so that a request to cancel will be honored. + * </p> + * <p> + * Convenience for: + * <pre> + * if (monitor.isCanceled()) + * throw new InterruptedException(); + * </pre> + * </p> + * + * @param monitor the progress monitor + * @exception InterruptedException if cancelling the operation has been requested + * @see IProgressMonitor#isCanceled() + */ + public static void checkCanceled(IProgressMonitor monitor) { + if (monitor.isCanceled()) { + throw new InterruptedException(); + } + } + + /** + * Returns the currently active modal context thread, or null if no modal context is active. + */ + private static ModalContextThread getCurrentModalContextThread() { + Thread t = Thread.getThis(); + if ( auto r = cast(ModalContextThread)t ) { + return r; + } + return null; + } + + /** + * Returns the modal nesting level. + * <p> + * The modal nesting level increases by one each time the + * <code>ModalContext.run</code> method is called within the + * dynamic scope of another call to <code>ModalContext.run</code>. + * </p> + * + * @return the modal nesting level, or <code>0</code> if + * this method is called outside the dynamic scope of any + * invocation of <code>ModalContext.run</code> + */ + public static int getModalLevel() { + return modalLevel; + } + + /** + * Returns whether the given thread is running a modal context. + * + * @param thread The thread to be checked + * @return <code>true</code> if the given thread is running a modal context, <code>false</code> 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. + * <p> + * The modal nesting level is increased by one from the perspective + * of the given runnable. + * </p> + *<p> + * If the supplied operation implements <code>IThreadListener</code>, 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 + * <code>run</code> 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. + *</p> + * @param operation the runnable to run + * @param fork <code>true</code> if the runnable should run in a separate thread, + * and <code>false</code> 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 <code>InvocationTargetException</code>; runtime exceptions and errors are automatically + * wrapped in an <code>InvocationTargetException</code> by this method + * @exception InterruptedException if the operation detects a request to cancel, + * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing + * <code>InterruptedException</code>; 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 <code>true</code> for debug mode, + * and <code>false</code> for normal mode (the default) + */ + public static void setDebugMode(bool debugMode) { + debug_ = debugMode; + } + + /** + * Sets whether ModalContext may process events (by calling <code>Display.readAndDispatch()</code>) + * while running operations. By default, ModalContext will process events while running operations. + * Use this method to disallow event processing temporarily. + * @param allowReadAndDispatch <code>true</code> (the default) if events may be processed while + * running an operation, <code>false</code> 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; + } +}