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

Save This Page
Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]