src/java.desktop/share/classes/javax/swing/plaf/basic/BasicProgressBarUI.java
changeset 47216 71c04702a3d5
parent 25859 3317bb8137f4
child 51901 3f5a55b6bad8
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package javax.swing.plaf.basic;
       
    27 
       
    28 import sun.swing.SwingUtilities2;
       
    29 import java.awt.*;
       
    30 import java.awt.geom.AffineTransform;
       
    31 import java.awt.event.*;
       
    32 import javax.swing.*;
       
    33 import javax.swing.event.*;
       
    34 import javax.swing.plaf.*;
       
    35 import java.beans.PropertyChangeListener;
       
    36 import java.beans.PropertyChangeEvent;
       
    37 import java.io.Serializable;
       
    38 import sun.swing.DefaultLookup;
       
    39 
       
    40 /**
       
    41  * A Basic L&F implementation of ProgressBarUI.
       
    42  *
       
    43  * @author Michael C. Albers
       
    44  * @author Kathy Walrath
       
    45  */
       
    46 public class BasicProgressBarUI extends ProgressBarUI {
       
    47     private int cachedPercent;
       
    48     private int cellLength, cellSpacing;
       
    49     // The "selectionForeground" is the color of the text when it is painted
       
    50     // over a filled area of the progress bar. The "selectionBackground"
       
    51     // is for the text over the unfilled progress bar area.
       
    52     private Color selectionForeground, selectionBackground;
       
    53 
       
    54     private Animator animator;
       
    55 
       
    56     /**
       
    57      * The instance of {@code JProgressBar}.
       
    58      */
       
    59     protected JProgressBar progressBar;
       
    60     /**
       
    61      * The instance of {@code ChangeListener}.
       
    62      */
       
    63     protected ChangeListener changeListener;
       
    64     private Handler handler;
       
    65 
       
    66     /**
       
    67      * The current state of the indeterminate animation's cycle.
       
    68      * 0, the initial value, means paint the first frame.
       
    69      * When the progress bar is indeterminate and showing,
       
    70      * the default animation thread updates this variable
       
    71      * by invoking incrementAnimationIndex()
       
    72      * every repaintInterval milliseconds.
       
    73      */
       
    74     private int animationIndex = 0;
       
    75 
       
    76     /**
       
    77      * The number of frames per cycle. Under the default implementation,
       
    78      * this depends on the cycleTime and repaintInterval.  It
       
    79      * must be an even number for the default painting algorithm.  This
       
    80      * value is set in the initIndeterminateValues method.
       
    81      */
       
    82     private int numFrames;   //0 1|numFrames-1 ... numFrames/2
       
    83 
       
    84     /**
       
    85      * Interval (in ms) between repaints of the indeterminate progress bar.
       
    86      * The value of this method is set
       
    87      * (every time the progress bar changes to indeterminate mode)
       
    88      * using the
       
    89      * "ProgressBar.repaintInterval" key in the defaults table.
       
    90      */
       
    91     private int repaintInterval;
       
    92 
       
    93     /**
       
    94      * The number of milliseconds until the animation cycle repeats.
       
    95      * The value of this method is set
       
    96      * (every time the progress bar changes to indeterminate mode)
       
    97      * using the
       
    98      * "ProgressBar.cycleTime" key in the defaults table.
       
    99      */
       
   100     private int cycleTime;  //must be repaintInterval*2*aPositiveInteger
       
   101 
       
   102     //performance stuff
       
   103     private static boolean ADJUSTTIMER = true; //makes a BIG difference;
       
   104                                                //make this false for
       
   105                                                //performance tests
       
   106 
       
   107     /**
       
   108      * Used to hold the location and size of the bouncing box (returned
       
   109      * by getBox) to be painted.
       
   110      *
       
   111      * @since 1.5
       
   112      */
       
   113     protected Rectangle boxRect;
       
   114 
       
   115     /**
       
   116      * The rectangle to be updated the next time the
       
   117      * animation thread calls repaint.  For bouncing-box
       
   118      * animation this rect should include the union of
       
   119      * the currently displayed box (which needs to be erased)
       
   120      * and the box to be displayed next.
       
   121      * This rectangle's values are set in
       
   122      * the setAnimationIndex method.
       
   123      */
       
   124     private Rectangle nextPaintRect;
       
   125 
       
   126     //cache
       
   127     /** The component's painting area, not including the border. */
       
   128     private Rectangle componentInnards;    //the current painting area
       
   129     private Rectangle oldComponentInnards; //used to see if the size changed
       
   130 
       
   131     /** For bouncing-box animation, the change in position per frame. */
       
   132     private double delta = 0.0;
       
   133 
       
   134     private int maxPosition = 0; //maximum X (horiz) or Y box location
       
   135 
       
   136     /**
       
   137      * Returns a new instance of {@code BasicProgressBarUI}.
       
   138      *
       
   139      * @param x a component
       
   140      * @return a new instance of {@code BasicProgressBarUI}
       
   141      */
       
   142     public static ComponentUI createUI(JComponent x) {
       
   143         return new BasicProgressBarUI();
       
   144     }
       
   145 
       
   146     public void installUI(JComponent c) {
       
   147         progressBar = (JProgressBar)c;
       
   148         installDefaults();
       
   149         installListeners();
       
   150         if (progressBar.isIndeterminate()) {
       
   151             initIndeterminateValues();
       
   152         }
       
   153     }
       
   154 
       
   155     public void uninstallUI(JComponent c) {
       
   156         if (progressBar.isIndeterminate()) {
       
   157             cleanUpIndeterminateValues();
       
   158         }
       
   159         uninstallDefaults();
       
   160         uninstallListeners();
       
   161         progressBar = null;
       
   162     }
       
   163 
       
   164     /**
       
   165      * Installs default properties.
       
   166      */
       
   167     protected void installDefaults() {
       
   168         LookAndFeel.installProperty(progressBar, "opaque", Boolean.TRUE);
       
   169         LookAndFeel.installBorder(progressBar,"ProgressBar.border");
       
   170         LookAndFeel.installColorsAndFont(progressBar,
       
   171                                          "ProgressBar.background",
       
   172                                          "ProgressBar.foreground",
       
   173                                          "ProgressBar.font");
       
   174         cellLength = UIManager.getInt("ProgressBar.cellLength");
       
   175         if (cellLength == 0) cellLength = 1;
       
   176         cellSpacing = UIManager.getInt("ProgressBar.cellSpacing");
       
   177         selectionForeground = UIManager.getColor("ProgressBar.selectionForeground");
       
   178         selectionBackground = UIManager.getColor("ProgressBar.selectionBackground");
       
   179     }
       
   180 
       
   181     /**
       
   182      * Unintalls default properties.
       
   183      */
       
   184     protected void uninstallDefaults() {
       
   185         LookAndFeel.uninstallBorder(progressBar);
       
   186     }
       
   187 
       
   188     /**
       
   189      * Registers listeners.
       
   190      */
       
   191     protected void installListeners() {
       
   192         //Listen for changes in the progress bar's data.
       
   193         changeListener = getHandler();
       
   194         progressBar.addChangeListener(changeListener);
       
   195 
       
   196         //Listen for changes between determinate and indeterminate state.
       
   197         progressBar.addPropertyChangeListener(getHandler());
       
   198     }
       
   199 
       
   200     private Handler getHandler() {
       
   201         if (handler == null) {
       
   202             handler = new Handler();
       
   203         }
       
   204         return handler;
       
   205     }
       
   206 
       
   207     /**
       
   208      * Starts the animation thread, creating and initializing
       
   209      * it if necessary. This method is invoked when an
       
   210      * indeterminate progress bar should start animating.
       
   211      * Reasons for this may include:
       
   212      * <ul>
       
   213      *    <li>The progress bar is determinate and becomes displayable
       
   214      *    <li>The progress bar is displayable and becomes determinate
       
   215      *    <li>The progress bar is displayable and determinate and this
       
   216      *        UI is installed
       
   217      * </ul>
       
   218      * If you implement your own animation thread,
       
   219      * you must override this method.
       
   220      *
       
   221      * @since 1.4
       
   222      * @see #stopAnimationTimer
       
   223      */
       
   224     protected void startAnimationTimer() {
       
   225         if (animator == null) {
       
   226             animator = new Animator();
       
   227         }
       
   228 
       
   229         animator.start(getRepaintInterval());
       
   230     }
       
   231 
       
   232     /**
       
   233      * Stops the animation thread.
       
   234      * This method is invoked when the indeterminate
       
   235      * animation should be stopped. Reasons for this may include:
       
   236      * <ul>
       
   237      *    <li>The progress bar changes to determinate
       
   238      *    <li>The progress bar is no longer part of a displayable hierarchy
       
   239      *    <li>This UI in uninstalled
       
   240      * </ul>
       
   241      * If you implement your own animation thread,
       
   242      * you must override this method.
       
   243      *
       
   244      * @since 1.4
       
   245      * @see #startAnimationTimer
       
   246      */
       
   247     protected void stopAnimationTimer() {
       
   248         if (animator != null) {
       
   249             animator.stop();
       
   250         }
       
   251     }
       
   252 
       
   253     /**
       
   254      * Removes all listeners installed by this object.
       
   255      */
       
   256     protected void uninstallListeners() {
       
   257         progressBar.removeChangeListener(changeListener);
       
   258         progressBar.removePropertyChangeListener(getHandler());
       
   259         handler = null;
       
   260     }
       
   261 
       
   262 
       
   263     /**
       
   264      * Returns the baseline.
       
   265      *
       
   266      * @throws NullPointerException {@inheritDoc}
       
   267      * @throws IllegalArgumentException {@inheritDoc}
       
   268      * @see javax.swing.JComponent#getBaseline(int, int)
       
   269      * @since 1.6
       
   270      */
       
   271     public int getBaseline(JComponent c, int width, int height) {
       
   272         super.getBaseline(c, width, height);
       
   273         if (progressBar.isStringPainted() &&
       
   274                 progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   275             FontMetrics metrics = progressBar.
       
   276                     getFontMetrics(progressBar.getFont());
       
   277             Insets insets = progressBar.getInsets();
       
   278             int y = insets.top;
       
   279             height = height - insets.top - insets.bottom;
       
   280             return y + (height + metrics.getAscent() -
       
   281                         metrics.getLeading() -
       
   282                         metrics.getDescent()) / 2;
       
   283         }
       
   284         return -1;
       
   285     }
       
   286 
       
   287     /**
       
   288      * Returns an enum indicating how the baseline of the component
       
   289      * changes as the size changes.
       
   290      *
       
   291      * @throws NullPointerException {@inheritDoc}
       
   292      * @see javax.swing.JComponent#getBaseline(int, int)
       
   293      * @since 1.6
       
   294      */
       
   295     public Component.BaselineResizeBehavior getBaselineResizeBehavior(
       
   296             JComponent c) {
       
   297         super.getBaselineResizeBehavior(c);
       
   298         if (progressBar.isStringPainted() &&
       
   299                 progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   300             return Component.BaselineResizeBehavior.CENTER_OFFSET;
       
   301         }
       
   302         return Component.BaselineResizeBehavior.OTHER;
       
   303     }
       
   304 
       
   305     // Many of the Basic*UI components have the following methods.
       
   306     // This component does not have these methods because *ProgressBarUI
       
   307     //  is not a compound component and does not accept input.
       
   308     //
       
   309     // protected void installComponents()
       
   310     // protected void uninstallComponents()
       
   311     // protected void installKeyboardActions()
       
   312     // protected void uninstallKeyboardActions()
       
   313 
       
   314     /**
       
   315      * Returns preferred size of the horizontal {@code JProgressBar}.
       
   316      *
       
   317      * @return preferred size of the horizontal {@code JProgressBar}
       
   318      */
       
   319     protected Dimension getPreferredInnerHorizontal() {
       
   320         Dimension horizDim = (Dimension)DefaultLookup.get(progressBar, this,
       
   321             "ProgressBar.horizontalSize");
       
   322         if (horizDim == null) {
       
   323             horizDim = new Dimension(146, 12);
       
   324         }
       
   325         return horizDim;
       
   326     }
       
   327 
       
   328     /**
       
   329      * Returns preferred size of the vertical {@code JProgressBar}.
       
   330      *
       
   331      * @return preferred size of the vertical {@code JProgressBar}
       
   332      */
       
   333     protected Dimension getPreferredInnerVertical() {
       
   334         Dimension vertDim = (Dimension)DefaultLookup.get(progressBar, this,
       
   335             "ProgressBar.verticalSize");
       
   336         if (vertDim == null) {
       
   337             vertDim = new Dimension(12, 146);
       
   338         }
       
   339         return vertDim;
       
   340     }
       
   341 
       
   342     /**
       
   343      * The "selectionForeground" is the color of the text when it is painted
       
   344      * over a filled area of the progress bar.
       
   345      *
       
   346      * @return the color of the selected foreground
       
   347      */
       
   348     protected Color getSelectionForeground() {
       
   349         return selectionForeground;
       
   350     }
       
   351 
       
   352     /**
       
   353      * The "selectionBackground" is the color of the text when it is painted
       
   354      * over an unfilled area of the progress bar.
       
   355      *
       
   356      * @return the color of the selected background
       
   357      */
       
   358     protected Color getSelectionBackground() {
       
   359         return selectionBackground;
       
   360     }
       
   361 
       
   362     private int getCachedPercent() {
       
   363         return cachedPercent;
       
   364     }
       
   365 
       
   366     private void setCachedPercent(int cachedPercent) {
       
   367         this.cachedPercent = cachedPercent;
       
   368     }
       
   369 
       
   370     /**
       
   371      * Returns the width (if HORIZONTAL) or height (if VERTICAL)
       
   372      * of each of the individual cells/units to be rendered in the
       
   373      * progress bar. However, for text rendering simplification and
       
   374      * aesthetic considerations, this function will return 1 when
       
   375      * the progress string is being rendered.
       
   376      *
       
   377      * @return the value representing the spacing between cells
       
   378      * @see    #setCellLength
       
   379      * @see    JProgressBar#isStringPainted
       
   380      */
       
   381     protected int getCellLength() {
       
   382         if (progressBar.isStringPainted()) {
       
   383             return 1;
       
   384         } else {
       
   385             return cellLength;
       
   386         }
       
   387     }
       
   388 
       
   389     /**
       
   390      * Sets the cell length.
       
   391      *
       
   392      * @param cellLen a new cell length
       
   393      */
       
   394     protected void setCellLength(int cellLen) {
       
   395         this.cellLength = cellLen;
       
   396     }
       
   397 
       
   398     /**
       
   399      * Returns the spacing between each of the cells/units in the
       
   400      * progress bar. However, for text rendering simplification and
       
   401      * aesthetic considerations, this function will return 0 when
       
   402      * the progress string is being rendered.
       
   403      *
       
   404      * @return the value representing the spacing between cells
       
   405      * @see    #setCellSpacing
       
   406      * @see    JProgressBar#isStringPainted
       
   407      */
       
   408     protected int getCellSpacing() {
       
   409         if (progressBar.isStringPainted()) {
       
   410             return 0;
       
   411         } else {
       
   412             return cellSpacing;
       
   413         }
       
   414     }
       
   415 
       
   416     /**
       
   417      * Sets the cell spacing.
       
   418      *
       
   419      * @param cellSpace a new cell spacing
       
   420      */
       
   421     protected void setCellSpacing(int cellSpace) {
       
   422         this.cellSpacing = cellSpace;
       
   423     }
       
   424 
       
   425     /**
       
   426      * This determines the amount of the progress bar that should be filled
       
   427      * based on the percent done gathered from the model. This is a common
       
   428      * operation so it was abstracted out. It assumes that your progress bar
       
   429      * is linear. That is, if you are making a circular progress indicator,
       
   430      * you will want to override this method.
       
   431      *
       
   432      * @param b insets
       
   433      * @param width a width
       
   434      * @param height a height
       
   435      * @return the amount of the progress bar that should be filled
       
   436      */
       
   437     protected int getAmountFull(Insets b, int width, int height) {
       
   438         int amountFull = 0;
       
   439         BoundedRangeModel model = progressBar.getModel();
       
   440 
       
   441         if ( (model.getMaximum() - model.getMinimum()) != 0) {
       
   442             if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   443                 amountFull = (int)Math.round(width *
       
   444                                              progressBar.getPercentComplete());
       
   445             } else {
       
   446                 amountFull = (int)Math.round(height *
       
   447                                              progressBar.getPercentComplete());
       
   448             }
       
   449         }
       
   450         return amountFull;
       
   451     }
       
   452 
       
   453     /**
       
   454      * Delegates painting to one of two methods:
       
   455      * paintDeterminate or paintIndeterminate.
       
   456      */
       
   457     public void paint(Graphics g, JComponent c) {
       
   458         if (progressBar.isIndeterminate()) {
       
   459             paintIndeterminate(g, c);
       
   460         } else {
       
   461             paintDeterminate(g, c);
       
   462         }
       
   463     }
       
   464 
       
   465     /**
       
   466      * Stores the position and size of
       
   467      * the bouncing box that would be painted for the current animation index
       
   468      * in <code>r</code> and returns <code>r</code>.
       
   469      * Subclasses that add to the painting performed
       
   470      * in this class's implementation of <code>paintIndeterminate</code> --
       
   471      * to draw an outline around the bouncing box, for example --
       
   472      * can use this method to get the location of the bouncing
       
   473      * box that was just painted.
       
   474      * By overriding this method,
       
   475      * you have complete control over the size and position
       
   476      * of the bouncing box,
       
   477      * without having to reimplement <code>paintIndeterminate</code>.
       
   478      *
       
   479      * @param r  the Rectangle instance to be modified;
       
   480      *           may be <code>null</code>
       
   481      * @return   <code>null</code> if no box should be drawn;
       
   482      *           otherwise, returns the passed-in rectangle
       
   483      *           (if non-null)
       
   484      *           or a new rectangle
       
   485      *
       
   486      * @see #setAnimationIndex
       
   487      * @since 1.4
       
   488      */
       
   489     protected Rectangle getBox(Rectangle r) {
       
   490         int currentFrame = getAnimationIndex();
       
   491         int middleFrame = numFrames/2;
       
   492 
       
   493         if (sizeChanged() || delta == 0.0 || maxPosition == 0.0) {
       
   494             updateSizes();
       
   495         }
       
   496 
       
   497         r = getGenericBox(r);
       
   498 
       
   499         if (r == null) {
       
   500             return null;
       
   501         }
       
   502         if (middleFrame <= 0) {
       
   503             return null;
       
   504         }
       
   505 
       
   506         //assert currentFrame >= 0 && currentFrame < numFrames
       
   507         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   508             if (currentFrame < middleFrame) {
       
   509                 r.x = componentInnards.x
       
   510                       + (int)Math.round(delta * (double)currentFrame);
       
   511             } else {
       
   512                 r.x = maxPosition
       
   513                       - (int)Math.round(delta *
       
   514                                         (currentFrame - middleFrame));
       
   515             }
       
   516         } else { //VERTICAL indeterminate progress bar
       
   517             if (currentFrame < middleFrame) {
       
   518                 r.y = componentInnards.y
       
   519                       + (int)Math.round(delta * currentFrame);
       
   520             } else {
       
   521                 r.y = maxPosition
       
   522                       - (int)Math.round(delta *
       
   523                                         (currentFrame - middleFrame));
       
   524             }
       
   525         }
       
   526         return r;
       
   527     }
       
   528 
       
   529     /**
       
   530      * Updates delta, max position.
       
   531      * Assumes componentInnards is correct (e.g. call after sizeChanged()).
       
   532      */
       
   533     private void updateSizes() {
       
   534         int length = 0;
       
   535 
       
   536         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   537             length = getBoxLength(componentInnards.width,
       
   538                                   componentInnards.height);
       
   539             maxPosition = componentInnards.x + componentInnards.width
       
   540                           - length;
       
   541 
       
   542         } else { //VERTICAL progress bar
       
   543             length = getBoxLength(componentInnards.height,
       
   544                                   componentInnards.width);
       
   545             maxPosition = componentInnards.y + componentInnards.height
       
   546                           - length;
       
   547         }
       
   548 
       
   549         //If we're doing bouncing-box animation, update delta.
       
   550         delta = 2.0 * (double)maxPosition/(double)numFrames;
       
   551     }
       
   552 
       
   553     /**
       
   554      * Assumes that the component innards, max position, etc. are up-to-date.
       
   555      */
       
   556     private Rectangle getGenericBox(Rectangle r) {
       
   557         if (r == null) {
       
   558             r = new Rectangle();
       
   559         }
       
   560 
       
   561         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   562             r.width = getBoxLength(componentInnards.width,
       
   563                                    componentInnards.height);
       
   564             if (r.width < 0) {
       
   565                 r = null;
       
   566             } else {
       
   567                 r.height = componentInnards.height;
       
   568                 r.y = componentInnards.y;
       
   569             }
       
   570           // end of HORIZONTAL
       
   571 
       
   572         } else { //VERTICAL progress bar
       
   573             r.height = getBoxLength(componentInnards.height,
       
   574                                     componentInnards.width);
       
   575             if (r.height < 0) {
       
   576                 r = null;
       
   577             } else {
       
   578                 r.width = componentInnards.width;
       
   579                 r.x = componentInnards.x;
       
   580             }
       
   581         } // end of VERTICAL
       
   582 
       
   583         return r;
       
   584     }
       
   585 
       
   586     /**
       
   587      * Returns the length
       
   588      * of the "bouncing box" to be painted.
       
   589      * This method is invoked by the
       
   590      * default implementation of <code>paintIndeterminate</code>
       
   591      * to get the width (if the progress bar is horizontal)
       
   592      * or height (if vertical) of the box.
       
   593      * For example:
       
   594      * <blockquote>
       
   595      * <pre>
       
   596      *boxRect.width = getBoxLength(componentInnards.width,
       
   597      *                             componentInnards.height);
       
   598      * </pre>
       
   599      * </blockquote>
       
   600      *
       
   601      * @param availableLength  the amount of space available
       
   602      *                         for the bouncing box to move in;
       
   603      *                         for a horizontal progress bar,
       
   604      *                         for example,
       
   605      *                         this should be
       
   606      *                         the inside width of the progress bar
       
   607      *                         (the component width minus borders)
       
   608      * @param otherDimension   for a horizontal progress bar, this should be
       
   609      *                         the inside height of the progress bar; this
       
   610      *                         value might be used to constrain or determine
       
   611      *                         the return value
       
   612      *
       
   613      * @return the size of the box dimension being determined;
       
   614      *         must be no larger than <code>availableLength</code>
       
   615      *
       
   616      * @see javax.swing.SwingUtilities#calculateInnerArea
       
   617      * @since 1.5
       
   618      */
       
   619     protected int getBoxLength(int availableLength, int otherDimension) {
       
   620         return (int)Math.round(availableLength/6.0);
       
   621     }
       
   622 
       
   623     /**
       
   624      * All purpose paint method that should do the right thing for all
       
   625      * linear bouncing-box progress bars.
       
   626      * Override this if you are making another kind of
       
   627      * progress bar.
       
   628      *
       
   629      * @param g an instance of {@code Graphics}
       
   630      * @param c a component
       
   631      * @see #paintDeterminate
       
   632      *
       
   633      * @since 1.4
       
   634      */
       
   635     protected void paintIndeterminate(Graphics g, JComponent c) {
       
   636         if (!(g instanceof Graphics2D)) {
       
   637             return;
       
   638         }
       
   639 
       
   640         Insets b = progressBar.getInsets(); // area for border
       
   641         int barRectWidth = progressBar.getWidth() - (b.right + b.left);
       
   642         int barRectHeight = progressBar.getHeight() - (b.top + b.bottom);
       
   643 
       
   644         if (barRectWidth <= 0 || barRectHeight <= 0) {
       
   645             return;
       
   646         }
       
   647 
       
   648         Graphics2D g2 = (Graphics2D)g;
       
   649 
       
   650         // Paint the bouncing box.
       
   651         boxRect = getBox(boxRect);
       
   652         if (boxRect != null) {
       
   653             g2.setColor(progressBar.getForeground());
       
   654             g2.fillRect(boxRect.x, boxRect.y,
       
   655                        boxRect.width, boxRect.height);
       
   656         }
       
   657 
       
   658         // Deal with possible text painting
       
   659         if (progressBar.isStringPainted()) {
       
   660             if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   661                 paintString(g2, b.left, b.top,
       
   662                             barRectWidth, barRectHeight,
       
   663                             boxRect.x, boxRect.width, b);
       
   664             }
       
   665             else {
       
   666                 paintString(g2, b.left, b.top,
       
   667                             barRectWidth, barRectHeight,
       
   668                             boxRect.y, boxRect.height, b);
       
   669             }
       
   670         }
       
   671     }
       
   672 
       
   673 
       
   674     /**
       
   675      * All purpose paint method that should do the right thing for almost
       
   676      * all linear, determinate progress bars. By setting a few values in
       
   677      * the defaults
       
   678      * table, things should work just fine to paint your progress bar.
       
   679      * Naturally, override this if you are making a circular or
       
   680      * semi-circular progress bar.
       
   681      *
       
   682      * @param g an instance of {@code Graphics}
       
   683      * @param c a component
       
   684      * @see #paintIndeterminate
       
   685      *
       
   686      * @since 1.4
       
   687      */
       
   688     protected void paintDeterminate(Graphics g, JComponent c) {
       
   689         if (!(g instanceof Graphics2D)) {
       
   690             return;
       
   691         }
       
   692 
       
   693         Insets b = progressBar.getInsets(); // area for border
       
   694         int barRectWidth = progressBar.getWidth() - (b.right + b.left);
       
   695         int barRectHeight = progressBar.getHeight() - (b.top + b.bottom);
       
   696 
       
   697         if (barRectWidth <= 0 || barRectHeight <= 0) {
       
   698             return;
       
   699         }
       
   700 
       
   701         int cellLength = getCellLength();
       
   702         int cellSpacing = getCellSpacing();
       
   703         // amount of progress to draw
       
   704         int amountFull = getAmountFull(b, barRectWidth, barRectHeight);
       
   705 
       
   706         Graphics2D g2 = (Graphics2D)g;
       
   707         g2.setColor(progressBar.getForeground());
       
   708 
       
   709         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   710             // draw the cells
       
   711             if (cellSpacing == 0 && amountFull > 0) {
       
   712                 // draw one big Rect because there is no space between cells
       
   713                 g2.setStroke(new BasicStroke((float)barRectHeight,
       
   714                         BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
       
   715             } else {
       
   716                 // draw each individual cell
       
   717                 g2.setStroke(new BasicStroke((float)barRectHeight,
       
   718                         BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
       
   719                         0.f, new float[] { cellLength, cellSpacing }, 0.f));
       
   720             }
       
   721 
       
   722             if (BasicGraphicsUtils.isLeftToRight(c)) {
       
   723                 g2.drawLine(b.left, (barRectHeight/2) + b.top,
       
   724                         amountFull + b.left, (barRectHeight/2) + b.top);
       
   725             } else {
       
   726                 g2.drawLine((barRectWidth + b.left),
       
   727                         (barRectHeight/2) + b.top,
       
   728                         barRectWidth + b.left - amountFull,
       
   729                         (barRectHeight/2) + b.top);
       
   730             }
       
   731 
       
   732         } else { // VERTICAL
       
   733             // draw the cells
       
   734             if (cellSpacing == 0 && amountFull > 0) {
       
   735                 // draw one big Rect because there is no space between cells
       
   736                 g2.setStroke(new BasicStroke((float)barRectWidth,
       
   737                         BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
       
   738             } else {
       
   739                 // draw each individual cell
       
   740                 g2.setStroke(new BasicStroke((float)barRectWidth,
       
   741                         BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
       
   742                         0f, new float[] { cellLength, cellSpacing }, 0f));
       
   743             }
       
   744 
       
   745             g2.drawLine(barRectWidth/2 + b.left,
       
   746                     b.top + barRectHeight,
       
   747                     barRectWidth/2 + b.left,
       
   748                     b.top + barRectHeight - amountFull);
       
   749         }
       
   750 
       
   751         // Deal with possible text painting
       
   752         if (progressBar.isStringPainted()) {
       
   753             paintString(g, b.left, b.top,
       
   754                         barRectWidth, barRectHeight,
       
   755                         amountFull, b);
       
   756         }
       
   757     }
       
   758 
       
   759     /**
       
   760      * Paints the progress string.
       
   761      *
       
   762      * @param g an instance of {@code Graphics}
       
   763      * @param x X location of bounding box
       
   764      * @param y Y location of bounding box
       
   765      * @param width width of bounding box
       
   766      * @param height height of bounding box
       
   767      * @param amountFull size of the fill region, either width or height
       
   768      *        depending upon orientation.
       
   769      * @param b Insets of the progress bar.
       
   770      */
       
   771     protected void paintString(Graphics g, int x, int y,
       
   772                                int width, int height,
       
   773                                int amountFull, Insets b) {
       
   774         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   775             if (BasicGraphicsUtils.isLeftToRight(progressBar)) {
       
   776                 if (progressBar.isIndeterminate()) {
       
   777                     boxRect = getBox(boxRect);
       
   778                     paintString(g, x, y, width, height,
       
   779                             boxRect.x, boxRect.width, b);
       
   780                 } else {
       
   781                     paintString(g, x, y, width, height, x, amountFull, b);
       
   782                 }
       
   783             }
       
   784             else {
       
   785                 paintString(g, x, y, width, height, x + width - amountFull,
       
   786                             amountFull, b);
       
   787             }
       
   788         }
       
   789         else {
       
   790             if (progressBar.isIndeterminate()) {
       
   791                 boxRect = getBox(boxRect);
       
   792                 paintString(g, x, y, width, height,
       
   793                         boxRect.y, boxRect.height, b);
       
   794             } else {
       
   795                 paintString(g, x, y, width, height, y + height - amountFull,
       
   796                         amountFull, b);
       
   797             }
       
   798         }
       
   799     }
       
   800 
       
   801     /**
       
   802      * Paints the progress string.
       
   803      *
       
   804      * @param g Graphics used for drawing.
       
   805      * @param x x location of bounding box
       
   806      * @param y y location of bounding box
       
   807      * @param width width of bounding box
       
   808      * @param height height of bounding box
       
   809      * @param fillStart start location, in x or y depending on orientation,
       
   810      *        of the filled portion of the progress bar.
       
   811      * @param amountFull size of the fill region, either width or height
       
   812      *        depending upon orientation.
       
   813      * @param b Insets of the progress bar.
       
   814      */
       
   815     private void paintString(Graphics g, int x, int y, int width, int height,
       
   816                              int fillStart, int amountFull, Insets b) {
       
   817         if (!(g instanceof Graphics2D)) {
       
   818             return;
       
   819         }
       
   820 
       
   821         Graphics2D g2 = (Graphics2D)g;
       
   822         String progressString = progressBar.getString();
       
   823         g2.setFont(progressBar.getFont());
       
   824         Point renderLocation = getStringPlacement(g2, progressString,
       
   825                                                   x, y, width, height);
       
   826         Rectangle oldClip = g2.getClipBounds();
       
   827 
       
   828         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   829             g2.setColor(getSelectionBackground());
       
   830             SwingUtilities2.drawString(progressBar, g2, progressString,
       
   831                                        renderLocation.x, renderLocation.y);
       
   832             g2.setColor(getSelectionForeground());
       
   833             g2.clipRect(fillStart, y, amountFull, height);
       
   834             SwingUtilities2.drawString(progressBar, g2, progressString,
       
   835                                        renderLocation.x, renderLocation.y);
       
   836         } else { // VERTICAL
       
   837             g2.setColor(getSelectionBackground());
       
   838             AffineTransform rotate =
       
   839                     AffineTransform.getRotateInstance(Math.PI/2);
       
   840             g2.setFont(progressBar.getFont().deriveFont(rotate));
       
   841             renderLocation = getStringPlacement(g2, progressString,
       
   842                                                   x, y, width, height);
       
   843             SwingUtilities2.drawString(progressBar, g2, progressString,
       
   844                                        renderLocation.x, renderLocation.y);
       
   845             g2.setColor(getSelectionForeground());
       
   846             g2.clipRect(x, fillStart, width, amountFull);
       
   847             SwingUtilities2.drawString(progressBar, g2, progressString,
       
   848                                        renderLocation.x, renderLocation.y);
       
   849         }
       
   850         g2.setClip(oldClip);
       
   851     }
       
   852 
       
   853 
       
   854     /**
       
   855      * Designate the place where the progress string will be painted.
       
   856      * This implementation places it at the center of the progress
       
   857      * bar (in both x and y). Override this if you want to right,
       
   858      * left, top, or bottom align the progress string or if you need
       
   859      * to nudge it around for any reason.
       
   860      *
       
   861      * @param g an instance of {@code Graphics}
       
   862      * @param progressString a text
       
   863      * @param x an X coordinate
       
   864      * @param y an Y coordinate
       
   865      * @param width a width
       
   866      * @param height a height
       
   867      * @return the place where the progress string will be painted
       
   868      */
       
   869     protected Point getStringPlacement(Graphics g, String progressString,
       
   870                                        int x,int y,int width,int height) {
       
   871         FontMetrics fontSizer = SwingUtilities2.getFontMetrics(progressBar, g,
       
   872                                             progressBar.getFont());
       
   873         int stringWidth = SwingUtilities2.stringWidth(progressBar, fontSizer,
       
   874                                                       progressString);
       
   875 
       
   876         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   877             return new Point(x + Math.round(width/2 - stringWidth/2),
       
   878                              y + ((height +
       
   879                                  fontSizer.getAscent() -
       
   880                                  fontSizer.getLeading() -
       
   881                                  fontSizer.getDescent()) / 2));
       
   882         } else { // VERTICAL
       
   883             return new Point(x + ((width - fontSizer.getAscent() +
       
   884                     fontSizer.getLeading() + fontSizer.getDescent()) / 2),
       
   885                     y + Math.round(height/2 - stringWidth/2));
       
   886         }
       
   887     }
       
   888 
       
   889 
       
   890     public Dimension getPreferredSize(JComponent c) {
       
   891         Dimension       size;
       
   892         Insets          border = progressBar.getInsets();
       
   893         FontMetrics     fontSizer = progressBar.getFontMetrics(
       
   894                                                   progressBar.getFont());
       
   895 
       
   896         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   897             size = new Dimension(getPreferredInnerHorizontal());
       
   898             // Ensure that the progress string will fit
       
   899             if (progressBar.isStringPainted()) {
       
   900                 // I'm doing this for completeness.
       
   901                 String progString = progressBar.getString();
       
   902                 int stringWidth = SwingUtilities2.stringWidth(
       
   903                           progressBar, fontSizer, progString);
       
   904                 if (stringWidth > size.width) {
       
   905                     size.width = stringWidth;
       
   906                 }
       
   907                 // This uses both Height and Descent to be sure that
       
   908                 // there is more than enough room in the progress bar
       
   909                 // for everything.
       
   910                 // This does have a strange dependency on
       
   911                 // getStringPlacememnt() in a funny way.
       
   912                 int stringHeight = fontSizer.getHeight() +
       
   913                                    fontSizer.getDescent();
       
   914                 if (stringHeight > size.height) {
       
   915                     size.height = stringHeight;
       
   916                 }
       
   917             }
       
   918         } else {
       
   919             size = new Dimension(getPreferredInnerVertical());
       
   920             // Ensure that the progress string will fit.
       
   921             if (progressBar.isStringPainted()) {
       
   922                 String progString = progressBar.getString();
       
   923                 int stringHeight = fontSizer.getHeight() +
       
   924                         fontSizer.getDescent();
       
   925                 if (stringHeight > size.width) {
       
   926                     size.width = stringHeight;
       
   927                 }
       
   928                 // This is also for completeness.
       
   929                 int stringWidth = SwingUtilities2.stringWidth(
       
   930                                        progressBar, fontSizer, progString);
       
   931                 if (stringWidth > size.height) {
       
   932                     size.height = stringWidth;
       
   933                 }
       
   934             }
       
   935         }
       
   936 
       
   937         size.width += border.left + border.right;
       
   938         size.height += border.top + border.bottom;
       
   939         return size;
       
   940     }
       
   941 
       
   942     /**
       
   943      * The Minimum size for this component is 10. The rationale here
       
   944      * is that there should be at least one pixel per 10 percent.
       
   945      */
       
   946     public Dimension getMinimumSize(JComponent c) {
       
   947         Dimension pref = getPreferredSize(progressBar);
       
   948         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   949             pref.width = 10;
       
   950         } else {
       
   951             pref.height = 10;
       
   952         }
       
   953         return pref;
       
   954     }
       
   955 
       
   956     public Dimension getMaximumSize(JComponent c) {
       
   957         Dimension pref = getPreferredSize(progressBar);
       
   958         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
       
   959             pref.width = Short.MAX_VALUE;
       
   960         } else {
       
   961             pref.height = Short.MAX_VALUE;
       
   962         }
       
   963         return pref;
       
   964     }
       
   965 
       
   966     /**
       
   967      * Gets the index of the current animation frame.
       
   968      *
       
   969      * @return the index of the current animation frame
       
   970      * @since 1.4
       
   971      */
       
   972     protected int getAnimationIndex() {
       
   973         return animationIndex;
       
   974     }
       
   975 
       
   976     /**
       
   977      * Returns the number of frames for the complete animation loop
       
   978      * used by an indeterminate JProgessBar. The progress chunk will go
       
   979      * from one end to the other and back during the entire loop. This
       
   980      * visual behavior may be changed by subclasses in other Look and Feels.
       
   981      *
       
   982      * @return the number of frames
       
   983      * @since 1.6
       
   984      */
       
   985     protected final int getFrameCount() {
       
   986         return numFrames;
       
   987     }
       
   988 
       
   989     /**
       
   990      * Sets the index of the current animation frame
       
   991      * to the specified value and requests that the
       
   992      * progress bar be repainted.
       
   993      * Subclasses that don't use the default painting code
       
   994      * might need to override this method
       
   995      * to change the way that the <code>repaint</code> method
       
   996      * is invoked.
       
   997      *
       
   998      * @param newValue the new animation index; no checking
       
   999      *                 is performed on its value
       
  1000      * @see #incrementAnimationIndex
       
  1001      *
       
  1002      * @since 1.4
       
  1003      */
       
  1004     protected void setAnimationIndex(int newValue) {
       
  1005         if (animationIndex != newValue) {
       
  1006             if (sizeChanged()) {
       
  1007                 animationIndex = newValue;
       
  1008                 maxPosition = 0;  //needs to be recalculated
       
  1009                 delta = 0.0;      //needs to be recalculated
       
  1010                 progressBar.repaint();
       
  1011                 return;
       
  1012             }
       
  1013 
       
  1014             //Get the previous box drawn.
       
  1015             nextPaintRect = getBox(nextPaintRect);
       
  1016 
       
  1017             //Update the frame number.
       
  1018             animationIndex = newValue;
       
  1019 
       
  1020             //Get the next box to draw.
       
  1021             if (nextPaintRect != null) {
       
  1022                 boxRect = getBox(boxRect);
       
  1023                 if (boxRect != null) {
       
  1024                     nextPaintRect.add(boxRect);
       
  1025                 }
       
  1026             }
       
  1027         } else { //animationIndex == newValue
       
  1028             return;
       
  1029         }
       
  1030 
       
  1031         if (nextPaintRect != null) {
       
  1032             progressBar.repaint(nextPaintRect);
       
  1033         } else {
       
  1034             progressBar.repaint();
       
  1035         }
       
  1036     }
       
  1037 
       
  1038     private boolean sizeChanged() {
       
  1039         if ((oldComponentInnards == null) || (componentInnards == null)) {
       
  1040             return true;
       
  1041         }
       
  1042 
       
  1043         oldComponentInnards.setRect(componentInnards);
       
  1044         componentInnards = SwingUtilities.calculateInnerArea(progressBar,
       
  1045                                                              componentInnards);
       
  1046         return !oldComponentInnards.equals(componentInnards);
       
  1047     }
       
  1048 
       
  1049     /**
       
  1050      * Sets the index of the current animation frame,
       
  1051      * to the next valid value,
       
  1052      * which results in the progress bar being repainted.
       
  1053      * The next valid value is, by default,
       
  1054      * the current animation index plus one.
       
  1055      * If the new value would be too large,
       
  1056      * this method sets the index to 0.
       
  1057      * Subclasses might need to override this method
       
  1058      * to ensure that the index does not go over
       
  1059      * the number of frames needed for the particular
       
  1060      * progress bar instance.
       
  1061      * This method is invoked by the default animation thread
       
  1062      * every <em>X</em> milliseconds,
       
  1063      * where <em>X</em> is specified by the "ProgressBar.repaintInterval"
       
  1064      * UI default.
       
  1065      *
       
  1066      * @see #setAnimationIndex
       
  1067      * @since 1.4
       
  1068      */
       
  1069     protected void incrementAnimationIndex() {
       
  1070         int newValue = getAnimationIndex() + 1;
       
  1071 
       
  1072         if (newValue < numFrames) {
       
  1073             setAnimationIndex(newValue);
       
  1074         } else {
       
  1075             setAnimationIndex(0);
       
  1076         }
       
  1077     }
       
  1078 
       
  1079     /**
       
  1080      * Returns the desired number of milliseconds between repaints.
       
  1081      * This value is meaningful
       
  1082      * only if the progress bar is in indeterminate mode.
       
  1083      * The repaint interval determines how often the
       
  1084      * default animation thread's timer is fired.
       
  1085      * It's also used by the default indeterminate progress bar
       
  1086      * painting code when determining
       
  1087      * how far to move the bouncing box per frame.
       
  1088      * The repaint interval is specified by
       
  1089      * the "ProgressBar.repaintInterval" UI default.
       
  1090      *
       
  1091      * @return  the repaint interval, in milliseconds
       
  1092      */
       
  1093     private int getRepaintInterval() {
       
  1094         return repaintInterval;
       
  1095     }
       
  1096 
       
  1097     private int initRepaintInterval() {
       
  1098         repaintInterval = DefaultLookup.getInt(progressBar,
       
  1099                 this, "ProgressBar.repaintInterval", 50);
       
  1100         return repaintInterval;
       
  1101     }
       
  1102 
       
  1103     /**
       
  1104      * Returns the number of milliseconds per animation cycle.
       
  1105      * This value is meaningful
       
  1106      * only if the progress bar is in indeterminate mode.
       
  1107      * The cycle time is used by the default indeterminate progress bar
       
  1108      * painting code when determining
       
  1109      * how far to move the bouncing box per frame.
       
  1110      * The cycle time is specified by
       
  1111      * the "ProgressBar.cycleTime" UI default
       
  1112      * and adjusted, if necessary,
       
  1113      * by the initIndeterminateDefaults method.
       
  1114      *
       
  1115      * @return  the cycle time, in milliseconds
       
  1116      */
       
  1117     private int getCycleTime() {
       
  1118         return cycleTime;
       
  1119     }
       
  1120 
       
  1121     private int initCycleTime() {
       
  1122         cycleTime = DefaultLookup.getInt(progressBar, this,
       
  1123                 "ProgressBar.cycleTime", 3000);
       
  1124         return cycleTime;
       
  1125     }
       
  1126 
       
  1127 
       
  1128     /** Initialize cycleTime, repaintInterval, numFrames, animationIndex. */
       
  1129     private void initIndeterminateDefaults() {
       
  1130         initRepaintInterval(); //initialize repaint interval
       
  1131         initCycleTime();       //initialize cycle length
       
  1132 
       
  1133         // Make sure repaintInterval is reasonable.
       
  1134         if (repaintInterval <= 0) {
       
  1135             repaintInterval = 100;
       
  1136         }
       
  1137 
       
  1138         // Make sure cycleTime is reasonable.
       
  1139         if (repaintInterval > cycleTime) {
       
  1140             cycleTime = repaintInterval * 20;
       
  1141         } else {
       
  1142             // Force cycleTime to be a even multiple of repaintInterval.
       
  1143             int factor = (int)Math.ceil(
       
  1144                                  ((double)cycleTime)
       
  1145                                / ((double)repaintInterval*2));
       
  1146             cycleTime = repaintInterval*factor*2;
       
  1147         }
       
  1148     }
       
  1149 
       
  1150     /**
       
  1151      * Invoked by PropertyChangeHandler.
       
  1152      *
       
  1153      *  NOTE: This might not be invoked until after the first
       
  1154      *  paintIndeterminate call.
       
  1155      */
       
  1156     private void initIndeterminateValues() {
       
  1157         initIndeterminateDefaults();
       
  1158         //assert cycleTime/repaintInterval is a whole multiple of 2.
       
  1159         numFrames = cycleTime/repaintInterval;
       
  1160         initAnimationIndex();
       
  1161 
       
  1162         boxRect = new Rectangle();
       
  1163         nextPaintRect = new Rectangle();
       
  1164         componentInnards = new Rectangle();
       
  1165         oldComponentInnards = new Rectangle();
       
  1166 
       
  1167         // we only bother installing the HierarchyChangeListener if we
       
  1168         // are indeterminate
       
  1169         progressBar.addHierarchyListener(getHandler());
       
  1170 
       
  1171         // start the animation thread if necessary
       
  1172         if (progressBar.isDisplayable()) {
       
  1173             startAnimationTimer();
       
  1174         }
       
  1175     }
       
  1176 
       
  1177     /** Invoked by PropertyChangeHandler. */
       
  1178     private void cleanUpIndeterminateValues() {
       
  1179         // stop the animation thread if necessary
       
  1180         if (progressBar.isDisplayable()) {
       
  1181             stopAnimationTimer();
       
  1182         }
       
  1183 
       
  1184         cycleTime = repaintInterval = 0;
       
  1185         numFrames = animationIndex = 0;
       
  1186         maxPosition = 0;
       
  1187         delta = 0.0;
       
  1188 
       
  1189         boxRect = nextPaintRect = null;
       
  1190         componentInnards = oldComponentInnards = null;
       
  1191 
       
  1192         progressBar.removeHierarchyListener(getHandler());
       
  1193     }
       
  1194 
       
  1195     // Called from initIndeterminateValues to initialize the animation index.
       
  1196     // This assumes that numFrames is set to a correct value.
       
  1197     private void initAnimationIndex() {
       
  1198         if ((progressBar.getOrientation() == JProgressBar.HORIZONTAL) &&
       
  1199             (BasicGraphicsUtils.isLeftToRight(progressBar))) {
       
  1200             // If this is a left-to-right progress bar,
       
  1201             // start at the first frame.
       
  1202             setAnimationIndex(0);
       
  1203         } else {
       
  1204             // If we go right-to-left or vertically, start at the right/bottom.
       
  1205             setAnimationIndex(numFrames/2);
       
  1206         }
       
  1207     }
       
  1208 
       
  1209     //
       
  1210     // Animation Thread
       
  1211     //
       
  1212     /**
       
  1213      * Implements an animation thread that invokes repaint
       
  1214      * at a fixed rate.  If ADJUSTTIMER is true, this thread
       
  1215      * will continuously adjust the repaint interval to
       
  1216      * try to make the actual time between repaints match
       
  1217      * the requested rate.
       
  1218      */
       
  1219     private class Animator implements ActionListener {
       
  1220         private Timer timer;
       
  1221         private long previousDelay; //used to tune the repaint interval
       
  1222         private int interval; //the fixed repaint interval
       
  1223         private long lastCall; //the last time actionPerformed was called
       
  1224         private int MINIMUM_DELAY = 5;
       
  1225 
       
  1226         /**
       
  1227          * Creates a timer if one doesn't already exist,
       
  1228          * then starts the timer thread.
       
  1229          */
       
  1230         private void start(int interval) {
       
  1231             previousDelay = interval;
       
  1232             lastCall = 0;
       
  1233 
       
  1234             if (timer == null) {
       
  1235                 timer = new Timer(interval, this);
       
  1236             } else {
       
  1237                 timer.setDelay(interval);
       
  1238             }
       
  1239 
       
  1240             if (ADJUSTTIMER) {
       
  1241                 timer.setRepeats(false);
       
  1242                 timer.setCoalesce(false);
       
  1243             }
       
  1244 
       
  1245             timer.start();
       
  1246         }
       
  1247 
       
  1248         /**
       
  1249          * Stops the timer thread.
       
  1250          */
       
  1251         private void stop() {
       
  1252             timer.stop();
       
  1253         }
       
  1254 
       
  1255         /**
       
  1256          * Reacts to the timer's action events.
       
  1257          */
       
  1258         public void actionPerformed(ActionEvent e) {
       
  1259             if (ADJUSTTIMER) {
       
  1260                 long time = System.currentTimeMillis();
       
  1261 
       
  1262                 if (lastCall > 0) { //adjust nextDelay
       
  1263                 //XXX maybe should cache this after a while
       
  1264                     //actual = time - lastCall
       
  1265                     //difference = actual - interval
       
  1266                     //nextDelay = previousDelay - difference
       
  1267                     //          = previousDelay - (time - lastCall - interval)
       
  1268                    int nextDelay = (int)(previousDelay
       
  1269                                           - time + lastCall
       
  1270                                           + getRepaintInterval());
       
  1271                     if (nextDelay < MINIMUM_DELAY) {
       
  1272                         nextDelay = MINIMUM_DELAY;
       
  1273                     }
       
  1274                     timer.setInitialDelay(nextDelay);
       
  1275                     previousDelay = nextDelay;
       
  1276                 }
       
  1277                 timer.start();
       
  1278                 lastCall = time;
       
  1279             }
       
  1280 
       
  1281             incrementAnimationIndex(); //paint next frame
       
  1282         }
       
  1283     }
       
  1284 
       
  1285 
       
  1286     /**
       
  1287      * This class should be treated as a &quot;protected&quot; inner class.
       
  1288      * Instantiate it only within subclasses of {@code BasicProgressBarUI}.
       
  1289      */
       
  1290     public class ChangeHandler implements ChangeListener {
       
  1291         // NOTE: This class exists only for backward compatibility. All
       
  1292         // its functionality has been moved into Handler. If you need to add
       
  1293         // new functionality add it to the Handler, but make sure this
       
  1294         // class calls into the Handler.
       
  1295         public void stateChanged(ChangeEvent e) {
       
  1296             getHandler().stateChanged(e);
       
  1297         }
       
  1298     }
       
  1299 
       
  1300 
       
  1301     private class Handler implements ChangeListener, PropertyChangeListener, HierarchyListener {
       
  1302         // ChangeListener
       
  1303         public void stateChanged(ChangeEvent e) {
       
  1304             BoundedRangeModel model = progressBar.getModel();
       
  1305             int newRange = model.getMaximum() - model.getMinimum();
       
  1306             int newPercent;
       
  1307             int oldPercent = getCachedPercent();
       
  1308 
       
  1309             if (newRange > 0) {
       
  1310                 newPercent = (int)((100 * (long)model.getValue()) / newRange);
       
  1311             } else {
       
  1312                 newPercent = 0;
       
  1313             }
       
  1314 
       
  1315             if (newPercent != oldPercent) {
       
  1316                 setCachedPercent(newPercent);
       
  1317                 progressBar.repaint();
       
  1318             }
       
  1319         }
       
  1320 
       
  1321         // PropertyChangeListener
       
  1322         public void propertyChange(PropertyChangeEvent e) {
       
  1323             String prop = e.getPropertyName();
       
  1324             if ("indeterminate" == prop) {
       
  1325                 if (progressBar.isIndeterminate()) {
       
  1326                     initIndeterminateValues();
       
  1327                 } else {
       
  1328                     //clean up
       
  1329                     cleanUpIndeterminateValues();
       
  1330                 }
       
  1331                 progressBar.repaint();
       
  1332             }
       
  1333         }
       
  1334 
       
  1335         // we don't want the animation to keep running if we're not displayable
       
  1336         public void hierarchyChanged(HierarchyEvent he) {
       
  1337             if ((he.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
       
  1338                 if (progressBar.isIndeterminate()) {
       
  1339                     if (progressBar.isDisplayable()) {
       
  1340                         startAnimationTimer();
       
  1341                     } else {
       
  1342                         stopAnimationTimer();
       
  1343                     }
       
  1344                 }
       
  1345             }
       
  1346         }
       
  1347     }
       
  1348 }