# HG changeset patch # User Frank Benoit # Date 1206729061 -3600 # Node ID 1a6747be662dc2bf31e45b94a00456e4bb98ecee # Parent 103ab03b77eb04d11308c7cb57e051d141264234 Jface operations diff -r 103ab03b77eb -r 1a6747be662d dwtx/jface/contexts/IContextIds.d --- /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 + *******************************************************************************/ +module dwtx.jface.contexts.IContextIds; + +import dwt.dwthelper.utils; + +/** + *

+ * 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. + *

+ * + * @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$ +} diff -r 103ab03b77eb -r 1a6747be662d dwtx/jface/internal/InternalPolicy.d --- /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 + ******************************************************************************/ +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; +} diff -r 103ab03b77eb -r 1a6747be662d dwtx/jface/operation/AccumulatingProgressMonitor.d --- /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 + *******************************************************************************/ +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 worked and subtask + * calls in the following way by wrapping a standard progress monitor: + *
    + *
  • When a worked or subtask call occurs the first time, + * the progress monitor posts a runnable into the asynchronous DWT event queue. + *
  • + *
  • Subsequent calls to 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. + *
+ *

+ * This class is internal to the framework; clients outside JFace should not + * use this class. + *

+ */ +/* package */class AccumulatingProgressMonitor : ProgressMonitorWrapper { + + /** + * The display. + */ + private Display display; + + /** + * The collector, or 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 + *******************************************************************************/ +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 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. + *

+ * For examples of UI components which implement this interface, + * see ApplicationWindow, ProgressMonitorDialog, + * and WizardDialog. + *

+ * + * @see IRunnableWithProgress + * @see dwtx.jface.window.ApplicationWindow + * @see dwtx.jface.dialogs.ProgressMonitorDialog + * @see dwtx.jface.wizard.WizardDialog + */ +public interface IRunnableContext { + /** + *

+ * Runs the given IRunnableWithProgress in this context. + * For example, if this is a ProgressMonitorDialog then the runnable + * is run using this dialog's progress monitor. + *

+ *

+ * If 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. + *

+ * + * @param fork 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 + *******************************************************************************/ +module dwtx.jface.operation.IRunnableWithProgress; + +import dwtx.core.runtime.IProgressMonitor; + +import dwt.dwthelper.utils; + +/** + * The 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 + *******************************************************************************/ +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); +} diff -r 103ab03b77eb -r 1a6747be662d dwtx/jface/operation/ModalContext.d --- /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 + *******************************************************************************/ +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 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. + *

+ * This class is not intended to be subclassed. + *

+ */ +public class ModalContext { + + /** + * Indicated whether ModalContext is in debug mode; + * 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. + *

+ * Code in a long-running operation should call this method + * regularly so that a request to cancel will be honored. + *

+ *

+ * Convenience for: + *

+     * if (monitor.isCanceled())
+     *    throw new InterruptedException();
+     * 
+ *

+ * + * @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. + *

+ * 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. + *

+ * + * @return the modal nesting level, or 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. + *

+ * @param operation the runnable to run + * @param fork 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; + } +}