Mercurial > projects > dwt-addons
annotate dwtx/jface/operation/ModalContext.d @ 43:ea8ff534f622
Fix override and super aliases
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Fri, 11 Apr 2008 01:24:25 +0200 |
parents | 1a6747be662d |
children | ef6c06252a87 |
rev | line source |
---|---|
6 | 1 /******************************************************************************* |
2 * Copyright (c) 2000, 2006 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.jface.operation.ModalContext; | |
14 | |
15 import dwt.widgets.Display; | |
16 import dwtx.core.runtime.Assert; | |
17 import dwtx.core.runtime.IProgressMonitor; | |
18 import dwtx.core.runtime.OperationCanceledException; | |
19 import dwtx.core.runtime.ProgressMonitorWrapper; | |
20 | |
21 import dwtx.jface.operation.IThreadListener; | |
22 import dwtx.jface.operation.IRunnableWithProgress; | |
23 import dwtx.jface.operation.AccumulatingProgressMonitor; | |
24 | |
25 import dwt.dwthelper.utils; | |
26 import dwt.dwthelper.Runnable; | |
27 import tango.core.Thread; | |
28 import tango.io.Stdout; | |
29 | |
30 /** | |
31 * Utility class for supporting modal operations. | |
32 * The runnable passed to the <code>run</code> method is executed in a | |
33 * separate thread, depending on the value of the passed fork argument. | |
34 * If the runnable is executed in a separate thread then the current thread | |
35 * either waits until the new thread ends or, if the current thread is the | |
36 * UI thread, it polls the DWT event queue and dispatches each event. | |
37 * <p> | |
38 * This class is not intended to be subclassed. | |
39 * </p> | |
40 */ | |
41 public class ModalContext { | |
42 | |
43 /** | |
44 * Indicated whether ModalContext is in debug mode; | |
45 * <code>false</code> by default. | |
46 */ | |
47 private static bool debug_ = false; | |
48 | |
49 /** | |
50 * The number of nested modal runs, or 0 if not inside a modal run. | |
51 * This is global state. | |
52 */ | |
53 private static int modalLevel = 0; | |
54 | |
55 /** | |
56 * Indicates whether operations should be run in a separate thread. | |
57 * Defaults to true. | |
58 * For internal debugging use, set to false to run operations in the calling thread. | |
59 */ | |
60 private static bool runInSeparateThread = true; | |
61 | |
62 /** | |
63 * Thread which runs the modal context. | |
64 */ | |
65 private static class ModalContextThread : Thread { | |
66 /** | |
67 * The operation to be run. | |
68 */ | |
69 private IRunnableWithProgress runnable; | |
70 | |
71 /** | |
72 * The exception thrown by the operation starter. | |
73 */ | |
74 private Exception throwable; | |
75 | |
76 /** | |
77 * The progress monitor used for progress and cancelation. | |
78 */ | |
79 private IProgressMonitor progressMonitor; | |
80 | |
81 /** | |
82 * The display used for event dispatching. | |
83 */ | |
84 private Display display; | |
85 | |
86 /** | |
87 * Indicates whether to continue event queue dispatching. | |
88 */ | |
89 private /+volatile+/ bool continueEventDispatching = true; | |
90 | |
91 /** | |
92 * The thread that forked this modal context thread. | |
93 * | |
94 * @since 3.1 | |
95 */ | |
96 private Thread callingThread; | |
97 | |
98 /** | |
99 * Creates a new modal context. | |
100 * | |
101 * @param operation the runnable to run | |
102 * @param monitor the progress monitor to use to display progress and receive | |
103 * requests for cancelation | |
104 * @param display the display to be used to read and dispatch events | |
105 */ | |
106 private this(IRunnableWithProgress operation, | |
107 IProgressMonitor monitor, Display display) { | |
108 super(/+"ModalContext"+/); //$NON-NLS-1$ | |
109 Assert.isTrue(monitor !is null && display !is null); | |
110 runnable = operation; | |
111 progressMonitor = new AccumulatingProgressMonitor(monitor, display); | |
112 this.display = display; | |
113 this.callingThread = Thread.getThis(); | |
114 } | |
115 | |
116 /* (non-Javadoc) | |
117 * Method declared on Thread. | |
118 */ | |
43
ea8ff534f622
Fix override and super aliases
Frank Benoit <benoit@tionex.de>
parents:
6
diff
changeset
|
119 public /+override+/ void run() { |
6 | 120 try { |
121 if (runnable !is null) { | |
122 runnable.run(progressMonitor); | |
123 } | |
124 /+ | |
125 } catch (InvocationTargetException e) { | |
126 throwable = e; | |
127 } catch (InterruptedException e) { | |
128 throwable = e; | |
129 } catch (RuntimeException e) { | |
130 throwable = e; | |
131 } catch (ThreadDeath e) { | |
132 // Make sure to propagate ThreadDeath, or threads will never fully terminate | |
133 throw e; | |
134 +/ | |
135 } catch (/+Error+/Exception e) { | |
136 throwable = e; | |
137 } finally { | |
138 //notify the operation of change of thread of control | |
139 if ( auto tl = cast(IThreadListener)runnable ) { | |
140 tl.threadChange(callingThread); | |
141 } | |
142 | |
143 // Make sure that all events in the asynchronous event queue | |
144 // are dispatched. | |
145 display.syncExec(new class() Runnable { | |
146 public void run() { | |
147 // do nothing | |
148 } | |
149 }); | |
150 | |
151 // Stop event dispatching | |
152 continueEventDispatching = false; | |
153 | |
154 // Force the event loop to return from sleep () so that | |
155 // it stops event dispatching. | |
156 display.asyncExec(null); | |
157 } | |
158 } | |
159 | |
160 /** | |
161 * Processes events or waits until this modal context thread terminates. | |
162 */ | |
163 public void block() { | |
164 if (display is Display.getCurrent()) { | |
165 while (continueEventDispatching) { | |
166 // Run the event loop. Handle any uncaught exceptions caused | |
167 // by UI events. | |
168 try { | |
169 if (!display.readAndDispatch()) { | |
170 display.sleep(); | |
171 } | |
172 } | |
173 /+ | |
174 // ThreadDeath is a normal error when the thread is dying. We must | |
175 // propagate it in order for it to properly terminate. | |
176 catch (ThreadDeath e) { | |
177 throw (e); | |
178 } | |
179 +/ | |
180 // For all other exceptions, log the problem. | |
181 catch (Exception e) { | |
182 Stderr.formatln("Unhandled event loop exception during blocked modal context."); //$NON-NLS-1$ | |
183 ExceptionPrintStackTrace(e); | |
184 } | |
185 } | |
186 } else { | |
187 try { | |
188 join(); | |
189 } catch (Exception e) { | |
190 throwable = e; | |
191 } | |
192 } | |
193 } | |
194 } | |
195 | |
196 /** | |
197 * Returns whether the first progress monitor is the same as, or | |
198 * a wrapper around, the second progress monitor. | |
199 * | |
200 * @param monitor1 the first progress monitor | |
201 * @param monitor2 the second progress monitor | |
202 * @return <code>true</code> if the first is the same as, or | |
203 * a wrapper around, the second | |
204 * @see ProgressMonitorWrapper | |
205 */ | |
206 public static bool canProgressMonitorBeUsed(IProgressMonitor monitor1, | |
207 IProgressMonitor monitor2) { | |
208 if (monitor1 is monitor2) { | |
209 return true; | |
210 } | |
211 | |
212 while ( cast(ProgressMonitorWrapper)monitor1 ) { | |
213 monitor1 = (cast(ProgressMonitorWrapper)monitor1) | |
214 .getWrappedProgressMonitor(); | |
215 if (monitor1 is monitor2) { | |
216 return true; | |
217 } | |
218 } | |
219 return false; | |
220 } | |
221 | |
222 /** | |
223 * Checks with the given progress monitor and throws | |
224 * <code>InterruptedException</code> if it has been canceled. | |
225 * <p> | |
226 * Code in a long-running operation should call this method | |
227 * regularly so that a request to cancel will be honored. | |
228 * </p> | |
229 * <p> | |
230 * Convenience for: | |
231 * <pre> | |
232 * if (monitor.isCanceled()) | |
233 * throw new InterruptedException(); | |
234 * </pre> | |
235 * </p> | |
236 * | |
237 * @param monitor the progress monitor | |
238 * @exception InterruptedException if cancelling the operation has been requested | |
239 * @see IProgressMonitor#isCanceled() | |
240 */ | |
241 public static void checkCanceled(IProgressMonitor monitor) { | |
242 if (monitor.isCanceled()) { | |
243 throw new InterruptedException(); | |
244 } | |
245 } | |
246 | |
247 /** | |
248 * Returns the currently active modal context thread, or null if no modal context is active. | |
249 */ | |
250 private static ModalContextThread getCurrentModalContextThread() { | |
251 Thread t = Thread.getThis(); | |
252 if ( auto r = cast(ModalContextThread)t ) { | |
253 return r; | |
254 } | |
255 return null; | |
256 } | |
257 | |
258 /** | |
259 * Returns the modal nesting level. | |
260 * <p> | |
261 * The modal nesting level increases by one each time the | |
262 * <code>ModalContext.run</code> method is called within the | |
263 * dynamic scope of another call to <code>ModalContext.run</code>. | |
264 * </p> | |
265 * | |
266 * @return the modal nesting level, or <code>0</code> if | |
267 * this method is called outside the dynamic scope of any | |
268 * invocation of <code>ModalContext.run</code> | |
269 */ | |
270 public static int getModalLevel() { | |
271 return modalLevel; | |
272 } | |
273 | |
274 /** | |
275 * Returns whether the given thread is running a modal context. | |
276 * | |
277 * @param thread The thread to be checked | |
278 * @return <code>true</code> if the given thread is running a modal context, <code>false</code> if not | |
279 */ | |
280 public static bool isModalContextThread(Thread thread) { | |
281 return (cast(ModalContextThread)thread) !is null; | |
282 } | |
283 | |
284 /** | |
285 * Runs the given runnable in a modal context, passing it a progress monitor. | |
286 * <p> | |
287 * The modal nesting level is increased by one from the perspective | |
288 * of the given runnable. | |
289 * </p> | |
290 *<p> | |
291 * If the supplied operation implements <code>IThreadListener</code>, it | |
292 * will be notified of any thread changes required to execute the operation. | |
293 * Specifically, the operation will be notified of the thread that will call its | |
294 * <code>run</code> method before it is called, and will be notified of the | |
295 * change of control back to the thread calling this method when the operation | |
296 * completes. These thread change notifications give the operation an | |
297 * opportunity to transfer any thread-local state to the execution thread before | |
298 * control is transferred to the new thread. | |
299 *</p> | |
300 * @param operation the runnable to run | |
301 * @param fork <code>true</code> if the runnable should run in a separate thread, | |
302 * and <code>false</code> if in the same thread | |
303 * @param monitor the progress monitor to use to display progress and receive | |
304 * requests for cancelation | |
305 * @param display the display to be used to read and dispatch events | |
306 * @exception InvocationTargetException if the run method must propagate a checked exception, | |
307 * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions and errors are automatically | |
308 * wrapped in an <code>InvocationTargetException</code> by this method | |
309 * @exception InterruptedException if the operation detects a request to cancel, | |
310 * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing | |
311 * <code>InterruptedException</code>; this method propagates the exception | |
312 */ | |
313 public static void run(IRunnableWithProgress operation, bool fork, | |
314 IProgressMonitor monitor, Display display) { | |
315 Assert.isTrue(operation !is null && monitor !is null); | |
316 | |
317 modalLevel++; | |
318 try { | |
319 if (monitor !is null) { | |
320 monitor.setCanceled(false); | |
321 } | |
322 // Is the runnable supposed to be execute in the same thread. | |
323 if (!fork || !runInSeparateThread) { | |
324 runInCurrentThread(operation, monitor); | |
325 } else { | |
326 ModalContextThread t = getCurrentModalContextThread(); | |
327 if (t !is null) { | |
328 Assert.isTrue(canProgressMonitorBeUsed(monitor, | |
329 t.progressMonitor)); | |
330 runInCurrentThread(operation, monitor); | |
331 } else { | |
332 t = new ModalContextThread(operation, monitor, display); | |
333 if ( auto tl = cast(IThreadListener)operation ) { | |
334 tl.threadChange(t); | |
335 } | |
336 t.start(); | |
337 t.block(); | |
338 Exception throwable = t.throwable; | |
339 if (throwable !is null) { | |
340 if (debug_ | |
341 && !(cast(InterruptedException)throwable ) | |
342 && !(cast(OperationCanceledException)throwable )) { | |
343 Stderr.formatln("Exception in modal context operation:"); //$NON-NLS-1$ | |
344 ExceptionPrintStackTrace(throwable); | |
345 Stderr.formatln("Called from:"); //$NON-NLS-1$ | |
346 // Don't create the InvocationTargetException on the throwable, | |
347 // otherwise it will print its stack trace (from the other thread). | |
348 ExceptionPrintStackTrace( new InvocationTargetException(null)); | |
349 } | |
350 if (cast(InvocationTargetException)throwable ) { | |
351 throw cast(InvocationTargetException) throwable; | |
352 } else if (cast(InterruptedException)throwable ) { | |
353 throw cast(InterruptedException) throwable; | |
354 } else if (cast(OperationCanceledException)throwable ) { | |
355 // See 1GAN3L5: ITPUI:WIN2000 - ModalContext converts OperationCancelException into InvocationTargetException | |
356 throw new InterruptedException(throwable | |
357 .msg); | |
358 } else { | |
359 throw new InvocationTargetException(throwable); | |
360 } | |
361 } | |
362 } | |
363 } | |
364 } finally { | |
365 modalLevel--; | |
366 } | |
367 } | |
368 | |
369 /** | |
370 * Run a runnable. Convert all thrown exceptions to | |
371 * either InterruptedException or InvocationTargetException | |
372 */ | |
373 private static void runInCurrentThread(IRunnableWithProgress runnable, | |
374 IProgressMonitor progressMonitor) { | |
375 try { | |
376 if (runnable !is null) { | |
377 runnable.run(progressMonitor); | |
378 } | |
379 } catch (InvocationTargetException e) { | |
380 throw e; | |
381 } catch (InterruptedException e) { | |
382 throw e; | |
383 } catch (OperationCanceledException e) { | |
384 throw new InterruptedException(); | |
385 /+ | |
386 } catch (ThreadDeath e) { | |
387 // Make sure to propagate ThreadDeath, or threads will never fully terminate | |
388 throw e; | |
389 +/ | |
390 } catch (RuntimeException e) { | |
391 throw new InvocationTargetException(e); | |
392 } catch (/+Error+/Exception e) { | |
393 throw new InvocationTargetException(e); | |
394 } | |
395 } | |
396 | |
397 /** | |
398 * Sets whether ModalContext is running in debug mode. | |
399 * | |
400 * @param debugMode <code>true</code> for debug mode, | |
401 * and <code>false</code> for normal mode (the default) | |
402 */ | |
403 public static void setDebugMode(bool debugMode) { | |
404 debug_ = debugMode; | |
405 } | |
406 | |
407 /** | |
408 * Sets whether ModalContext may process events (by calling <code>Display.readAndDispatch()</code>) | |
409 * while running operations. By default, ModalContext will process events while running operations. | |
410 * Use this method to disallow event processing temporarily. | |
411 * @param allowReadAndDispatch <code>true</code> (the default) if events may be processed while | |
412 * running an operation, <code>false</code> if Display.readAndDispatch() should not be called | |
413 * from ModalContext. | |
414 * @since 3.2 | |
415 */ | |
416 public static void setAllowReadAndDispatch(bool allowReadAndDispatch) { | |
417 // use a separate thread if and only if it is OK to spin the event loop | |
418 runInSeparateThread = allowReadAndDispatch; | |
419 } | |
420 } |