KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > dialogs > ProgressMonitorDialog


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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  *******************************************************************************/

11 package org.eclipse.jface.dialogs;
12
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14
15 import org.eclipse.core.runtime.IProgressMonitor;
16 import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
17 import org.eclipse.core.runtime.IStatus;
18 import org.eclipse.jface.operation.IRunnableContext;
19 import org.eclipse.jface.operation.IRunnableWithProgress;
20 import org.eclipse.jface.operation.ModalContext;
21 import org.eclipse.jface.resource.JFaceResources;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.graphics.Cursor;
24 import org.eclipse.swt.graphics.Image;
25 import org.eclipse.swt.graphics.Point;
26 import org.eclipse.swt.layout.GridData;
27 import org.eclipse.swt.widgets.Button;
28 import org.eclipse.swt.widgets.Composite;
29 import org.eclipse.swt.widgets.Control;
30 import org.eclipse.swt.widgets.Event;
31 import org.eclipse.swt.widgets.Label;
32 import org.eclipse.swt.widgets.Listener;
33 import org.eclipse.swt.widgets.Shell;
34
35 /**
36  * A modal dialog that displays progress during a long running operation.
37  * <p>
38  * This concrete dialog class can be instantiated as is, or further subclassed
39  * as required.
40  * </p>
41  * <p>
42  * Typical usage is:
43  *
44  * <pre>
45  *
46  *
47  * try {
48  * IRunnableWithProgress op = ...;
49  * new ProgressMonitorDialog(activeShell).run(true, true, op);
50  * } catch (InvocationTargetException e) {
51  * // handle exception
52  * } catch (InterruptedException e) {
53  * // handle cancelation
54  * }
55  *
56  *
57  * </pre>
58  *
59  * </p>
60  * <p>
61  * Note that the ProgressMonitorDialog is not intended to be used with multiple
62  * runnables - this dialog should be discarded after completion of one
63  * IRunnableWithProgress and a new one instantiated for use by a second or
64  * sebsequent IRunnableWithProgress to ensure proper initialization.
65  * </p>
66  * <p>
67  * Note that not forking the process will result in it running in the UI which
68  * may starve the UI. The most obvious symptom of this problem is non
69  * responsiveness of the cancel button. If you are running within the UI Thread
70  * you should do the bulk of your work in another Thread to prevent starvation.
71  * It is recommended that fork is set to true in most cases.
72  * </p>
73  */

74 public class ProgressMonitorDialog extends IconAndMessageDialog implements
75         IRunnableContext {
76     /**
77      * Name to use for task when normal task name is empty string.
78      */

79     private static String JavaDoc DEFAULT_TASKNAME = JFaceResources
80             .getString("ProgressMonitorDialog.message"); //$NON-NLS-1$
81

82     /**
83      * Constants for label and monitor size
84      */

85     private static int LABEL_DLUS = 21;
86
87     private static int BAR_DLUS = 9;
88
89     /**
90      * The progress indicator control.
91      */

92     protected ProgressIndicator progressIndicator;
93
94     /**
95      * The label control for the task. Kept for backwards compatibility.
96      */

97     protected Label taskLabel;
98
99     /**
100      * The label control for the subtask.
101      */

102     protected Label subTaskLabel;
103
104     /**
105      * The Cancel button control.
106      */

107     protected Button cancel;
108
109     /**
110      * Indicates whether the Cancel button is to be shown.
111      */

112     protected boolean operationCancelableState = false;
113
114     /**
115      * Indicates whether the Cancel button is to be enabled.
116      */

117     protected boolean enableCancelButton;
118
119     /**
120      * The progress monitor.
121      */

122     private ProgressMonitor progressMonitor = new ProgressMonitor();
123
124     /**
125      * The name of the current task (used by ProgressMonitor).
126      */

127     private String JavaDoc task;
128
129     /**
130      * The nesting depth of currently running runnables.
131      */

132     private int nestingDepth;
133
134     /**
135      * The cursor used in the cancel button;
136      */

137     protected Cursor arrowCursor;
138
139     /**
140      * The cursor used in the shell;
141      */

142     private Cursor waitCursor;
143
144     /**
145      * Flag indicating whether to open or merely create the dialog before run.
146      */

147     private boolean openOnRun = true;
148
149     /**
150      * Internal progress monitor implementation.
151      */

152     private class ProgressMonitor implements IProgressMonitorWithBlocking {
153         private String JavaDoc fSubTask = "";//$NON-NLS-1$
154

155         private boolean fIsCanceled;
156
157         /**
158          * is the process forked
159          */

160         protected boolean forked = false;
161
162         /**
163          * is locked
164          */

165         protected boolean locked = false;
166
167         public void beginTask(String JavaDoc name, int totalWork) {
168             if (progressIndicator.isDisposed()) {
169                 return;
170             }
171             if (name == null) {
172                 task = "";//$NON-NLS-1$
173
} else {
174                 task = name;
175             }
176             String JavaDoc s = task;
177             if (s.length() <= 0) {
178                 s = DEFAULT_TASKNAME;
179             }
180             setMessage(s, false);
181             if (!forked) {
182                 update();
183             }
184             if (totalWork == UNKNOWN) {
185                 progressIndicator.beginAnimatedTask();
186             } else {
187                 progressIndicator.beginTask(totalWork);
188             }
189         }
190
191         public void done() {
192             if (!progressIndicator.isDisposed()) {
193                 progressIndicator.sendRemainingWork();
194                 progressIndicator.done();
195             }
196         }
197
198         public void setTaskName(String JavaDoc name) {
199             if (name == null) {
200                 task = "";//$NON-NLS-1$
201
} else {
202                 task = name;
203             }
204             String JavaDoc s = task;
205             if (s.length() <= 0) {
206                 s = DEFAULT_TASKNAME;
207             }
208             setMessage(s, false);
209             if (!forked) {
210                 update();
211             }
212         }
213
214         public boolean isCanceled() {
215             return fIsCanceled;
216         }
217
218         public void setCanceled(boolean b) {
219             fIsCanceled = b;
220             if (locked) {
221                 clearBlocked();
222             }
223         }
224
225         public void subTask(String JavaDoc name) {
226             if (subTaskLabel.isDisposed()) {
227                 return;
228             }
229             if (name == null) {
230                 fSubTask = "";//$NON-NLS-1$
231
} else {
232                 fSubTask = name;
233             }
234             subTaskLabel.setText(shortenText(fSubTask, subTaskLabel));
235             if (!forked) {
236                 subTaskLabel.update();
237             }
238         }
239
240         public void worked(int work) {
241             internalWorked(work);
242         }
243
244         public void internalWorked(double work) {
245             if (!progressIndicator.isDisposed()) {
246                 progressIndicator.worked(work);
247             }
248         }
249
250         /*
251          * (non-Javadoc)
252          *
253          * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
254          */

255         public void clearBlocked() {
256             locked = false;
257             updateForClearBlocked();
258         }
259
260         /*
261          * (non-Javadoc)
262          *
263          * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#setBlocked(org.eclipse.core.runtime.IStatus)
264          */

265         public void setBlocked(IStatus reason) {
266             locked = true;
267             updateForSetBlocked(reason);
268         }
269     }
270
271     /**
272      * Clear blocked state from the receiver.
273      */

274     protected void updateForClearBlocked() {
275         setMessage(task, true);
276         imageLabel.setImage(getImage());
277     }
278
279     /**
280      * Set blocked state from the receiver.
281      *
282      * @param reason
283      * IStatus that gives the details
284      */

285     protected void updateForSetBlocked(IStatus reason) {
286         setMessage(reason.getMessage(), true);
287         imageLabel.setImage(getImage());
288     }
289
290     /**
291      * Creates a progress monitor dialog under the given shell. The dialog has a
292      * standard title and no image. <code>open</code> is non-blocking.
293      *
294      * @param parent
295      * the parent shell, or <code>null</code> to create a top-level
296      * shell
297      */

298     public ProgressMonitorDialog(Shell parent) {
299         super(parent);
300         setShellStyle(getDefaultOrientation() | SWT.BORDER | SWT.TITLE
301                 | SWT.APPLICATION_MODAL); // no
302
// close
303
// button
304
setBlockOnOpen(false);
305     }
306
307     /**
308      * Enables the cancel button (asynchronously).
309      *
310      * @param b
311      * The state to set the button to.
312      */

313     private void asyncSetOperationCancelButtonEnabled(final boolean b) {
314         if (getShell() != null) {
315             getShell().getDisplay().asyncExec(new Runnable JavaDoc() {
316                 public void run() {
317                     setOperationCancelButtonEnabled(b);
318                 }
319             });
320         }
321     }
322
323     /**
324      * The cancel button has been pressed.
325      *
326      * @since 3.0
327      */

328     protected void cancelPressed() {
329         // NOTE: this was previously done from a listener installed on the
330
// cancel button. On GTK, the listener installed by
331
// Dialog.createButton is called first and this was throwing an
332
// exception because the cancel button was already disposed
333
cancel.setEnabled(false);
334         progressMonitor.setCanceled(true);
335         super.cancelPressed();
336     }
337
338     /*
339      * (non-Javadoc) Method declared on Window.
340      */

341     /**
342      * The <code>ProgressMonitorDialog</code> implementation of this method
343      * only closes the dialog if there are no currently running runnables.
344      */

345     public boolean close() {
346         if (getNestingDepth() <= 0) {
347             clearCursors();
348             return super.close();
349         }
350         return false;
351     }
352
353     /**
354      * Clear the cursors in the dialog.
355      *
356      * @since 3.0
357      */

358     protected void clearCursors() {
359         if (cancel != null && !cancel.isDisposed()) {
360             cancel.setCursor(null);
361         }
362         Shell shell = getShell();
363         if (shell != null && !shell.isDisposed()) {
364             shell.setCursor(null);
365         }
366         if (arrowCursor != null) {
367             arrowCursor.dispose();
368         }
369         if (waitCursor != null) {
370             waitCursor.dispose();
371         }
372         arrowCursor = null;
373         waitCursor = null;
374     }
375
376     /*
377      * (non-Javadoc) Method declared in Window.
378      */

379     protected void configureShell(final Shell shell) {
380         super.configureShell(shell);
381         shell.setText(JFaceResources.getString("ProgressMonitorDialog.title")); //$NON-NLS-1$
382
if (waitCursor == null) {
383             waitCursor = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
384         }
385         shell.setCursor(waitCursor);
386         // Add a listener to set the message properly when the dialog becomes
387
// visible
388
shell.addListener(SWT.Show, new Listener() {
389             public void handleEvent(Event event) {
390                 // We need to async the message update since the Show precedes
391
// visibility
392
shell.getDisplay().asyncExec(new Runnable JavaDoc() {
393                     public void run() {
394                         setMessage(message, true);
395                     }
396                 });
397             }
398         });
399     }
400
401     /*
402      * (non-Javadoc) Method declared on Dialog.
403      */

404     protected void createButtonsForButtonBar(Composite parent) {
405         // cancel button
406
createCancelButton(parent);
407     }
408
409     /**
410      * Creates the cancel button.
411      *
412      * @param parent
413      * the parent composite
414      * @since 3.0
415      */

416     protected void createCancelButton(Composite parent) {
417         cancel = createButton(parent, IDialogConstants.CANCEL_ID,
418                 IDialogConstants.CANCEL_LABEL, true);
419         if (arrowCursor == null) {
420             arrowCursor = new Cursor(cancel.getDisplay(), SWT.CURSOR_ARROW);
421         }
422         cancel.setCursor(arrowCursor);
423         setOperationCancelButtonEnabled(enableCancelButton);
424     }
425
426     /*
427      * (non-Javadoc) Method declared on Dialog.
428      */

429     protected Control createDialogArea(Composite parent) {
430         setMessage(DEFAULT_TASKNAME, false);
431         createMessageArea(parent);
432         // Only set for backwards compatibility
433
taskLabel = messageLabel;
434         // progress indicator
435
progressIndicator = new ProgressIndicator(parent);
436         GridData gd = new GridData();
437         gd.heightHint = convertVerticalDLUsToPixels(BAR_DLUS);
438         gd.horizontalAlignment = GridData.FILL;
439         gd.grabExcessHorizontalSpace = true;
440         gd.horizontalSpan = 2;
441         progressIndicator.setLayoutData(gd);
442         // label showing current task
443
subTaskLabel = new Label(parent, SWT.LEFT | SWT.WRAP);
444         gd = new GridData(GridData.FILL_HORIZONTAL);
445         gd.heightHint = convertVerticalDLUsToPixels(LABEL_DLUS);
446         gd.horizontalSpan = 2;
447         subTaskLabel.setLayoutData(gd);
448         subTaskLabel.setFont(parent.getFont());
449         return parent;
450     }
451
452     /*
453      * (non-Javadoc)
454      *
455      * @see org.eclipse.jface.window.Window#getInitialSize()
456      */

457     protected Point getInitialSize() {
458         Point calculatedSize = super.getInitialSize();
459         if (calculatedSize.x < 450) {
460             calculatedSize.x = 450;
461         }
462         return calculatedSize;
463     }
464
465     /**
466      * Returns the progress monitor to use for operations run in this progress
467      * dialog.
468      *
469      * @return the progress monitor
470      */

471     public IProgressMonitor getProgressMonitor() {
472         return progressMonitor;
473     }
474
475     /**
476      * This implementation of IRunnableContext#run(boolean, boolean,
477      * IRunnableWithProgress) runs the given <code>IRunnableWithProgress</code>
478      * using the progress monitor for this progress dialog and blocks until the
479      * runnable has been run, regardless of the value of <code>fork</code>.
480      * The dialog is opened before the runnable is run, and closed after it
481      * completes. It is recommended that <code>fork</code> is set to true in
482      * most cases. If <code>fork</code> is set to <code>false</code>, the
483      * runnable will run in the UI thread and it is the runnable's
484      * responsibility to call <code>Display.readAndDispatch()</code> to ensure
485      * UI responsiveness.
486      */

487     public void run(boolean fork, boolean cancelable,
488             IRunnableWithProgress runnable) throws InvocationTargetException JavaDoc,
489             InterruptedException JavaDoc {
490         setCancelable(cancelable);
491         try {
492             aboutToRun();
493             // Let the progress monitor know if they need to update in UI Thread
494
progressMonitor.forked = fork;
495             ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
496                     .getDisplay());
497         } finally {
498             finishedRun();
499         }
500     }
501
502     /**
503      * Returns whether the dialog should be opened before the operation is run.
504      * Defaults to <code>true</code>
505      *
506      * @return <code>true</code> to open the dialog before run,
507      * <code>false</code> to only create the dialog, but not open it
508      * @since 3.0
509      */

510     public boolean getOpenOnRun() {
511         return openOnRun;
512     }
513
514     /**
515      * Sets whether the dialog should be opened before the operation is run.
516      * NOTE: Setting this to false and not forking a process may starve any
517      * asyncExec that tries to open the dialog later.
518      *
519      * @param openOnRun
520      * <code>true</code> to open the dialog before run,
521      * <code>false</code> to only create the dialog, but not open
522      * it
523      * @since 3.0
524      */

525     public void setOpenOnRun(boolean openOnRun) {
526         this.openOnRun = openOnRun;
527     }
528
529     /**
530      * Returns the nesting depth of running operations.
531      *
532      * @return the nesting depth of running operations
533      * @since 3.0
534      */

535     protected int getNestingDepth() {
536         return nestingDepth;
537     }
538
539     /**
540      * Increments the nesting depth of running operations.
541      *
542      * @since 3.0
543      */

544     protected void incrementNestingDepth() {
545         nestingDepth++;
546     }
547
548     /**
549      * Decrements the nesting depth of running operations.
550      *
551      * @since 3.0
552      *
553      */

554     protected void decrementNestingDepth() {
555         nestingDepth--;
556     }
557
558     /**
559      * Called just before the operation is run. Default behaviour is to open or
560      * create the dialog, based on the setting of <code>getOpenOnRun</code>,
561      * and increment the nesting depth.
562      *
563      * @since 3.0
564      */

565     protected void aboutToRun() {
566         if (getOpenOnRun()) {
567             open();
568         } else {
569             create();
570         }
571         incrementNestingDepth();
572     }
573
574     /**
575      * Called just after the operation is run. Default behaviour is to decrement
576      * the nesting depth, and close the dialog.
577      *
578      * @since 3.0
579      */

580     protected void finishedRun() {
581         decrementNestingDepth();
582         close();
583     }
584
585     /**
586      * Sets whether the progress dialog is cancelable or not.
587      *
588      * @param cancelable
589      * <code>true</code> if the end user can cancel this progress
590      * dialog, and <code>false</code> if it cannot be canceled
591      */

592     public void setCancelable(boolean cancelable) {
593         if (cancel == null) {
594             enableCancelButton = cancelable;
595         } else {
596             asyncSetOperationCancelButtonEnabled(cancelable);
597         }
598     }
599
600     /**
601      * Helper to enable/disable Cancel button for this dialog.
602      *
603      * @param b
604      * <code>true</code> to enable the cancel button, and
605      * <code>false</code> to disable it
606      * @since 3.0
607      */

608     protected void setOperationCancelButtonEnabled(boolean b) {
609         operationCancelableState = b;
610         cancel.setEnabled(b);
611     }
612
613     /*
614      * (non-Javadoc)
615      *
616      * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getImage()
617      */

618     protected Image getImage() {
619         return getInfoImage();
620     }
621
622     /**
623      * Set the message in the message label.
624      *
625      * @param messageString
626      * The string for the new message.
627      * @param force
628      * If force is true then always set the message text.
629      */

630     private void setMessage(String JavaDoc messageString, boolean force) {
631         // must not set null text in a label
632
message = messageString == null ? "" : messageString; //$NON-NLS-1$
633
if (messageLabel == null || messageLabel.isDisposed()) {
634             return;
635         }
636         if (force || messageLabel.isVisible()) {
637             messageLabel.setToolTipText(message);
638             messageLabel.setText(shortenText(message, messageLabel));
639         }
640     }
641
642     /**
643      * Update the message label. Required if the monitor is forked.
644      */

645     private void update() {
646         if (messageLabel == null || messageLabel.isDisposed()) {
647             return;
648         }
649         messageLabel.update();
650     }
651
652     /*
653      * (non-Javadoc)
654      *
655      * @see org.eclipse.jface.window.Window#open()
656      */

657     public int open() {
658         // Check to be sure it is not already done. If it is just return OK.
659
if (!getOpenOnRun()) {
660             if (getNestingDepth() == 0) {
661                 return OK;
662             }
663         }
664         int result = super.open();
665         // update message label just in case beginTask() has been invoked already
666
if (task == null || task.length() == 0)
667             setMessage(DEFAULT_TASKNAME, true);
668         else
669             setMessage(task, true);
670         return result;
671     }
672 }
673
Popular Tags