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

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

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