blob: 1264d08eed99ea6e021386ad244c460a1fcd177b [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Showing Modal Progress in Eclipse 3.0</title>
<link rel="stylesheet" href="http://dev.eclipse.org/default_style.css" type="text/css">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<h1>Showing Modal Progress in Eclipse 3.0</h1>
<font size="-1">Last modified: June 23, 2004</font>
<p>
A goal of the responsive UI plan item was to give the user the feeling of being in control
over things happening in the background. There is one scenario where
we need your help to achieve this goal. When a modal user operation is blocked by a
background job, we want to show the running jobs and provide the option to cancel some of
them. The existing JFace <tt>ProgressMonitorDialog</tt> can only provide limited support to
do so. All it can do is inform the user that the current operation needs to wait.
See the following screenshot:
<pre>
<img src="progress-images/progressdialog.gif"/>
</pre>
<p>
What we really want to show is the progress view with the option to cancel a job:
<pre>
<img src="progress-images/progressservice.gif"/>
</pre>
<p>
You can get this additional support by migrating from <tt>ProgressMonitorDialog</tt> to
the new <tt>org.eclipse.ui.progress.IProgressService</tt>.
<p>
Here is how you can migrate:
<ol>
<li>
<b>Existing code:</b>
<pre>
ProgressMonitorDialog progress = new ProgressMonitorDialog(shell);
progress.run(true, true, operation);
</pre>
<b>Replacement:</b>
<pre>
PlatformUI.getWorkbench().getProgressService().busyCursorWhile(operation);
</pre>
<tt>busyCursorWhile</tt> shows the busy cursor and after some delay it shows
an enhanced progress dialog as shown above.
<p>
</li>
<li>
<b>Existing code:</b>
<pre>
ProgressMonitorDialog progress = new ProgressMonitorDialog(shell);
progress.run(<b>false</b>, false, operation);
</pre>
In this case the first parameter, <tt>fork</tt>, in <tt>dialog.run()</tt> is <b><tt>false</tt></b>,
which means the operation isn't run in a separate thread but in the UI thread with
the consequence of preventing any UI refreshes.
<p>
<b>Replacement:</b><br><br>
The simplest replacement for non-forking progress is to call:
<pre>
PlatformUI.getWorkbench().getProgressService().run(false, false, operation);
</pre>
This will ensure that a progress dialog is shown, but the UI will still not be able
to refresh for the duration of the operation. A better replacement is to use:
<pre>
IProgressService service = PlatformUI.getWorkbench().getProgressService();
service.runInUI(service, operation, schedulingRule);
</pre>
The <tt>runInUI</tt> method operates in two phases. First, it acquires the supplied
scheduling rule in the UI thread. If the scheduling rule is not available due to concurrent
modifications in another thread, it will block until the rule is available. During this time,
it will show a progress dialog and will continue to refresh the UI. Once the rule is
acquired, the operation will run in the UI thread, during which time the UI will not
refresh. This ensures that the UI remains responsive in situations where your operation
is blocked for unknown periods of time, while still allowing your operation to run
in the UI thread.
<p>
To use the <tt>runInUI</tt> API, you need to know what scheduling rule is appropriate
for your operation. In the case of operations that modify resources, the most defensive
scheduling rule to use is the workspace root: <tt>ResourcesPlugin.getWorkspace().getRoot()</tt>.
If possible use the <tt>IResourceRuleFactory</tt> to get a more fine grained scheduling rule.
You can read more about scheduling rules in the Platform Plugin Developer Guide,
under <b>Programmer's Guide &gt; Runtime overview &gt; Concurrency infrastructure</b>.
<p>
<b>Important:</b> whenever possible try to run your operations with <tt>fork==true</tt>.
When converting to <tt>IProgressService</tt> please check whether you can
convert from <tt>fork==false</tt> to <tt>fork==true</tt>. Using <tt>fork==false</tt>
simply because the operation needs to access some state from UI widgets is not an
excuse to not set <tt>fork==true</tt>. You should change your code so that you
can fetch the state from the widgets outside of the operation in the UI thread, or
gather all the necessary information from the UI before starting the operation.
</li>
<li>
An API method expects a runnable context, e.g.:
<pre>
createTypeDialog(Shell parent, IRunnableContext context, ...)
</pre>
<b>Existing code:</b>
<pre>
ProgressMonitorDialog progress= new ProgressMonitorDialog(shell);
createTypeDialog(shell, progress, ...);
</pre>
<b>Replacement:</b>
<pre>
createTypeDialog(shell, PlatformUI.getWorkbench().getProgressService());
</pre>
<p>
In other words the <tt>IProgressService</tt> is an <tt>IRunnableContext</tt>.
</li>
</ol>
Note that <tt>IProgressService.busyCursorWhile</tt> also serves as a good replacement
for <tt>BusyIndicator.showWhile</tt>, and <tt>IStatusLineManager.getProgressMonitor()</tt>.
The progress service will progressively switch from a busy cursor to a progress dialog
if the operation exceeds a given duration, and will do a better job of reporting blockage
than any existing progress mechanism. This takes the guess-work out of deciding what
progress mechanism to use for potentially long running operations.
<p>
&#147;The Eclipse 3.0 progress service, one-stop shopping for all your modal progress needs!&#148;
<hr>
</p>
</body>
</html>