Use Tree Navigation
public class

BasicProgressBarUI.ChangeHandler

extends Object
implements ChangeListener
/*
 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package javax.swing.plaf.basic;

import sun.swing.SwingUtilities2;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.io.Serializable;
import sun.swing.DefaultLookup;

/**
 * A Basic L&F implementation of ProgressBarUI.
 *
 * @author Michael C. Albers
 * @author Kathy Walrath
 */

public class BasicProgressBarUI extends ProgressBarUI {
   
private int cachedPercent;
   
private int cellLength, cellSpacing;
   
// The "selectionForeground" is the color of the text when it is painted
   
// over a filled area of the progress bar. The "selectionBackground"
   
// is for the text over the unfilled progress bar area.
   
private Color selectionForeground, selectionBackground;

   
private Animator animator;

   
protected JProgressBar progressBar;
   
protected ChangeListener changeListener;
   
private Handler handler;

   
/**
     * The current state of the indeterminate animation's cycle.
     * 0, the initial value, means paint the first frame.
     * When the progress bar is indeterminate and showing,
     * the default animation thread updates this variable
     * by invoking incrementAnimationIndex()
     * every repaintInterval milliseconds.
     */

   
private int animationIndex = 0;

   
/**
     * The number of frames per cycle. Under the default implementation,
     * this depends on the cycleTime and repaintInterval.  It
     * must be an even number for the default painting algorithm.  This
     * value is set in the initIndeterminateValues method.
     */

   
private int numFrames;   //0 1|numFrames-1 ... numFrames/2

   
/**
     * Interval (in ms) between repaints of the indeterminate progress bar.
     * The value of this method is set
     * (every time the progress bar changes to indeterminate mode)
     * using the
     * "ProgressBar.repaintInterval" key in the defaults table.
     */

   
private int repaintInterval;

   
/**
     * The number of milliseconds until the animation cycle repeats.
     * The value of this method is set
     * (every time the progress bar changes to indeterminate mode)
     * using the
     * "ProgressBar.cycleTime" key in the defaults table.
     */

   
private int cycleTime;  //must be repaintInterval*2*aPositiveInteger

   
//performance stuff
   
private static boolean ADJUSTTIMER = true; //makes a BIG difference;
                                               
//make this false for
                                               
//performance tests

   
/**
     * Used to hold the location and size of the bouncing box (returned
     * by getBox) to be painted.
     *
     * @since 1.5
     */

   
protected Rectangle boxRect;

   
/**
     * The rectangle to be updated the next time the
     * animation thread calls repaint.  For bouncing-box
     * animation this rect should include the union of
     * the currently displayed box (which needs to be erased)
     * and the box to be displayed next.
     * This rectangle's values are set in
     * the setAnimationIndex method.
     */

   
private Rectangle nextPaintRect;

   
//cache
   
/** The component's painting area, not including the border. */
   
private Rectangle componentInnards;    //the current painting area
   
private Rectangle oldComponentInnards; //used to see if the size changed

   
/** For bouncing-box animation, the change in position per frame. */
   
private double delta = 0.0;

   
private int maxPosition = 0; //maximum X (horiz) or Y box location


   
public static ComponentUI createUI(JComponent x) {
       
return new BasicProgressBarUI();
   
}

   
public void installUI(JComponent c) {
        progressBar
= (JProgressBar)c;
        installDefaults
();
        installListeners
();
       
if (progressBar.isIndeterminate()) {
            initIndeterminateValues
();
       
}
   
}

   
public void uninstallUI(JComponent c) {
       
if (progressBar.isIndeterminate()) {
            cleanUpIndeterminateValues
();
       
}
        uninstallDefaults
();
        uninstallListeners
();
        progressBar
= null;
   
}

   
protected void installDefaults() {
       
LookAndFeel.installProperty(progressBar, "opaque", Boolean.TRUE);
       
LookAndFeel.installBorder(progressBar,"ProgressBar.border");
       
LookAndFeel.installColorsAndFont(progressBar,
                                         
"ProgressBar.background",
                                         
"ProgressBar.foreground",
                                         
"ProgressBar.font");
        cellLength
= UIManager.getInt("ProgressBar.cellLength");
       
if (cellLength == 0) cellLength = 1;
        cellSpacing
= UIManager.getInt("ProgressBar.cellSpacing");
        selectionForeground
= UIManager.getColor("ProgressBar.selectionForeground");
        selectionBackground
= UIManager.getColor("ProgressBar.selectionBackground");
   
}

   
protected void uninstallDefaults() {
       
LookAndFeel.uninstallBorder(progressBar);
   
}

   
protected void installListeners() {
       
//Listen for changes in the progress bar's data.
        changeListener
= getHandler();
        progressBar
.addChangeListener(changeListener);

       
//Listen for changes between determinate and indeterminate state.
        progressBar
.addPropertyChangeListener(getHandler());
   
}

   
private Handler getHandler() {
       
if (handler == null) {
            handler
= new Handler();
       
}
       
return handler;
   
}

   
/**
     * Starts the animation thread, creating and initializing
     * it if necessary. This method is invoked when an
     * indeterminate progress bar should start animating.
     * Reasons for this may include:
     * <ul>
     *    <li>The progress bar is determinate and becomes displayable
     *    <li>The progress bar is displayable and becomes determinate
     *    <li>The progress bar is displayable and determinate and this
     *        UI is installed
     * </ul>
     * If you implement your own animation thread,
     * you must override this method.
     *
     * @since 1.4
     * @see #stopAnimationTimer
     */

   
protected void startAnimationTimer() {
       
if (animator == null) {
            animator
= new Animator();
       
}

        animator
.start(getRepaintInterval());
   
}

   
/**
     * Stops the animation thread.
     * This method is invoked when the indeterminate
     * animation should be stopped. Reasons for this may include:
     * <ul>
     *    <li>The progress bar changes to determinate
     *    <li>The progress bar is no longer part of a displayable hierarchy
     *    <li>This UI in uninstalled
     * </ul>
     * If you implement your own animation thread,
     * you must override this method.
     *
     * @since 1.4
     * @see #startAnimationTimer
     */

   
protected void stopAnimationTimer() {
       
if (animator != null) {
            animator
.stop();
       
}
   
}

   
/**
     * Removes all listeners installed by this object.
     */

   
protected void uninstallListeners() {
        progressBar
.removeChangeListener(changeListener);
        progressBar
.removePropertyChangeListener(getHandler());
        handler
= null;
   
}


   
/**
     * Returns the baseline.
     *
     * @throws NullPointerException {@inheritDoc}
     * @throws IllegalArgumentException {@inheritDoc}
     * @see javax.swing.JComponent#getBaseline(int, int)
     * @since 1.6
     */

   
public int getBaseline(JComponent c, int width, int height) {
       
super.getBaseline(c, width, height);
       
if (progressBar.isStringPainted() &&
                progressBar
.getOrientation() == JProgressBar.HORIZONTAL) {
           
FontMetrics metrics = progressBar.
                    getFontMetrics
(progressBar.getFont());
           
Insets insets = progressBar.getInsets();
           
int y = insets.top;
            height
= height - insets.top - insets.bottom;
           
return y + (height + metrics.getAscent() -
                        metrics
.getLeading() -
                        metrics
.getDescent()) / 2;
       
}
       
return -1;
   
}

   
/**
     * Returns an enum indicating how the baseline of the component
     * changes as the size changes.
     *
     * @throws NullPointerException {@inheritDoc}
     * @see javax.swing.JComponent#getBaseline(int, int)
     * @since 1.6
     */

   
public Component.BaselineResizeBehavior getBaselineResizeBehavior(
           
JComponent c) {
       
super.getBaselineResizeBehavior(c);
       
if (progressBar.isStringPainted() &&
                progressBar
.getOrientation() == JProgressBar.HORIZONTAL) {
           
return Component.BaselineResizeBehavior.CENTER_OFFSET;
       
}
       
return Component.BaselineResizeBehavior.OTHER;
   
}

   
// Many of the Basic*UI components have the following methods.
   
// This component does not have these methods because *ProgressBarUI
   
//  is not a compound component and does not accept input.
   
//
   
// protected void installComponents()
   
// protected void uninstallComponents()
   
// protected void installKeyboardActions()
   
// protected void uninstallKeyboardActions()

   
protected Dimension getPreferredInnerHorizontal() {
       
Dimension horizDim = (Dimension)DefaultLookup.get(progressBar, this,
           
"ProgressBar.horizontalSize");
       
if (horizDim == null) {
            horizDim
= new Dimension(146, 12);
       
}
       
return horizDim;
   
}

   
protected Dimension getPreferredInnerVertical() {
       
Dimension vertDim = (Dimension)DefaultLookup.get(progressBar, this,
           
"ProgressBar.verticalSize");
       
if (vertDim == null) {
            vertDim
= new Dimension(12, 146);
       
}
       
return vertDim;
   
}

   
/**
     * The "selectionForeground" is the color of the text when it is painted
     * over a filled area of the progress bar.
     */

   
protected Color getSelectionForeground() {
       
return selectionForeground;
   
}

   
/**
     * The "selectionBackground" is the color of the text when it is painted
     * over an unfilled area of the progress bar.
     */

   
protected Color getSelectionBackground() {
       
return selectionBackground;
   
}

   
private int getCachedPercent() {
       
return cachedPercent;
   
}

   
private void setCachedPercent(int cachedPercent) {
       
this.cachedPercent = cachedPercent;
   
}

   
/**
     * Returns the width (if HORIZONTAL) or height (if VERTICAL)
     * of each of the indivdual cells/units to be rendered in the
     * progress bar. However, for text rendering simplification and
     * aesthetic considerations, this function will return 1 when
     * the progress string is being rendered.
     *
     * @return the value representing the spacing between cells
     * @see    #setCellLength
     * @see    JProgressBar#isStringPainted
     */

   
protected int getCellLength() {
       
if (progressBar.isStringPainted()) {
           
return 1;
       
} else {
           
return cellLength;
       
}
   
}

   
protected void setCellLength(int cellLen) {
       
this.cellLength = cellLen;
   
}

   
/**
     * Returns the spacing between each of the cells/units in the
     * progress bar. However, for text rendering simplification and
     * aesthetic considerations, this function will return 0 when
     * the progress string is being rendered.
     *
     * @return the value representing the spacing between cells
     * @see    #setCellSpacing
     * @see    JProgressBar#isStringPainted
     */

   
protected int getCellSpacing() {
       
if (progressBar.isStringPainted()) {
           
return 0;
       
} else {
           
return cellSpacing;
       
}
   
}

   
protected void setCellSpacing(int cellSpace) {
       
this.cellSpacing = cellSpace;
   
}

   
/**
     * This determines the amount of the progress bar that should be filled
     * based on the percent done gathered from the model. This is a common
     * operation so it was abstracted out. It assumes that your progress bar
     * is linear. That is, if you are making a circular progress indicator,
     * you will want to override this method.
     */

   
protected int getAmountFull(Insets b, int width, int height) {
       
int amountFull = 0;
       
BoundedRangeModel model = progressBar.getModel();

       
if ( (model.getMaximum() - model.getMinimum()) != 0) {
           
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
                amountFull
= (int)Math.round(width *
                                             progressBar
.getPercentComplete());
           
} else {
                amountFull
= (int)Math.round(height *
                                             progressBar
.getPercentComplete());
           
}
       
}
       
return amountFull;
   
}

   
/**
     * Delegates painting to one of two methods:
     * paintDeterminate or paintIndeterminate.
     */

   
public void paint(Graphics g, JComponent c) {
       
if (progressBar.isIndeterminate()) {
            paintIndeterminate
(g, c);
       
} else {
            paintDeterminate
(g, c);
       
}
   
}

   
/**
     * Stores the position and size of
     * the bouncing box that would be painted for the current animation index
     * in <code>r</code> and returns <code>r</code>.
     * Subclasses that add to the painting performed
     * in this class's implementation of <code>paintIndeterminate</code> --
     * to draw an outline around the bouncing box, for example --
     * can use this method to get the location of the bouncing
     * box that was just painted.
     * By overriding this method,
     * you have complete control over the size and position
     * of the bouncing box,
     * without having to reimplement <code>paintIndeterminate</code>.
     *
     * @param r  the Rectangle instance to be modified;
     *           may be <code>null</code>
     * @return   <code>null</code> if no box should be drawn;
     *           otherwise, returns the passed-in rectangle
     *           (if non-null)
     *           or a new rectangle
     *
     * @see #setAnimationIndex
     * @since 1.4
     */

   
protected Rectangle getBox(Rectangle r) {
       
int currentFrame = getAnimationIndex();
       
int middleFrame = numFrames/2;

       
if (sizeChanged() || delta == 0.0 || maxPosition == 0.0) {
            updateSizes
();
       
}

        r
= getGenericBox(r);

       
if (r == null) {
           
return null;
       
}
       
if (middleFrame <= 0) {
           
return null;
       
}

       
//assert currentFrame >= 0 && currentFrame < numFrames
       
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
           
if (currentFrame < middleFrame) {
                r
.x = componentInnards.x
                     
+ (int)Math.round(delta * (double)currentFrame);
           
} else {
                r
.x = maxPosition
                     
- (int)Math.round(delta *
                                       
(currentFrame - middleFrame));
           
}
       
} else { //VERTICAL indeterminate progress bar
           
if (currentFrame < middleFrame) {
                r
.y = componentInnards.y
                     
+ (int)Math.round(delta * currentFrame);
           
} else {
                r
.y = maxPosition
                     
- (int)Math.round(delta *
                                       
(currentFrame - middleFrame));
           
}
       
}
       
return r;
   
}

   
/**
     * Updates delta, max position.
     * Assumes componentInnards is correct (e.g. call after sizeChanged()).
     */

   
private void updateSizes() {
       
int length = 0;

       
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
            length
= getBoxLength(componentInnards.width,
                                  componentInnards
.height);
            maxPosition
= componentInnards.x + componentInnards.width
                         
- length;

       
} else { //VERTICAL progress bar
            length
= getBoxLength(componentInnards.height,
                                  componentInnards
.width);
            maxPosition
= componentInnards.y + componentInnards.height
                         
- length;
       
}

       
//If we're doing bouncing-box animation, update delta.
        delta
= 2.0 * (double)maxPosition/(double)numFrames;
   
}

   
/**
     * Assumes that the component innards, max position, etc. are up-to-date.
     */

   
private Rectangle getGenericBox(Rectangle r) {
       
if (r == null) {
            r
= new Rectangle();
       
}

       
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
            r
.width = getBoxLength(componentInnards.width,
                                   componentInnards
.height);
           
if (r.width < 0) {
                r
= null;
           
} else {
                r
.height = componentInnards.height;
                r
.y = componentInnards.y;
           
}
         
// end of HORIZONTAL

       
} else { //VERTICAL progress bar
            r
.height = getBoxLength(componentInnards.height,
                                    componentInnards
.width);
           
if (r.height < 0) {
                r
= null;
           
} else {
                r
.width = componentInnards.width;
                r
.x = componentInnards.x;
           
}
       
} // end of VERTICAL

       
return r;
   
}

   
/**
     * Returns the length
     * of the "bouncing box" to be painted.
     * This method is invoked by the
     * default implementation of <code>paintIndeterminate</code>
     * to get the width (if the progress bar is horizontal)
     * or height (if vertical) of the box.
     * For example:
     * <blockquote>
     * <pre>
     *boxRect.width = getBoxLength(componentInnards.width,
     *                             componentInnards.height);
     * </pre>
     * </blockquote>
     *
     * @param availableLength  the amount of space available
     *                         for the bouncing box to move in;
     *                         for a horizontal progress bar,
     *                         for example,
     *                         this should be
     *                         the inside width of the progress bar
     *                         (the component width minus borders)
     * @param otherDimension   for a horizontal progress bar, this should be
     *                         the inside height of the progress bar; this
     *                         value might be used to constrain or determine
     *                         the return value
     *
     * @return the size of the box dimension being determined;
     *         must be no larger than <code>availableLength</code>
     *
     * @see javax.swing.SwingUtilities#calculateInnerArea
     * @since 1.5
     */

   
protected int getBoxLength(int availableLength, int otherDimension) {
       
return (int)Math.round(availableLength/6.0);
   
}

   
/**
     * All purpose paint method that should do the right thing for all
     * linear bouncing-box progress bars.
     * Override this if you are making another kind of
     * progress bar.
     *
     * @see #paintDeterminate
     *
     * @since 1.4
     */

   
protected void paintIndeterminate(Graphics g, JComponent c) {
       
if (!(g instanceof Graphics2D)) {
           
return;
       
}

       
Insets b = progressBar.getInsets(); // area for border
       
int barRectWidth = progressBar.getWidth() - (b.right + b.left);
       
int barRectHeight = progressBar.getHeight() - (b.top + b.bottom);

       
if (barRectWidth <= 0 || barRectHeight <= 0) {
           
return;
       
}

       
Graphics2D g2 = (Graphics2D)g;

       
// Paint the bouncing box.
        boxRect
= getBox(boxRect);
       
if (boxRect != null) {
            g2
.setColor(progressBar.getForeground());
            g2
.fillRect(boxRect.x, boxRect.y,
                       boxRect
.width, boxRect.height);
       
}

       
// Deal with possible text painting
       
if (progressBar.isStringPainted()) {
           
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
                paintString
(g2, b.left, b.top,
                            barRectWidth
, barRectHeight,
                            boxRect
.x, boxRect.width, b);
           
}
           
else {
                paintString
(g2, b.left, b.top,
                            barRectWidth
, barRectHeight,
                            boxRect
.y, boxRect.height, b);
           
}
       
}
   
}


   
/**
     * All purpose paint method that should do the right thing for almost
     * all linear, determinate progress bars. By setting a few values in
     * the defaults
     * table, things should work just fine to paint your progress bar.
     * Naturally, override this if you are making a circular or
     * semi-circular progress bar.
     *
     * @see #paintIndeterminate
     *
     * @since 1.4
     */

   
protected void paintDeterminate(Graphics g, JComponent c) {
       
if (!(g instanceof Graphics2D)) {
           
return;
       
}

       
Insets b = progressBar.getInsets(); // area for border
       
int barRectWidth = progressBar.getWidth() - (b.right + b.left);
       
int barRectHeight = progressBar.getHeight() - (b.top + b.bottom);

       
if (barRectWidth <= 0 || barRectHeight <= 0) {
           
return;
       
}

       
int cellLength = getCellLength();
       
int cellSpacing = getCellSpacing();
       
// amount of progress to draw
       
int amountFull = getAmountFull(b, barRectWidth, barRectHeight);

       
Graphics2D g2 = (Graphics2D)g;
        g2
.setColor(progressBar.getForeground());

       
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
           
// draw the cells
           
if (cellSpacing == 0 && amountFull > 0) {
               
// draw one big Rect because there is no space between cells
                g2
.setStroke(new BasicStroke((float)barRectHeight,
                       
BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
           
} else {
               
// draw each individual cell
                g2
.setStroke(new BasicStroke((float)barRectHeight,
                       
BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
                       
0.f, new float[] { cellLength, cellSpacing }, 0.f));
           
}

           
if (BasicGraphicsUtils.isLeftToRight(c)) {
                g2
.drawLine(b.left, (barRectHeight/2) + b.top,
                        amountFull
+ b.left, (barRectHeight/2) + b.top);
           
} else {
                g2
.drawLine((barRectWidth + b.left),
                       
(barRectHeight/2) + b.top,
                        barRectWidth
+ b.left - amountFull,
                       
(barRectHeight/2) + b.top);
           
}

       
} else { // VERTICAL
           
// draw the cells
           
if (cellSpacing == 0 && amountFull > 0) {
               
// draw one big Rect because there is no space between cells
                g2
.setStroke(new BasicStroke((float)barRectWidth,
                       
BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
           
} else {
               
// draw each individual cell
                g2
.setStroke(new BasicStroke((float)barRectWidth,
                       
BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
                       
0f, new float[] { cellLength, cellSpacing }, 0f));
           
}

            g2
.drawLine(barRectWidth/2 + b.left,
                    b
.top + barRectHeight,
                    barRectWidth
/2 + b.left,
                    b
.top + barRectHeight - amountFull);
       
}

       
// Deal with possible text painting
       
if (progressBar.isStringPainted()) {
            paintString
(g, b.left, b.top,
                        barRectWidth
, barRectHeight,
                        amountFull
, b);
       
}
   
}


   
protected void paintString(Graphics g, int x, int y,
                               
int width, int height,
                               
int amountFull, Insets b) {
       
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
           
if (BasicGraphicsUtils.isLeftToRight(progressBar)) {
               
if (progressBar.isIndeterminate()) {
                    boxRect
= getBox(boxRect);
                    paintString
(g, x, y, width, height,
                            boxRect
.x, boxRect.width, b);
               
} else {
                    paintString
(g, x, y, width, height, x, amountFull, b);
               
}
           
}
           
else {
                paintString
(g, x, y, width, height, x + width - amountFull,
                            amountFull
, b);
           
}
       
}
       
else {
           
if (progressBar.isIndeterminate()) {
                boxRect
= getBox(boxRect);
                paintString
(g, x, y, width, height,
                        boxRect
.y, boxRect.height, b);
           
} else {
                paintString
(g, x, y, width, height, y + height - amountFull,
                        amountFull
, b);
           
}
       
}
   
}

   
/**
     * Paints the progress string.
     *
     * @param g Graphics used for drawing.
     * @param x x location of bounding box
     * @param y y location of bounding box
     * @param width width of bounding box
     * @param height height of bounding box
     * @param fillStart start location, in x or y depending on orientation,
     *        of the filled portion of the progress bar.
     * @param amountFull size of the fill region, either width or height
     *        depending upon orientation.
     * @param b Insets of the progress bar.
     */

   
private void paintString(Graphics g, int x, int y, int width, int height,
                             
int fillStart, int amountFull, Insets b) {
       
if (!(g instanceof Graphics2D)) {
           
return;
       
}

       
Graphics2D g2 = (Graphics2D)g;
       
String progressString = progressBar.getString();
        g2
.setFont(progressBar.getFont());
       
Point renderLocation = getStringPlacement(g2, progressString,
                                                  x
, y, width, height);
       
Rectangle oldClip = g2.getClipBounds();

       
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
            g2
.setColor(getSelectionBackground());
           
SwingUtilities2.drawString(progressBar, g2, progressString,
                                       renderLocation
.x, renderLocation.y);
            g2
.setColor(getSelectionForeground());
            g2
.clipRect(fillStart, y, amountFull, height);
           
SwingUtilities2.drawString(progressBar, g2, progressString,
                                       renderLocation
.x, renderLocation.y);
       
} else { // VERTICAL
            g2
.setColor(getSelectionBackground());
           
AffineTransform rotate =
                   
AffineTransform.getRotateInstance(Math.PI/2);
            g2
.setFont(progressBar.getFont().deriveFont(rotate));
            renderLocation
= getStringPlacement(g2, progressString,
                                                  x
, y, width, height);
           
SwingUtilities2.drawString(progressBar, g2, progressString,
                                       renderLocation
.x, renderLocation.y);
            g2
.setColor(getSelectionForeground());
            g2
.clipRect(x, fillStart, width, amountFull);
           
SwingUtilities2.drawString(progressBar, g2, progressString,
                                       renderLocation
.x, renderLocation.y);
       
}
        g2
.setClip(oldClip);
   
}


   
/**
     * Designate the place where the progress string will be painted.
     * This implementation places it at the center of the progress
     * bar (in both x and y). Override this if you want to right,
     * left, top, or bottom align the progress string or if you need
     * to nudge it around for any reason.
     */

   
protected Point getStringPlacement(Graphics g, String progressString,
                                       
int x,int y,int width,int height) {
       
FontMetrics fontSizer = SwingUtilities2.getFontMetrics(progressBar, g,
                                            progressBar
.getFont());
       
int stringWidth = SwingUtilities2.stringWidth(progressBar, fontSizer,
                                                      progressString
);

       
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
           
return new Point(x + Math.round(width/2 - stringWidth/2),
                             y
+ ((height +
                                 fontSizer
.getAscent() -
                                 fontSizer
.getLeading() -
                                 fontSizer
.getDescent()) / 2));
       
} else { // VERTICAL
           
return new Point(x + ((width - fontSizer.getAscent() +
                    fontSizer
.getLeading() + fontSizer.getDescent()) / 2),
                    y
+ Math.round(height/2 - stringWidth/2));
       
}
   
}


   
public Dimension getPreferredSize(JComponent c) {
       
Dimension       size;
       
Insets          border = progressBar.getInsets();
       
FontMetrics     fontSizer = progressBar.getFontMetrics(
                                                  progressBar
.getFont());

       
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
            size
= new Dimension(getPreferredInnerHorizontal());
           
// Ensure that the progress string will fit
           
if (progressBar.isStringPainted()) {
               
// I'm doing this for completeness.
               
String progString = progressBar.getString();
               
int stringWidth = SwingUtilities2.stringWidth(
                          progressBar
, fontSizer, progString);
               
if (stringWidth > size.width) {
                    size
.width = stringWidth;
               
}
               
// This uses both Height and Descent to be sure that
               
// there is more than enough room in the progress bar
               
// for everything.
               
// This does have a strange dependency on
               
// getStringPlacememnt() in a funny way.
               
int stringHeight = fontSizer.getHeight() +
                                   fontSizer
.getDescent();
               
if (stringHeight > size.height) {
                    size
.height = stringHeight;
               
}
           
}
       
} else {
            size
= new Dimension(getPreferredInnerVertical());
           
// Ensure that the progress string will fit.
           
if (progressBar.isStringPainted()) {
               
String progString = progressBar.getString();
               
int stringHeight = fontSizer.getHeight() +
                        fontSizer
.getDescent();
               
if (stringHeight > size.width) {
                    size
.width = stringHeight;
               
}
               
// This is also for completeness.
               
int stringWidth = SwingUtilities2.stringWidth(
                                       progressBar
, fontSizer, progString);
               
if (stringWidth > size.height) {
                    size
.height = stringWidth;
               
}
           
}
       
}

        size
.width += border.left + border.right;
        size
.height += border.top + border.bottom;
       
return size;
   
}

   
/**
     * The Minimum size for this component is 10. The rationale here
     * is that there should be at least one pixel per 10 percent.
     */

   
public Dimension getMinimumSize(JComponent c) {
       
Dimension pref = getPreferredSize(progressBar);
       
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
            pref
.width = 10;
       
} else {
            pref
.height = 10;
       
}
       
return pref;
   
}

   
public Dimension getMaximumSize(JComponent c) {
       
Dimension pref = getPreferredSize(progressBar);
       
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
            pref
.width = Short.MAX_VALUE;
       
} else {
            pref
.height = Short.MAX_VALUE;
       
}
       
return pref;
   
}

   
/**
     * Gets the index of the current animation frame.
     *
     * @since 1.4
     */

   
protected int getAnimationIndex() {
       
return animationIndex;
   
}

   
/**
     * Returns the number of frames for the complete animation loop
     * used by an indeterminate JProgessBar. The progress chunk will go
     * from one end to the other and back during the entire loop. This
     * visual behavior may be changed by subclasses in other Look and Feels.
     *
     * @return the number of frames
     * @since 1.6
     */

   
protected final int getFrameCount() {
       
return numFrames;
   
}

   
/**
     * Sets the index of the current animation frame
     * to the specified value and requests that the
     * progress bar be repainted.
     * Subclasses that don't use the default painting code
     * might need to override this method
     * to change the way that the <code>repaint</code> method
     * is invoked.
     *
     * @param newValue the new animation index; no checking
     *                 is performed on its value
     * @see #incrementAnimationIndex
     *
     * @since 1.4
     */

   
protected void setAnimationIndex(int newValue) {
       
if (animationIndex != newValue) {
           
if (sizeChanged()) {
                animationIndex
= newValue;
                maxPosition
= 0;  //needs to be recalculated
                delta
= 0.0;      //needs to be recalculated
                progressBar
.repaint();
               
return;
           
}

           
//Get the previous box drawn.
            nextPaintRect
= getBox(nextPaintRect);

           
//Update the frame number.
            animationIndex
= newValue;

           
//Get the next box to draw.
           
if (nextPaintRect != null) {
                boxRect
= getBox(boxRect);
               
if (boxRect != null) {
                    nextPaintRect
.add(boxRect);
               
}
           
}
       
} else { //animationIndex == newValue
           
return;
       
}

       
if (nextPaintRect != null) {
            progressBar
.repaint(nextPaintRect);
       
} else {
            progressBar
.repaint();
       
}
   
}

   
private boolean sizeChanged() {
       
if ((oldComponentInnards == null) || (componentInnards == null)) {
           
return true;
       
}

        oldComponentInnards
.setRect(componentInnards);
        componentInnards
= SwingUtilities.calculateInnerArea(progressBar,
                                                             componentInnards
);
       
return !oldComponentInnards.equals(componentInnards);
   
}

   
/**
     * Sets the index of the current animation frame,
     * to the next valid value,
     * which results in the progress bar being repainted.
     * The next valid value is, by default,
     * the current animation index plus one.
     * If the new value would be too large,
     * this method sets the index to 0.
     * Subclasses might need to override this method
     * to ensure that the index does not go over
     * the number of frames needed for the particular
     * progress bar instance.
     * This method is invoked by the default animation thread
     * every <em>X</em> milliseconds,
     * where <em>X</em> is specified by the "ProgressBar.repaintInterval"
     * UI default.
     *
     * @see #setAnimationIndex
     * @since 1.4
     */

   
protected void incrementAnimationIndex() {
       
int newValue = getAnimationIndex() + 1;

       
if (newValue < numFrames) {
            setAnimationIndex
(newValue);
       
} else {
            setAnimationIndex
(0);
       
}
   
}

   
/**
     * Returns the desired number of milliseconds between repaints.
     * This value is meaningful
     * only if the progress bar is in indeterminate mode.
     * The repaint interval determines how often the
     * default animation thread's timer is fired.
     * It's also used by the default indeterminate progress bar
     * painting code when determining
     * how far to move the bouncing box per frame.
     * The repaint interval is specified by
     * the "ProgressBar.repaintInterval" UI default.
     *
     * @return  the repaint interval, in milliseconds
     */

   
private int getRepaintInterval() {
       
return repaintInterval;
   
}

   
private int initRepaintInterval() {
        repaintInterval
= DefaultLookup.getInt(progressBar,
               
this, "ProgressBar.repaintInterval", 50);
       
return repaintInterval;
   
}

   
/**
     * Returns the number of milliseconds per animation cycle.
     * This value is meaningful
     * only if the progress bar is in indeterminate mode.
     * The cycle time is used by the default indeterminate progress bar
     * painting code when determining
     * how far to move the bouncing box per frame.
     * The cycle time is specified by
     * the "ProgressBar.cycleTime" UI default
     * and adjusted, if necessary,
     * by the initIndeterminateDefaults method.
     *
     * @return  the cycle time, in milliseconds
     */

   
private int getCycleTime() {
       
return cycleTime;
   
}

   
private int initCycleTime() {
        cycleTime
= DefaultLookup.getInt(progressBar, this,
               
"ProgressBar.cycleTime", 3000);
       
return cycleTime;
   
}


   
/** Initialize cycleTime, repaintInterval, numFrames, animationIndex. */
   
private void initIndeterminateDefaults() {
        initRepaintInterval
(); //initialize repaint interval
        initCycleTime
();       //initialize cycle length

       
// Make sure repaintInterval is reasonable.
       
if (repaintInterval <= 0) {
            repaintInterval
= 100;
       
}

       
// Make sure cycleTime is reasonable.
       
if (repaintInterval > cycleTime) {
            cycleTime
= repaintInterval * 20;
       
} else {
           
// Force cycleTime to be a even multiple of repaintInterval.
           
int factor = (int)Math.ceil(
                                 
((double)cycleTime)
                               
/ ((double)repaintInterval*2));
            cycleTime
= repaintInterval*factor*2;
       
}
   
}

   
/**
     * Invoked by PropertyChangeHandler.
     *
     *  NOTE: This might not be invoked until after the first
     *  paintIndeterminate call.
     */

   
private void initIndeterminateValues() {
        initIndeterminateDefaults
();
       
//assert cycleTime/repaintInterval is a whole multiple of 2.
        numFrames
= cycleTime/repaintInterval;
        initAnimationIndex
();

        boxRect
= new Rectangle();
        nextPaintRect
= new Rectangle();
        componentInnards
= new Rectangle();
        oldComponentInnards
= new Rectangle();

       
// we only bother installing the HierarchyChangeListener if we
       
// are indeterminate
        progressBar
.addHierarchyListener(getHandler());

       
// start the animation thread if necessary
       
if (progressBar.isDisplayable()) {
            startAnimationTimer
();
       
}
   
}

   
/** Invoked by PropertyChangeHandler. */
   
private void cleanUpIndeterminateValues() {
       
// stop the animation thread if necessary
       
if (progressBar.isDisplayable()) {
            stopAnimationTimer
();
       
}

        cycleTime
= repaintInterval = 0;
        numFrames
= animationIndex = 0;
        maxPosition
= 0;
        delta
= 0.0;

        boxRect
= nextPaintRect = null;
        componentInnards
= oldComponentInnards = null;

        progressBar
.removeHierarchyListener(getHandler());
   
}

   
// Called from initIndeterminateValues to initialize the animation index.
   
// This assumes that numFrames is set to a correct value.
   
private void initAnimationIndex() {
       
if ((progressBar.getOrientation() == JProgressBar.HORIZONTAL) &&
           
(BasicGraphicsUtils.isLeftToRight(progressBar))) {
           
// If this is a left-to-right progress bar,
           
// start at the first frame.
            setAnimationIndex
(0);
       
} else {
           
// If we go right-to-left or vertically, start at the right/bottom.
            setAnimationIndex
(numFrames/2);
       
}
   
}

   
//
   
// Animation Thread
   
//
   
/**
     * Implements an animation thread that invokes repaint
     * at a fixed rate.  If ADJUSTTIMER is true, this thread
     * will continuously adjust the repaint interval to
     * try to make the actual time between repaints match
     * the requested rate.
     */

   
private class Animator implements ActionListener {
       
private Timer timer;
       
private long previousDelay; //used to tune the repaint interval
       
private int interval; //the fixed repaint interval
       
private long lastCall; //the last time actionPerformed was called
       
private int MINIMUM_DELAY = 5;

       
/**
         * Creates a timer if one doesn't already exist,
         * then starts the timer thread.
         */

       
private void start(int interval) {
            previousDelay
= interval;
            lastCall
= 0;

           
if (timer == null) {
                timer
= new Timer(interval, this);
           
} else {
                timer
.setDelay(interval);
           
}

           
if (ADJUSTTIMER) {
                timer
.setRepeats(false);
                timer
.setCoalesce(false);
           
}

            timer
.start();
       
}

       
/**
         * Stops the timer thread.
         */

       
private void stop() {
            timer
.stop();
       
}

       
/**
         * Reacts to the timer's action events.
         */

       
public void actionPerformed(ActionEvent e) {
           
if (ADJUSTTIMER) {
               
long time = System.currentTimeMillis();

               
if (lastCall > 0) { //adjust nextDelay
               
//XXX maybe should cache this after a while
                   
//actual = time - lastCall
                   
//difference = actual - interval
                   
//nextDelay = previousDelay - difference
                   
//          = previousDelay - (time - lastCall - interval)
                   
int nextDelay = (int)(previousDelay
                                         
- time + lastCall
                                         
+ getRepaintInterval());
                   
if (nextDelay < MINIMUM_DELAY) {
                        nextDelay
= MINIMUM_DELAY;
                   
}
                    timer
.setInitialDelay(nextDelay);
                    previousDelay
= nextDelay;
               
}
                timer
.start();
                lastCall
= time;
           
}

            incrementAnimationIndex
(); //paint next frame
       
}
   
}


   
/**
     * This inner class is marked &quot;public&quot; due to a compiler bug.
     * This class should be treated as a &quot;protected&quot; inner class.
     * Instantiate it only within subclasses of BasicProgressBarUI.
     */

   
public class ChangeHandler implements ChangeListener {
       
// NOTE: This class exists only for backward compatability. All
       
// its functionality has been moved into Handler. If you need to add
       
// new functionality add it to the Handler, but make sure this
       
// class calls into the Handler.
       
public void stateChanged(ChangeEvent e) {
            getHandler
().stateChanged(e);
       
}
   
}


   
private class Handler implements ChangeListener, PropertyChangeListener, HierarchyListener {
       
// ChangeListener
       
public void stateChanged(ChangeEvent e) {
           
BoundedRangeModel model = progressBar.getModel();
           
int newRange = model.getMaximum() - model.getMinimum();
           
int newPercent;
           
int oldPercent = getCachedPercent();

           
if (newRange > 0) {
                newPercent
= (int)((100 * (long)model.getValue()) / newRange);
           
} else {
                newPercent
= 0;
           
}

           
if (newPercent != oldPercent) {
                setCachedPercent
(newPercent);
                progressBar
.repaint();
           
}
       
}

       
// PropertyChangeListener
       
public void propertyChange(PropertyChangeEvent e) {
           
String prop = e.getPropertyName();
           
if ("indeterminate" == prop) {
               
if (progressBar.isIndeterminate()) {
                    initIndeterminateValues
();
               
} else {
                   
//clean up
                    cleanUpIndeterminateValues
();
               
}
                progressBar
.repaint();
           
}
       
}

       
// we don't want the animation to keep running if we're not displayable
       
public void hierarchyChanged(HierarchyEvent he) {
           
if ((he.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
               
if (progressBar.isIndeterminate()) {
                   
if (progressBar.isDisplayable()) {
                        startAnimationTimer
();
                   
} else {
                        stopAnimationTimer
();
                   
}
               
}
           
}
       
}
   
}
}