diff dwtx/jface/operation/ModalContext.d @ 70:46a6e0e6ccd4

Merge with d-fied sources of 3.4M7
author Frank Benoit <benoit@tionex.de>
date Thu, 22 May 2008 01:36:46 +0200
parents 84ce9636d109
children 4878bef4a38e
line wrap: on
line diff
--- a/dwtx/jface/operation/ModalContext.d	Mon May 19 13:41:06 2008 +0200
+++ b/dwtx/jface/operation/ModalContext.d	Thu May 22 01:36:46 2008 +0200
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * Copyright (c) 2000, 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
@@ -15,8 +15,11 @@
 import dwt.widgets.Display;
 import dwtx.core.runtime.Assert;
 import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
 import dwtx.core.runtime.OperationCanceledException;
 import dwtx.core.runtime.ProgressMonitorWrapper;
+import dwtx.core.runtime.Status;
+import dwtx.jface.util.Policy;
 
 import dwtx.jface.operation.IThreadListener;
 import dwtx.jface.operation.IRunnableWithProgress;
@@ -28,12 +31,12 @@
 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.
+ * 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>
@@ -41,21 +44,21 @@
 public class ModalContext {
 
     /**
-     * Indicated whether ModalContext is in debug mode;
-     * <code>false</code> by default.
+     * Indicated whether ModalContext is in debug mode; <code>false</code> by
+     * default.
      */
     private static bool debug_ = true;
 
     /**
-     * The number of nested modal runs, or 0 if not inside a modal run.
-     * This is global state.
+     * 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.
+     * 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;
 
@@ -98,10 +101,13 @@
         /**
          * 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
+         * @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) {
@@ -113,8 +119,8 @@
             this.callingThread = Thread.getThis();
         }
 
-        /* (non-Javadoc)
-         * Method declared on Thread.
+        /*
+         * (non-Javadoc) Method declared on Thread.
          */
         public /+override+/ void run2() {
             try {
@@ -129,15 +135,21 @@
             } catch (RuntimeException e) {
                 throwable = e;
             } catch (ThreadDeath e) {
-                // Make sure to propagate ThreadDeath, or threads will never fully terminate
+                // 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
+                // notify the operation of change of thread of control
                 if ( auto tl = cast(IThreadListener)runnable ) {
-                    tl.threadChange(callingThread);
+                    auto exception = 
+                        invokeThreadListener(tl, callingThread);
+                    
+                    //Forward it if we don't already have one
+                    if(exception !is null && throwable is null)
+                        throwable = exception;
                 }
 
                 // Make sure that all events in the asynchronous event queue
@@ -163,7 +175,7 @@
         public void block() {
             if (display is Display.getCurrent()) {
                 while (continueEventDispatching) {
-                    // Run the event loop.  Handle any uncaught exceptions caused
+                    // Run the event loop. Handle any uncaught exceptions caused
                     // by UI events.
                     try {
                         if (!display.readAndDispatch()) {
@@ -171,7 +183,8 @@
                         }
                     }
                     /+
-                    // ThreadDeath is a normal error when the thread is dying.  We must
+                    // 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);
@@ -179,8 +192,14 @@
                     +/
                     // 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);
+                        Policy
+                                .getLog()
+                                .log(
+                                        new Status(
+                                                IStatus.ERROR,
+                                                Policy.JFACE,
+                                                "Unhandled event loop exception during blocked modal context.",//$NON-NLS-1$
+                                                e));
                     }
                 }
             } else {
@@ -194,13 +213,15 @@
     }
 
     /**
-     * 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
+     * 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,
@@ -223,19 +244,23 @@
      * 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.
+     * 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();
+     *  throw new InterruptedException();
      * </pre>
+     * 
      * </p>
-     *
-     * @param monitor the progress monitor
-     * @exception InterruptedException if cancelling the operation has been requested
+     * 
+     * @param monitor
+     *            the progress monitor
+     * @exception InterruptedException
+     *                if cancelling the operation has been requested
      * @see IProgressMonitor#isCanceled()
      */
     public static void checkCanceled(IProgressMonitor monitor) {
@@ -245,7 +270,8 @@
     }
 
     /**
-     * Returns the currently active modal context thread, or null if no modal context is active.
+     * Returns the currently active modal context thread, or null if no modal
+     * context is active.
      */
     private static ModalContextThread getCurrentModalContextThread() {
         Thread t = Thread.getThis();
@@ -259,13 +285,13 @@
      * 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>.
+     * <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>
+     * 
+     * @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;
@@ -274,41 +300,54 @@
     /**
      * 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
+     * @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.
+     * 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.
+     * The modal nesting level is increased by one from the perspective of the
+     * given runnable.
      * </p>
-     *<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
+     * 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) {
@@ -330,11 +369,19 @@
                     runInCurrentThread(operation, monitor);
                 } else {
                     t = new ModalContextThread(operation, monitor, display);
+                    Exception listenerException = null;
                     if ( auto tl = cast(IThreadListener)operation ) {
-                        tl.threadChange(t);
+                        listenerException = invokeThreadListener(tl, t);
                     }
-                    t.start();
-                    t.block();
+                    
+                    if(listenerException is null){
+                        t.start();
+                        t.block();
+                    }
+                    else {
+                        if(t.throwable is null)
+                            t.throwable = listenerException;
+                    }
                     Exception throwable = t.throwable;
                     if (throwable !is null) {
                         if (debug_
@@ -343,8 +390,10 @@
                             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).
+                            // 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 ) {
@@ -352,7 +401,9 @@
                         } else if (cast(InterruptedException)throwable ) {
                             throw cast(InterruptedException) throwable;
                         } else if (cast(OperationCanceledException)throwable ) {
-                            // See 1GAN3L5: ITPUI:WIN2000 - ModalContext converts OperationCancelException into InvocationTargetException
+                            // See 1GAN3L5: ITPUI:WIN2000 - ModalContext
+                            // converts OperationCancelException into
+                            // InvocationTargetException
                             throw new InterruptedException(throwable
                                     .msg);
                         } else {
@@ -367,8 +418,32 @@
     }
 
     /**
-     * Run a runnable.  Convert all thrown exceptions to
-     * either InterruptedException or InvocationTargetException
+     * Invoke the ThreadListener if there are any errors or RuntimeExceptions
+     * return them.
+     * 
+     * @param listener
+     * @param switchingThread
+     *            the {@link Thread} being switched to
+     */
+    static Throwable invokeThreadListener(IThreadListener listener,
+            Thread switchingThread) {
+        try {
+            listener.threadChange(switchingThread);
+        } catch (ThreadDeath e) {
+            // Make sure to propagate ThreadDeath, or threads will never
+            // fully terminate
+            throw e;
+        } catch (Error e) {
+            return e;
+        }catch (RuntimeException e) {
+            return e;
+        }
+        return null;
+    }
+
+    /**
+     * Run a runnable. Convert all thrown exceptions to either
+     * InterruptedException or InvocationTargetException
      */
     private static void runInCurrentThread(IRunnableWithProgress runnable,
             IProgressMonitor progressMonitor) {
@@ -384,7 +459,8 @@
             throw new InterruptedException();
         /+
         } catch (ThreadDeath e) {
-            // Make sure to propagate ThreadDeath, or threads will never fully terminate
+            // Make sure to propagate ThreadDeath, or threads will never fully
+            // terminate
             throw e;
         +/
         } catch (RuntimeException e) {
@@ -396,21 +472,26 @@
 
     /**
      * 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)
+     * 
+     * @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.
+     * 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) {