# Indeterminate Progress Bars

This document describes the changes we made to implement indeterminate progress bars -- GUI components that look similar to normal progress bars and, like normal progress bars, use animation to show that a time-consuming operation is occurring. Unlike normal progress bars, indeterminate progress bars do not show the degree of completeness of the operation. This document has the following sections:

- New JProgressBar API
- New UI Defaults
- New BasicProgressBarUI API
- Converting Existing Progress Bar Implementations
- Implementation Details
- Javadoc for New API

### New JProgressBar API

By default, every progress bar (created using one of several `JProgressBar` constructors) is determinate. You may make any `JProgressBar` indeterminate using the `setIndeterminate` method:

        pb.setIndeterminate(true);

An indeterminate progress bar animates constantly. You can stop the animation and clear the progress bar by making the progress bar determinate and setting the current value to the minimum. For example:

        pb.setValue(pb.getMinimum());
        pb.setIndeterminate(false);

You can switch from determinate to indeterminate mode, and vice versa, at any time. You can check whether the progress bar is indeterminate using the `isIndeterminate` method.

When a progress bar is indeterminate, it ignores its model (a `BoundedRangeModel`). However, the model should exist and contain legal data, since L&Fs that haven't been updated for indeterminate progress bars might use the model.

### New UI Defaults

To give developers control over the progress bar's speed of animation, we've added two new UI defaults:

- `"ProgressBar.repaintInterval"`
- `"ProgressBar.cycleTime"`

The `"ProgressBar.cycleTime"` UI default lets look and feel implementers (as well as other developers)specify how many milliseconds each animation cycle takes. For example, a cycle time of 500 means that the indeterminate-mode progress bar animation repeats twice per second. The default drawing code uses this value --along with the repaint interval, box length, and component inner area --to determine how far to move the bouncing box each time it's drawn. This value must be an even multiple of the repaint interval. For example, if the repaint interval is 100, then valid cycle times are 200, 1000, and 1200, but not 100 or 500. If the user specifies an invalid cycle time,the indeterminate progress bar code automatically increases the cycle time to a suitable value.

To set the repaint interval and cycle time defaults:

        UIManager.put("ProgressBar.repaintInterval", new Integer(250));
        UIManager.put("ProgressBar.cycleTime", new Integer(6000));

To get the repaint interval and cycle time:

        int interval = UIManager.getInt("ProgressBar.repaintInterval");
        int totalTime = UIManager.getInt("ProgressBar.cycleTime");

In the basic, JLF, Motif, and Windows implementations provided by Sun, these defaults are checked when (and only when) the progress bar is switching into indeterminate mode.

### New BasicProgressBarUI API

To help programmers implement look-and-feel code for progress bars,we have added the following API to the `BasicProgressBarUI` class.

Methods related to painting:

- `protected void paintIndeterminate(Graphics g, JComponent c)`
- `protected void paintDeterminate(Graphics g, JComponent c)`
- `protected Rectangle getBox(Rectangle)`

Methods for setting and getting the index of the current frame of animation:

- `protected int getAnimationIndex()`
- `protected void setAnimationIndex(int newValue)`
- `protected void incrementAnimationIndex()`

Methods for starting and stopping custom animation threads:

- `protected void startAnimationTimer()`
- `protected void stopAnimationTimer()`

The `paint` method that previously performed all painting for the progress bar now delegates all its drawing to either `paintDeterminate` or `paintIndeterminate`, depending on the value of the progress bar's indeterminate property. When the progress bar is in indeterminate mode, the `paint` method (and thus the `paintIndeterminate` method) executes every repaint interval milliseconds.The `paintIndeterminate` method should paint the progress bar to match the animation state, which is specified by the `getAnimationIndex` method.

The `getBox` method allows implementations to customize bouncing box painting. For example, `MetalProgressBarUI` invokes `getBox` in its `paintIndeterminate` method to get the box drawn by the superclass (`BasicProgressBarUI`) and then to add an outline to the box. By overriding the `getBox` method, an implementer gets full control over the bouncing box's size and position without needing to reimplement `paintIndeterminate`.

If you override `paintIndeterminate` or `getBox`, you might also have to override `incrementAnimationIndex` so that it cycles correctly through the valid values. The first value (representing the first drawing) is 0. By convention, the second is 1, the third is 2, and so on. The last legal value is, by convention, the total number of drawings in the animation cycle, minus one. To determine the total number of drawings, you may need to take into account the repaint interval and perhaps the component size. As the "by convention" implies, you can implement the animation index to have any meaning and values you wish, as long as zero indicates the beginning of the animation cycle.

If you don't want to use the animation thread we provide, you must override the two xxx `AnimationTimer` methods. You can then provide your own implementation that periodically increments the animation index and invokes `repaint` on the progress bar.

As a side effect of the progress bar work, we've added a new method to `SwingUtilities`:

- `public static Rectangle calculateInnerArea(Component c, Rectangle r)`

The `calculateInnerArea` method is useful to all classes that perform drawing. It returns the area in which the component can draw -- that is, the rectangle (in the component's coordinate system) that includes all of the component except its border area (the component's insets).

### Converting Existing Progress Bar Implementations

Converting an existing L&F to indeterminate progress bars is relatively straightforward. If the L&F's progress bar UI class doesn't override `paint` (or does but also invokes `super.paint`), then support for indeterminate progress bars is automatic. `WindowsProgressBarUI`, `MotifProgressBarUI`, and `MetalProgressBarUI` are in this lucky camp.

If the L&F's progress bar UI class is a subclass of `BasicProgressBarUI` and overrides `paint` without invoking the superclass version, then determinate mode still works, but indeterminate mode looks the same as determinate mode.

Existing drawing code should be moved out of the `paint` method and into the new `paintDeterminate` method. Code for indeterminate painting should go in the new `paintIndeterminate` method. If at all possible, the `paint` method should not be overridden unless it invokes `super.paint`. The reason: the `BasicProgressBarUI` implementation of the `paint` method may work with the default animation thread to enhance performance and behavior.

The Mac look and feel (both the no-longer-maintained Sun version and the Apple version) is an example of a look and feel that overrides `paint` without invoking the superclass version.

If you already have a thread scheme for indeterminate painting, you can continue to use that scheme by overriding `startAnimationTimer` and `stopAnimationTimer`. Or you can just delete your thread code and use our scheme.

### Implementation Details

The `BasicProgressBarUI` class contains most of our implementation of indeterminate progress bars. Aside from the drawing code, most of the code is in two private inner classes: `Animator`, which implements the animation thread, and`PropertyChangeHandler`, which listens for changes to and from indeterminate mode.

The `Animator` implements the default animation thread, using the Swing `Timer` class. An `Animator` instance is created if necessary by the `BasicProgressBarUI.startAnimationTimer` method, which the property handler invokes when the progress bar switches to indeterminate mode. When the progress bar is indeterminate, the `Animator` timer fires an action event once every repaint interval milliseconds. `Animator`'s action event handler invokes `incrementAnimationIndex`, followed by `repaint` (which causes `paintIndeterminate` to run). Repaint interval is specified by the `ProgressBar.repaintInterval` UI default, which is checked by `startAnimationTimer`.

The `PropertyChangeHandler` registers itself as a property listener on the progress bar. When it detects the "indeterminate" property changing,the handler notes the change and invokes either `stopAnimationTimer` or `startAnimationTimer`.

### Javadoc for New API

In `javax.swing.JProgressBar`:

        public void setIndeterminate(boolean newValue)

        public boolean isIndeterminate()

In `javax.swing.plaf.basic.BasicProgressBarUI`:

        protected void paintIndeterminate(Graphics g, JComponent c)

        protected void paintDeterminate(Graphics g, JComponent c)

        protected int getAnimationIndex()

        protected void setAnimationIndex(int newValue)

        protected void incrementAnimationIndex()

        protected void startAnimationTimer()

        protected void stopAnimationTimer()

        protected Rectangle getBox(Rectangle r)

In `javax.swing.SwingUtilities`:

        public static Rectangle calculateInnerArea(JComponent c, Rectangle r)