/*
 * @(#)JProgressBar.java	1.89 01/12/03
 *
 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package javax.swing;

import java.awt.Color;
import java.awt.Graphics;

import java.text.Format;
import java.text.NumberFormat;

import java.io.Serializable;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.IOException;

import javax.swing.event.*;
import javax.accessibility.*;
import javax.swing.plaf.ProgressBarUI;


/**
 * A component that, by default, displays an integer value within a bounded 
 * interval. A progress bar typically communicates the progress of some 
 * work by displaying its percentage of completion and possibly a textual
 * display of this percentage.
 *
 * <p>
 *
 * To indicate that a task of unknown length is executing,
 * you can put a progress bar into indeterminate mode.
 * While the bar is in indeterminate mode,
 * it animates constantly to show that work is occurring.
 * As soon as you can determine the task's length and amount of progress,
 * you should update the progress bar's value
 * and switch it back to determinate mode.
 * 
 * <p>
 *
 * Here is an example of creating a progress bar,
 * where <code>task</code> is an object 
 * that returns information about the progress of some work:
 * 
 *<pre>
 *progressBar = new JProgressBar(0, task.getLengthOfTask());
 *progressBar.setValue(0);
 *progressBar.setStringPainted(true);
 *</pre>
 *
 * Here is an example of updating the value of the progress bar:
 *
 *<pre>
 *progressBar.setValue(task.getCurrent());
 *</pre>
 *
 * Here is an example of putting a progress bar into
 * indeterminate mode,
 * and then switching back to determinate mode
 * once the length of the task is known:
 *
 *<pre>
 *progressBar = new JProgressBar();
 *<em>...//when the task of (initially) unknown length begins:</em>
 *progressBar.setIndeterminate(true);
 *<em>...//do some work; get length of task...</em>
 *progressBar.setMaximum(newLength);
 *progressBar.setValue(newValue);
 *progressBar.setIndeterminate(false);
 *</pre>
 *
 * <p>
 * 
 * For complete examples and further documentation see
 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/progress.html" target="_top">How to Monitor Progress</a>,
 * a section in <em>The Java Tutorial.</em>
 *
 * <p>
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans<sup><font size="-2">TM</font></sup>
 * has been added to the <code>java.beans</code> package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @see javax.swing.plaf.basic.BasicProgressBarUI
 *
 * @beaninfo
 *      attribute: isContainer false
 *    description: A component that displays an integer value.
 *
 * @version 1.89 12/03/01
 * @author Michael C. Albers
 * @author Kathy Walrath
 */
public class JProgressBar extends JComponent implements SwingConstants, Accessible
{
    /**
     * @see #getUIClassID
     */
    private static final String uiClassID = "ProgressBarUI";

    /**
     * Whether the progress bar is horizontal or vertical.
     * The default is <code>HORIZONTAL</code>.
     *
     * @see #setOrientation
     */
    protected int orientation;

    /**
     * Whether to display a border around the progress bar.
     * The default is <code>true</code>.
     *
     * @see #setBorderPainted
     */
    protected boolean paintBorder;

    /**
     * The object that holds the data for the progress bar.
     *
     * @see #setModel
     */
    protected BoundedRangeModel model;

    /**
     * An optional string that can be displayed on the progress bar.
     * The default is <code>null</code>. Setting this to a non-<code>null</code>
     * value does not imply that the string will be displayed.
     *
     * @see #setString
     */
    protected String progressString;

    /**
     * Whether to textually display a string on the progress bar.
     * The default is <code>false</code>.
     * Setting this to <code>true</code> causes a textual
     * display of the progress to be rendered on the progress bar. If
     * the <code>progressString</code> is <code>null</code>,
     * the percentage of completion is displayed on the progress bar.
     * Otherwise, the <code>progressString</code> is
     * rendered on the progress bar.
     *
     * @see #setStringPainted
     */
    protected boolean paintString;

    /**
     * The default minimum for a progress bar is 0.
     */
    static final private int defaultMinimum = 0;
    /**
     * The default maximum for a progress bar is 100.
     */
    static final private int defaultMaximum = 100;
    /**
     * The default orientation for a progress bar is <code>HORIZONTAL</code>.
     */
    static final private int defaultOrientation = HORIZONTAL;

    /**
     * Only one <code>ChangeEvent</code> is needed per instance since the
     * event's only interesting property is the immutable source, which
     * is the progress bar.
     */
    protected transient ChangeEvent changeEvent = null;

    /**
     * Listens for change events sent by the progress bar's model,
     * redispatching them
     * to change-event listeners registered upon
     * this progress bar.
     *
     * @see #createChangeListener
     */
    protected ChangeListener changeListener = null;

    /**
     * Format used when displaying percent complete.
     */
    private transient Format format;

    /**
     * Whether the progress bar is indeterminate (<code>true</code>) or 
     * normal (<code>false</code>); the default is <code>false</code>.
     *
     * @see #setIndeterminate
     * @since 1.4
     */
    private boolean indeterminate;


   /**
     * Creates a horizontal progress bar
     * that displays a border but no progress string.
     * The initial and minimum values are 0,
     * and the maximum is 100.
     *
     * @see #setOrientation
     * @see #setBorderPainted
     * @see #setStringPainted
     * @see #setString
     * @see #setIndeterminate
     */
    public JProgressBar()
    {
	this(defaultOrientation);
    }

   /**
     * Creates a progress bar with the specified orientation,
     * which can be 
     * either <code>JProgressBar.VERTICAL</code> or 
     * <code>JProgressBar.HORIZONTAL</code>.
     * By default, a border is painted but a progress string is not.
     * The initial and minimum values are 0,
     * and the maximum is 100.
     *
     * @param orient  the desired orientation of the progress bar
     *
     * @see #setOrientation
     * @see #setBorderPainted
     * @see #setStringPainted
     * @see #setString
     * @see #setIndeterminate
     */
    public JProgressBar(int orient)
    {
	this(orient, defaultMinimum, defaultMaximum);
    }


    /**
     * Creates a horizontal progress bar
     * with the specified minimum and maximum.
     * Sets the initial value of the progress bar to the specified minimum.
     * By default, a border is painted but a progress string is not.
     * The <code>BoundedRangeModel</code> that holds the progress bar's data
     * handles any issues that may arise from improperly setting the 
     * minimum, initial, and maximum values on the progress bar.
     *
     * @param min  the minimum value of the progress bar
     * @param max  the maximum value of the progress bar
     *
     * @see BoundedRangeModel
     * @see #setOrientation
     * @see #setBorderPainted
     * @see #setStringPainted
     * @see #setString
     * @see #setIndeterminate
     */
    public JProgressBar(int min, int max)
    {
	this(defaultOrientation, min, max);
    }


    /**
     * Creates a progress bar using the specified orientation,
     * minimum, and maximum.
     * By default, a border is painted but a progress string is not.
     * Sets the initial value of the progress bar to the specified minimum.
     * The <code>BoundedRangeModel</code> that holds the progress bar's data
     * handles any issues that may arise from improperly setting the 
     * minimum, initial, and maximum values on the progress bar.
     *
     * @param orient  the desired orientation of the progress bar
     * @param min  the minimum value of the progress bar
     * @param max  the maximum value of the progress bar
     *
     * @see BoundedRangeModel
     * @see #setOrientation
     * @see #setBorderPainted
     * @see #setStringPainted
     * @see #setString
     * @see #setIndeterminate
     */
    public JProgressBar(int orient, int min, int max)
    {
	// Creating the model this way is a bit simplistic, but
	//  I believe that it is the the most common usage of this
	//  component - it's what people will expect.
        setModel(new DefaultBoundedRangeModel(min, 0, min, max));
        updateUI();

        setOrientation(orient);      // documented with set/getOrientation()
        setBorderPainted(true);      // documented with is/setBorderPainted()
	setStringPainted(false);     // see setStringPainted
	setString(null);             // see getString
	setIndeterminate(false);     // see setIndeterminate
    }


    /**
     * Creates a horizontal progress bar 
     * that uses the specified model
     * to hold the progress bar's data.
     * By default, a border is painted but a progress string is not.
     *
     * @param newModel  the data model for the progress bar
     *
     * @see #setOrientation
     * @see #setBorderPainted
     * @see #setStringPainted
     * @see #setString
     * @see #setIndeterminate
     */
    public JProgressBar(BoundedRangeModel newModel)
    {
        setModel(newModel);
        updateUI();

        setOrientation(defaultOrientation);  // see setOrientation()
        setBorderPainted(true);              // see setBorderPainted()
	setStringPainted(false);             // see setStringPainted
	setString(null);                     // see getString
	setIndeterminate(false);             // see setIndeterminate
    }


    /**
     * Returns <code>JProgressBar.VERTICAL</code> or 
     * <code>JProgressBar.HORIZONTAL</code>, depending on the orientation
     * of the progress bar. The default orientation is 
     * <code>HORIZONTAL</code>.
     *
     * @return <code>HORIZONTAL</code> or <code>VERTICAL</code>
     * @see #setOrientation
     */
    public int getOrientation() {
        return orientation;
    }


   /**
     * Sets the progress bar's orientation to <code>newOrientation</code>, 
     * which must be <code>JProgressBar.VERTICAL</code> or 
     * <code>JProgressBar.HORIZONTAL</code>. The default orientation 
     * is <code>HORIZONTAL</code>.
     *
     * @param  newOrientation  <code>HORIZONTAL</code> or <code>VERTICAL</code>
     * @exception      IllegalArgumentException    if <code>newOrientation</code>
     *                                              is an illegal value
     * @see #getOrientation
     *
     * @beaninfo
     *    preferred: true
     *        bound: true
     *    attribute: visualUpdate true
     *  description: Set the progress bar's orientation.
     */
    public void setOrientation(int newOrientation) {
        if (orientation != newOrientation) {
            switch (newOrientation) {
            case VERTICAL:
            case HORIZONTAL:
                int oldOrientation = orientation;
                orientation = newOrientation;
                firePropertyChange("orientation", oldOrientation, newOrientation);
                if (accessibleContext != null) {
                    accessibleContext.firePropertyChange(
			    AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
                            ((oldOrientation == VERTICAL) 
			     ? AccessibleState.VERTICAL 
			     : AccessibleState.HORIZONTAL),
                            ((orientation == VERTICAL) 
			     ? AccessibleState.VERTICAL 
			     : AccessibleState.HORIZONTAL));
	        }
                break;
            default:
                throw new IllegalArgumentException(newOrientation +
                                             " is not a legal orientation");
            }
            revalidate();
        }
    }


    /**
     * Returns the value of the <code>stringPainted</code> property.
     *
     * @return the value of the <code>stringPainted</code> property
     * @see    #setStringPainted
     * @see    #setString
     */
    public boolean isStringPainted() {
        return paintString;
    }


    /**
     * Sets the value of the <code>stringPainted</code> property,
     * which determines whether the progress bar
     * should render a progress string.
     * The default is <code>false</code>:
     * no string is painted.
     * Some look and feels might not support progress strings
     * or might support them only when the progress bar is in determinate mode.
     *
     * @param   b       <code>true</code> if the progress bar should render a string
     * @see     #isStringPainted
     * @see     #setString
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: Whether the progress bar should render a string.
     */
    public void setStringPainted(boolean b) {
        //PENDING: specify that string not painted when in indeterminate mode?
	//         or just leave that to the L&F?
        boolean oldValue = paintString;
        paintString = b;
        firePropertyChange("stringPainted", oldValue, paintString);
        if (paintString != oldValue) {
	    revalidate();
            repaint();
        }
    }


    /**
     * Returns the current value of the progress string.
     * If you are providing a custom progress string 
     * by overriding this method,
     * make sure your implementation calls <code>setString</code> before
     * calling <code>super.getString</code>.
     *
     * @return the value of the percent string
     * @see    #setString
     */
    public String getString(){
	if (progressString != null) {
	    return progressString;
	} else {
            if (format == null) {
                format = NumberFormat.getPercentInstance();
            }
            return format.format(new Double(getPercentComplete()));
        }
    }

    /**
     * Sets the value of the progress string. By default,
     * this string is <code>null</code>.
     * If you have provided a custom progress string and want to revert to 
     * the built-in behavior, set the string back to <code>null</code>.
     * If you are providing a custom progress string 
     * by overriding this method,
     * make sure that you call <code>setString</code> before
     * calling <code>getString</code>.
     * The progress string is painted only if
     * the <code>isStringPainted</code> method returns <code>true</code>.
     *
     * @param  s       the value of the percent string
     * @see    #getString
     * @see    #setStringPainted
     * @see    #isStringPainted
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: Specifies the progress string to paint
     */
    public void setString(String s){
        String oldValue = progressString;
	progressString = s;
        firePropertyChange("string", oldValue, progressString);
        if (progressString == null || oldValue == null || !progressString.equals(oldValue)) {
	    repaint();
        }
    }

    /**
     * Returns the percent complete for the progress bar.
     * Note that this number is between 0.0 and 1.0.
     *
     * @return the percent complete for this progress bar
     */
    public double getPercentComplete() {
	long span = model.getMaximum() - model.getMinimum();
	double currentValue = model.getValue();
	double pc = (currentValue - model.getMinimum()) / span;
	return pc;
    }

    /**
     * Returns the <code>borderPainted</code> property.
     *
     * @return the value of the <code>borderPainted</code> property
     * @see    #setBorderPainted
     * @beaninfo
     *  description: Does the progress bar paint its border
     */
    public boolean isBorderPainted() {
        return paintBorder;
    }

    /**
     * Sets the <code>borderPainted</code> property, which is
     * <code>true</code> if the progress bar should paint its border.
     * The default value for this property is <code>true</code>.
     * Some look and feels might not implement painted borders;
     * they will ignore this property.
     *
     * @param   b       <code>true</code> if the progress bar
     *                  should paint its border;
     *                  otherwise, <code>false</code>
     * @see     #isBorderPainted
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: Whether the progress bar should paint its border.
     */
    public void setBorderPainted(boolean b) {
        boolean oldValue = paintBorder;
        paintBorder = b;
        firePropertyChange("borderPainted", oldValue, paintBorder);
        if (paintBorder != oldValue) {
            repaint();
        }
    }

    /**
     * Paints the progress bar's border if the <code>borderPainted</code>
     * property is <code>true</code>.
     * 
     * @param g  the <code>Graphics</code> context within which to paint the border
     * @see #paint
     * @see #setBorder
     * @see #isBorderPainted
     * @see #setBorderPainted
     */
    protected void paintBorder(Graphics g) {    
        if (isBorderPainted()) {
            super.paintBorder(g);
        }
    }


    /**
     * Returns the look-and-feel object that renders this component.
     *
     * @return the <code>ProgressBarUI</code> object that renders this component
     */
    public ProgressBarUI getUI() {
        return (ProgressBarUI)ui;
    }

    /**
     * Sets the look-and-feel object that renders this component.
     *
     * @param ui  a <code>ProgressBarUI</code> object
     * @see UIDefaults#getUI
     * @beaninfo
     *        bound: true
     *       hidden: true
     *    attribute: visualUpdate true
     *  description: The UI object that implements the Component's LookAndFeel. 
     */
    public void setUI(ProgressBarUI ui) {
        super.setUI(ui);
    }


    /**
     * Resets the UI property to a value from the current look and feel.
     *
     * @see JComponent#updateUI
     */
    public void updateUI() {
        setUI((ProgressBarUI)UIManager.getUI(this));
    }


    /**
     * Returns the name of the look-and-feel class that renders this component.
     *
     * @return the string "ProgressBarUI"
     * @see JComponent#getUIClassID
     * @see UIDefaults#getUI
     * @beaninfo
     *        expert: true
     *   description: A string that specifies the name of the look-and-feel class.
     */
    public String getUIClassID() {
        return uiClassID;
    }


    /* We pass each Change event to the listeners with the
     * the progress bar as the event source.
     * <p>
     * <strong>Warning:</strong>
     * Serialized objects of this class will not be compatible with
     * future Swing releases. The current serialization support is
     * appropriate for short term storage or RMI between applications running
     * the same version of Swing.  As of 1.4, support for long term storage
     * of all JavaBeans<sup><font size="-2">TM</font></sup>
     * has been added to the <code>java.beans</code> package.
     * Please see {@link java.beans.XMLEncoder}.
     */
    private class ModelListener implements ChangeListener, Serializable {
        public void stateChanged(ChangeEvent e) {
            fireStateChanged();
        }
    }

    /**
     * Subclasses that want to handle change events 
     * from the model differently
     * can override this to return 
     * an instance of a custom <code>ChangeListener</code> implementation.
     *
     * @see #changeListener
     * @see javax.swing.event.ChangeListener
     * @see javax.swing.BoundedRangeModel
     */
    protected ChangeListener createChangeListener() {
        return new ModelListener();
    }

    /**
     * Adds the specified <code>ChangeListener</code> to the progress bar.
     *
     * @param l the <code>ChangeListener</code> to add
     */
    public void addChangeListener(ChangeListener l) {
        listenerList.add(ChangeListener.class, l);
    }
    
    /**
     * Removes a <code>ChangeListener</code> from the progress bar.
     *
     * @param l the <code>ChangeListener</code> to remove
     */
    public void removeChangeListener(ChangeListener l) {
        listenerList.remove(ChangeListener.class, l);
    }
        
    /**
     * Returns an array of all the <code>ChangeListener</code>s added
     * to this progress bar with <code>addChangeListener</code>.
     *
     * @return all of the <code>ChangeListener</code>s added or an empty
     *         array if no listeners have been added
     * @since 1.4
     */
    public ChangeListener[] getChangeListeners() {
        return (ChangeListener[])listenerList.getListeners(
                ChangeListener.class);
    }

    /**
     * Notifies all listeners that have registered interest in
     * <code>ChangeEvent</code>s.
     * The event instance 
     * is created if necessary.
     *
     * @see EventListenerList
     */
    protected void fireStateChanged() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==ChangeListener.class) {
                // Lazily create the event:
                if (changeEvent == null)
                    changeEvent = new ChangeEvent(this);
                ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
            }          
        }
    } 
      
    /**
     * Returns the data model used by this progress bar.
     *
     * @return the <code>BoundedRangeModel</code> currently in use
     * @see    BoundedRangeModel
     */
    public BoundedRangeModel getModel() {
        return model;
    }

    /**
     * Sets the data model used by the <code>JProgressBar</code>.
     *
     * @param  newModel the <code>BoundedRangeModel</code> to use
     *
     * @beaninfo
     *    expert: true
     * description: The data model used by the JProgressBar.
     */
    public void setModel(BoundedRangeModel newModel) {
        // PENDING(???) setting the same model to multiple bars is broken; listeners
        BoundedRangeModel oldModel = getModel();

        if (newModel != oldModel) {
            if (oldModel != null) {
                oldModel.removeChangeListener(changeListener);
                changeListener = null;
            }

            model = newModel;

            if (newModel != null) {
                changeListener = createChangeListener();
                newModel.addChangeListener(changeListener);
            }

	    if (accessibleContext != null) {
                accessibleContext.firePropertyChange(
		        AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
                        (oldModel== null 
			 ? null : new Integer(oldModel.getValue())),
                        (newModel== null 
			 ? null : new Integer(newModel.getValue())));
	    }

	    if (model != null) {
		model.setExtent(0);
	    }
            repaint();
        }
    }


    /* All of the model methods are implemented by delegation. */

    /**
     * Returns the progress bar's current value,
     * which is stored in the progress bar's <code>BoundedRangeModel</code>.
     * The value is always between the 
     * minimum and maximum values, inclusive. By default, the 
     * value is initialized to be equal to the minimum value.
     *
     * @return  the current value of the progress bar
     * @see     #setValue
     * @see     BoundedRangeModel#getValue
     */
    public int getValue() { return getModel().getValue(); }

    /**
     * Returns the progress bar's minimum value,
     * which is stored in the progress bar's <code>BoundedRangeModel</code>.
     * By default, the minimum value is <code>0</code>.
     *
     * @return  the progress bar's minimum value
     * @see     #setMinimum
     * @see     BoundedRangeModel#getMinimum
     */
    public int getMinimum() { return getModel().getMinimum(); }

    /**
     * Returns the progress bar's maximum value,
     * which is stored in the progress bar's <code>BoundedRangeModel</code>.
     * By default, the maximum value is <code>100</code>.
     *
     * @return  the progress bar's maximum value
     * @see     #setMaximum
     * @see     BoundedRangeModel#getMaximum
     */
    public int getMaximum() { return getModel().getMaximum(); }

    /**
     * Sets the progress bar's current value 
     * (stored in the progress bar's data model) to <code>n</code>.
     * The data model (a <code>BoundedRangeModel</code> instance)
     * handles any mathematical
     * issues arising from assigning faulty values.
     * <p>
     * If the new value is different from the previous value,
     * all change listeners are notified.
     *
     * @param   n       the new value
     * @see     #getValue
     * @see     BoundedRangeModel#setValue
     * @beaninfo
     *    preferred: true
     *  description: The progress bar's current value.
     */
    public void setValue(int n) { 
        BoundedRangeModel brm = getModel();
	int oldValue = brm.getValue();
	brm.setValue(n);
 
	if (accessibleContext != null) {
            accessibleContext.firePropertyChange(
		    AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
                    new Integer(oldValue),
                    new Integer(brm.getValue()));
	}
    }

    /**
     * Sets the progress bar's minimum value 
     * (stored in the progress bar's data model) to <code>n</code>.
     * The data model (a <code>BoundedRangeModel</code> instance)
     * handles any mathematical
     * issues arising from assigning faulty values.
     * <p>
     * If the minimum value is different from the previous minimum,
     * all change listeners are notified.
     *
     * @param  n       the new minimum
     * @see    #getMinimum
     * @see    #addChangeListener
     * @see    BoundedRangeModel#setMinimum
     * @beaninfo
     *  preferred: true
     * description: The progress bar's minimum value.
     */
    public void setMinimum(int n) { getModel().setMinimum(n); }

    /**
     * Sets the progress bar's maximum value
     * (stored in the progress bar's data model) to <code>n</code>.
     * The underlying <code>BoundedRangeModel</code> handles any mathematical
     * issues arising from assigning faulty values.
     * <p>
     * If the maximum value is different from the previous maximum,
     * all change listeners are notified.
     *
     * @param  n       the new maximum
     * @see    #getMaximum
     * @see    #addChangeListener
     * @see    BoundedRangeModel#setMaximum
     * @beaninfo
     *    preferred: true
     *  description: The progress bar's maximum value.
     */
    public void setMaximum(int n) { getModel().setMaximum(n); }

    /**
     * Sets the <code>indeterminate</code> property of the progress bar,
     * which determines whether the progress bar is in determinate
     * or indeterminate mode.
     * By default, the progress bar is determinate 
     * and this method returns <code>false</code>.
     * An indeterminate progress bar continuously displays animation
     * indicating that an operation of unknown length is occurring.
     * By default, this property is <code>false</code>.
     * Some look and feels might not support indeterminate progress bars;
     * they will ignore this property.
     * 
     * <p>
     *
     * See 
     * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/progress.html" target="_top">How to Monitor Progress</a>
     * for examples of using indeterminate progress bars.
     *
     * @param newValue	<code>true</code> if the progress bar
     * 			should change to indeterminate mode;
     * 			<code>false</code> if it should revert to normal.
     *
     * @see #isIndeterminate
     * @see javax.swing.plaf.basic.BasicProgressBarUI
     * 
     * @since 1.4
     *
     * @beaninfo
     *        bound: true
     *    attribute: visualUpdate true
     *  description: Set whether the progress bar is indeterminate (true)
     * 		     or normal (false).
     */
    public void setIndeterminate(boolean newValue) {
	boolean oldValue = indeterminate;
	indeterminate = newValue;
	firePropertyChange("indeterminate", oldValue, indeterminate);
    }

    /**
     * Returns the value of the <code>indeterminate</code> property.
     *
     * @return the value of the <code>indeterminate</code> property
     * @see    #setIndeterminate
     *
     * @since 1.4
     *
     * @beaninfo  
     *  description: Is the progress bar indeterminate (true)
     *               or normal (false)?
     */
    public boolean isIndeterminate() {
        return indeterminate;
    }


    /** 
     * See readObject() and writeObject() in JComponent for more 
     * information about serialization in Swing.
     */
    private void writeObject(ObjectOutputStream s) throws IOException {
        s.defaultWriteObject();
        if (getUIClassID().equals(uiClassID)) {
            byte count = JComponent.getWriteObjCounter(this);
            JComponent.setWriteObjCounter(this, --count);
            if (count == 0 && ui != null) {
                ui.installUI(this);
            }
        }
    }


    /**
     * Returns a string representation of this <code>JProgressBar</code>.
     * This method is intended to be used only for debugging purposes. The 
     * content and format of the returned string may vary between
     * implementations. The returned string may be empty but may not 
     * be <code>null</code>.
     * 
     * @return  a string representation of this <code>JProgressBar</code>
     */
    protected String paramString() {
	String orientationString = (orientation == HORIZONTAL ?
				    "HORIZONTAL" : "VERTICAL");
	String paintBorderString = (paintBorder ?
				    "true" : "false");
	String progressStringString = (progressString != null ?
				       progressString : "");
	String paintStringString = (paintString ?
				    "true" : "false");
	String indeterminateString = (indeterminate ?
				    "true" : "false");

	return super.paramString() +
	",orientation=" + orientationString +
	",paintBorder=" + paintBorderString +
	",paintString=" + paintStringString +
	",progressString=" + progressStringString +
	",indeterminateString=" + indeterminateString;
    }

/////////////////
// Accessibility support
////////////////

    /**
     * Gets the <code>AccessibleContext</code> associated with this 
     * <code>JProgressBar</code>. For progress bars, the
     * <code>AccessibleContext</code> takes the form of an 
     * <code>AccessibleJProgressBar</code>. 
     * A new <code>AccessibleJProgressBar</code> instance is created if necessary.
     *
     * @return an <code>AccessibleJProgressBar</code> that serves as the 
     *         <code>AccessibleContext</code> of this <code>JProgressBar</code>
     * @beaninfo
     *       expert: true
     *  description: The AccessibleContext associated with this ProgressBar.
     */
    public AccessibleContext getAccessibleContext() {
        if (accessibleContext == null) {
            accessibleContext = new AccessibleJProgressBar();
        }
        return accessibleContext;
    }

    /**
     * This class implements accessibility support for the 
     * <code>JProgressBar</code> class.  It provides an implementation of the 
     * Java Accessibility API appropriate to progress bar user-interface 
     * elements.
     * <p>
     * <strong>Warning:</strong>
     * Serialized objects of this class will not be compatible with
     * future Swing releases. The current serialization support is
     * appropriate for short term storage or RMI between applications running
     * the same version of Swing.  As of 1.4, support for long term storage
     * of all JavaBeans<sup><font size="-2">TM</font></sup>
     * has been added to the <code>java.beans</code> package.
     * Please see {@link java.beans.XMLEncoder}.
     */
    protected class AccessibleJProgressBar extends AccessibleJComponent
        implements AccessibleValue {

        /**
         * Gets the state set of this object.
         *
         * @return an instance of AccessibleState containing the current state 
         * of the object
         * @see AccessibleState
         */
        public AccessibleStateSet getAccessibleStateSet() {
            AccessibleStateSet states = super.getAccessibleStateSet();
            if (getModel().getValueIsAdjusting()) {
                states.add(AccessibleState.BUSY);
            }
            if (getOrientation() == VERTICAL) {
                states.add(AccessibleState.VERTICAL);
            } else {
                states.add(AccessibleState.HORIZONTAL);
            }
            return states;
        }

        /**
         * Gets the role of this object.
         *
         * @return an instance of AccessibleRole describing the role of the 
         * object
         */
        public AccessibleRole getAccessibleRole() {
            return AccessibleRole.PROGRESS_BAR;
        }

        /**
         * Gets the <code>AccessibleValue</code> associated with this object.  In the
         * implementation of the Java Accessibility API for this class, 
	 * returns this object, which is responsible for implementing the
         * <code>AccessibleValue</code> interface on behalf of itself.
	 * 
	 * @return this object
         */
        public AccessibleValue getAccessibleValue() {
            return this;
        }

        /**
         * Gets the accessible value of this object.
         *
         * @return the current value of this object
         */
        public Number getCurrentAccessibleValue() {
            return new Integer(getValue());
        }

        /**
         * Sets the value of this object as a <code>Number</code>.
         *
         * @return <code>true</code> if the value was set
         */
        public boolean setCurrentAccessibleValue(Number n) {
            if (n instanceof Integer) {
                setValue(n.intValue());
                return true;
            } else {
                return false;
            }
        }

        /**
         * Gets the minimum accessible value of this object.
         *
         * @return the minimum value of this object
         */
        public Number getMinimumAccessibleValue() {
            return new Integer(getMinimum());
        }

        /**
         * Gets the maximum accessible value of this object.
         *
         * @return the maximum value of this object
         */
        public Number getMaximumAccessibleValue() {
            return new Integer(getMaximum());
        }

    } // AccessibleJProgressBar
}
