Showing Modal Progress in Eclipse 3.0

Last modified: June 23, 2004

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 ProgressMonitorDialog 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:

   

What we really want to show is the progress view with the option to cancel a job:

   

You can get this additional support by migrating from ProgressMonitorDialog to the new org.eclipse.ui.progress.IProgressService.

Here is how you can migrate:

  1. Existing code:
       ProgressMonitorDialog progress = new ProgressMonitorDialog(shell);				
       progress.run(true, true, operation);	
    
    Replacement:
       PlatformUI.getWorkbench().getProgressService().busyCursorWhile(operation);	
    
    busyCursorWhile shows the busy cursor and after some delay it shows an enhanced progress dialog as shown above.

  2. Existing code:
       ProgressMonitorDialog progress = new ProgressMonitorDialog(shell);				
       progress.run(false, false, operation);	
    
    In this case the first parameter, fork, in dialog.run() is false, which means the operation isn't run in a separate thread but in the UI thread with the consequence of preventing any UI refreshes.

    Replacement:

    The simplest replacement for non-forking progress is to call:

       PlatformUI.getWorkbench().getProgressService().run(false, false, operation);
    
    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:
       IProgressService service = PlatformUI.getWorkbench().getProgressService();
       service.runInUI(service, operation, schedulingRule);
    
    The runInUI 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.

    To use the runInUI 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: ResourcesPlugin.getWorkspace().getRoot(). If possible use the IResourceRuleFactory to get a more fine grained scheduling rule. You can read more about scheduling rules in the Platform Plugin Developer Guide, under Programmer's Guide > Runtime overview > Concurrency infrastructure.

    Important: whenever possible try to run your operations with fork==true. When converting to IProgressService please check whether you can convert from fork==false to fork==true. Using fork==false simply because the operation needs to access some state from UI widgets is not an excuse to not set fork==true. 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.

  3. An API method expects a runnable context, e.g.:
       createTypeDialog(Shell parent, IRunnableContext context, ...)	
    
    Existing code:
       ProgressMonitorDialog progress= new ProgressMonitorDialog(shell);				
       createTypeDialog(shell, progress, ...);
    
    Replacement:
       createTypeDialog(shell, PlatformUI.getWorkbench().getProgressService());
    

    In other words the IProgressService is an IRunnableContext.

Note that IProgressService.busyCursorWhile also serves as a good replacement for BusyIndicator.showWhile, and IStatusLineManager.getProgressMonitor(). 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.

“The Eclipse 3.0 progress service, one-stop shopping for all your modal progress needs!”