KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > basic > BasicProgressBarUI


1 /*
2  * @(#)BasicProgressBarUI.java 1.68 04/03/11
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing.plaf.basic;
9
10 import com.sun.java.swing.SwingUtilities2;
11 import java.awt.*;
12 import java.awt.geom.AffineTransform JavaDoc;
13 import java.awt.event.*;
14 import javax.swing.*;
15 import javax.swing.event.*;
16 import javax.swing.plaf.*;
17 import java.beans.PropertyChangeListener JavaDoc;
18 import java.beans.PropertyChangeEvent JavaDoc;
19 import java.io.Serializable JavaDoc;
20 import sun.swing.DefaultLookup;
21
22 /**
23  * A Basic L&F implementation of ProgressBarUI.
24  *
25  * @version 1.68 03/11/04
26  * @author Michael C. Albers
27  * @author Kathy Walrath
28  */

29 public class BasicProgressBarUI extends ProgressBarUI {
30     private int cachedPercent;
31     private int cellLength, cellSpacing;
32     // The "selectionForeground" is the color of the text when it is painted
33
// over a filled area of the progress bar. The "selectionBackground"
34
// is for the text over the unfilled progress bar area.
35
private Color selectionForeground, selectionBackground;
36
37     private Animator animator;
38
39     protected JProgressBar progressBar;
40     protected ChangeListener JavaDoc changeListener;
41     private Handler handler;
42
43     /**
44      * The current state of the indeterminate animation's cycle.
45      * 0, the initial value, means paint the first frame.
46      * When the progress bar is indeterminate and showing,
47      * the default animation thread updates this variable
48      * by invoking incrementAnimationIndex()
49      * every repaintInterval milliseconds.
50      */

51     private int animationIndex = 0;
52
53     /**
54      * The number of frames per cycle. Under the default implementation,
55      * this depends on the cycleTime and repaintInterval. It
56      * must be an even number for the default painting algorithm. This
57      * value is set in the initIndeterminateValues method.
58      */

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

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

68     private int repaintInterval;
69
70     /**
71      * The number of milliseconds until the animation cycle repeats.
72      * The value of this method is set
73      * (every time the progress bar changes to indeterminate mode)
74      * using the
75      * "ProgressBar.cycleTime" key in the defaults table.
76      */

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

79     //performance stuff
80
private static boolean ADJUSTTIMER = true; //makes a BIG difference;
81
//make this false for
82
//performance tests
83

84     /**
85      * Used to hold the location and size of the bouncing box (returned
86      * by getBox) to be painted.
87      *
88      * @since 1.5
89      */

90     protected Rectangle boxRect;
91                                                                     
92     /**
93      * The rectangle to be updated the next time the
94      * animation thread calls repaint. For bouncing-box
95      * animation this rect should include the union of
96      * the currently displayed box (which needs to be erased)
97      * and the box to be displayed next.
98      * This rectangle's values are set in
99      * the setAnimationIndex method.
100      */

101     private Rectangle nextPaintRect;
102                                                                     
103     //cache
104
/** The component's painting area, not including the border. */
105     private Rectangle componentInnards; //the current painting area
106
private Rectangle oldComponentInnards; //used to see if the size changed
107

108     /** For bouncing-box animation, the change in position per frame. */
109     private double delta = 0.0;
110
111     private int maxPosition = 0; //maximum X (horiz) or Y box location
112

113     
114     public static ComponentUI createUI(JComponent x) {
115     return new BasicProgressBarUI JavaDoc();
116     }
117     
118     public void installUI(JComponent c) {
119     progressBar = (JProgressBar)c;
120     installDefaults();
121     installListeners();
122         if (progressBar.isIndeterminate()) {
123             initIndeterminateValues();
124         }
125     }
126     
127     public void uninstallUI(JComponent c) {
128         if (progressBar.isIndeterminate()) {
129             cleanUpIndeterminateValues();
130         }
131     uninstallDefaults();
132     uninstallListeners();
133     progressBar = null;
134     }
135     
136     protected void installDefaults() {
137         LookAndFeel.installProperty(progressBar, "opaque", Boolean.TRUE);
138     LookAndFeel.installBorder(progressBar,"ProgressBar.border");
139     LookAndFeel.installColorsAndFont(progressBar,
140                      "ProgressBar.background",
141                      "ProgressBar.foreground",
142                      "ProgressBar.font");
143     cellLength = UIManager.getInt("ProgressBar.cellLength");
144     cellSpacing = UIManager.getInt("ProgressBar.cellSpacing");
145     selectionForeground = UIManager.getColor("ProgressBar.selectionForeground");
146     selectionBackground = UIManager.getColor("ProgressBar.selectionBackground");
147     }
148     
149     protected void uninstallDefaults() {
150     LookAndFeel.uninstallBorder(progressBar);
151     }
152     
153     protected void installListeners() {
154     //Listen for changes in the progress bar's data.
155
changeListener = getHandler();
156     progressBar.addChangeListener(changeListener);
157
158     //Listen for changes between determinate and indeterminate state.
159
progressBar.addPropertyChangeListener(getHandler());
160     }
161     
162     private Handler getHandler() {
163         if (handler == null) {
164             handler = new Handler();
165         }
166         return handler;
167     }
168
169     /**
170      * Starts the animation thread, creating and initializing
171      * it if necessary. This method is invoked when an
172      * indeterminate progress bar should start animating.
173      * Reasons for this may include:
174      * <ul>
175      * <li>The progress bar is determinate and becomes displayable
176      * <li>The progress bar is displayable and becomes determinate
177      * <li>The progress bar is displayable and determinate and this
178      * UI is installed
179      * </ul>
180      * If you implement your own animation thread,
181      * you must override this method.
182      *
183      * @since 1.4
184      * @see #stopAnimationTimer
185      */

186     protected void startAnimationTimer() {
187         if (animator == null) {
188         animator = new Animator();
189         }
190
191         animator.start(getRepaintInterval());
192     }
193
194     /**
195      * Stops the animation thread.
196      * This method is invoked when the indeterminate
197      * animation should be stopped. Reasons for this may include:
198      * <ul>
199      * <li>The progress bar changes to determinate
200      * <li>The progress bar is no longer part of a displayable hierarchy
201      * <li>This UI in uninstalled
202      * </ul>
203      * If you implement your own animation thread,
204      * you must override this method.
205      *
206      * @since 1.4
207      * @see #startAnimationTimer
208      */

209     protected void stopAnimationTimer() {
210     if (animator != null) {
211             animator.stop();
212     }
213     }
214
215     /**
216      * Removes all listeners installed by this object.
217      */

218     protected void uninstallListeners() {
219     progressBar.removeChangeListener(changeListener);
220     progressBar.removePropertyChangeListener(getHandler());
221         handler = null;
222     }
223
224     
225     // Many of the Basic*UI components have the following methods.
226
// This component does not have these methods because *ProgressBarUI
227
// is not a compound component and does not accept input.
228
//
229
// protected void installComponents()
230
// protected void uninstallComponents()
231
// protected void installKeyboardActions()
232
// protected void uninstallKeyboardActions()
233

234     protected Dimension getPreferredInnerHorizontal() {
235         Dimension horizDim = (Dimension)DefaultLookup.get(progressBar, this,
236             "ProgressBar.horizontalSize");
237         if (horizDim == null) {
238             horizDim = new Dimension(146, 12);
239         }
240         return horizDim;
241     }
242     
243     protected Dimension getPreferredInnerVertical() {
244         Dimension vertDim = (Dimension)DefaultLookup.get(progressBar, this,
245             "ProgressBar.vertictalSize");
246         if (vertDim == null) {
247             vertDim = new Dimension(12, 146);
248         }
249         return vertDim;
250     }
251     
252     /**
253      * The "selectionForeground" is the color of the text when it is painted
254      * over a filled area of the progress bar.
255      */

256     protected Color getSelectionForeground() {
257     return selectionForeground;
258     }
259     
260     /**
261      * The "selectionBackground" is the color of the text when it is painted
262      * over an unfilled area of the progress bar.
263      */

264     protected Color getSelectionBackground() {
265     return selectionBackground;
266     }
267     
268     private int getCachedPercent() {
269     return cachedPercent;
270     }
271     
272     private void setCachedPercent(int cachedPercent) {
273     this.cachedPercent = cachedPercent;
274     }
275     
276     /**
277      * Returns the width (if HORIZONTAL) or height (if VERTICAL)
278      * of each of the indivdual cells/units to be rendered in the
279      * progress bar. However, for text rendering simplification and
280      * aesthetic considerations, this function will return 1 when
281      * the progress string is being rendered.
282      *
283      * @return the value representing the spacing between cells
284      * @see #setCellLength
285      * @see JProgressBar#isStringPainted
286      */

287     protected int getCellLength() {
288     if (progressBar.isStringPainted()) {
289         return 1;
290     } else {
291         return cellLength;
292     }
293     }
294     
295     protected void setCellLength(int cellLen) {
296     this.cellLength = cellLen;
297     }
298     
299     /**
300      * Returns the spacing between each of the cells/units in the
301      * progress bar. However, for text rendering simplification and
302      * aesthetic considerations, this function will return 0 when
303      * the progress string is being rendered.
304      *
305      * @return the value representing the spacing between cells
306      * @see #setCellSpacing
307      * @see JProgressBar#isStringPainted
308      */

309     protected int getCellSpacing() {
310     if (progressBar.isStringPainted()) {
311         return 0;
312     } else {
313         return cellSpacing;
314     }
315     }
316     
317     protected void setCellSpacing(int cellSpace) {
318     this.cellSpacing = cellSpace;
319     }
320     
321     /**
322      * This determines the amount of the progress bar that should be filled
323      * based on the percent done gathered from the model. This is a common
324      * operation so it was abstracted out. It assumes that your progress bar
325      * is linear. That is, if you are making a circular progress indicator,
326      * you will want to override this method.
327      */

328     protected int getAmountFull(Insets b, int width, int height) {
329     int amountFull = 0;
330     BoundedRangeModel model = progressBar.getModel();
331     
332     if ( (model.getMaximum() - model.getMinimum()) != 0) {
333         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
334             amountFull = (int)Math.round(width *
335                          progressBar.getPercentComplete());
336         } else {
337             amountFull = (int)Math.round(height *
338                          progressBar.getPercentComplete());
339         }
340     }
341     return amountFull;
342     }
343     
344     /**
345      * Delegates painting to one of two methods:
346      * paintDeterminate or paintIndeterminate.
347      */

348     public void paint(Graphics g, JComponent c) {
349         if (progressBar.isIndeterminate()) {
350             paintIndeterminate(g, c);
351         } else {
352             paintDeterminate(g, c);
353         }
354     }
355
356     /**
357      * Stores the position and size of
358      * the bouncing box that would be painted for the current animation index
359      * in <code>r</code> and returns <code>r</code>.
360      * Subclasses that add to the painting performed
361      * in this class's implementation of <code>paintIndeterminate</code> --
362      * to draw an outline around the bouncing box, for example --
363      * can use this method to get the location of the bouncing
364      * box that was just painted.
365      * By overriding this method,
366      * you have complete control over the size and position
367      * of the bouncing box,
368      * without having to reimplement <code>paintIndeterminate</code>.
369      *
370      * @param r the Rectangle instance to be modified;
371      * may be <code>null</code>
372      * @return <code>null</code> if no box should be drawn;
373      * otherwise, returns the passed-in rectangle
374      * (if non-null)
375      * or a new rectangle
376      *
377      * @see #setAnimationIndex
378      * @since 1.4
379      */

380     protected Rectangle getBox(Rectangle r) {
381         int currentFrame = getAnimationIndex();
382         int middleFrame = numFrames/2;
383
384         if (sizeChanged() || delta == 0.0 || maxPosition == 0.0) {
385             updateSizes();
386         }
387
388         r = getGenericBox(r);
389
390         if (r == null) {
391             return null;
392         }
393         if (middleFrame <= 0) {
394             return null;
395         }
396
397         //assert currentFrame >= 0 && currentFrame < numFrames
398
if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
399             if (currentFrame < middleFrame) {
400                 r.x = componentInnards.x
401                       + (int)Math.round(delta * (double)currentFrame);
402             } else {
403                 r.x = maxPosition
404                       - (int)Math.round(delta *
405                                         (currentFrame - middleFrame));
406             }
407         } else { //VERTICAL indeterminate progress bar
408
if (currentFrame < middleFrame) {
409                 r.y = componentInnards.y
410                       + (int)Math.round(delta * currentFrame);
411             } else {
412                 r.y = maxPosition
413                       - (int)Math.round(delta *
414                                         (currentFrame - middleFrame));
415             }
416         }
417         return r;
418     }
419
420     /**
421      * Updates delta, max position.
422      * Assumes componentInnards is correct (e.g. call after sizeChanged()).
423      */

424     private void updateSizes() {
425         int length = 0;
426
427         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
428             length = getBoxLength(componentInnards.width,
429                                   componentInnards.height);
430             maxPosition = componentInnards.x + componentInnards.width
431                           - length;
432
433         } else { //VERTICAL progress bar
434
length = getBoxLength(componentInnards.height,
435                                   componentInnards.width);
436             maxPosition = componentInnards.y + componentInnards.height
437                           - length;
438         }
439
440         //If we're doing bouncing-box animation, update delta.
441
delta = 2.0 * (double)maxPosition/(double)numFrames;
442     }
443
444     /**
445      * Assumes that the component innards, max position, etc. are up-to-date.
446      */

447     private Rectangle getGenericBox(Rectangle r) {
448         if (r == null) {
449             r = new Rectangle();
450         }
451
452         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
453             r.width = getBoxLength(componentInnards.width,
454                                    componentInnards.height);
455             if (r.width < 0) {
456                 r = null;
457             } else {
458                 r.height = componentInnards.height;
459                 r.y = componentInnards.y;
460             }
461           // end of HORIZONTAL
462

463         } else { //VERTICAL progress bar
464
r.height = getBoxLength(componentInnards.height,
465                                     componentInnards.width);
466             if (r.height < 0) {
467                 r = null;
468             } else {
469                 r.width = componentInnards.width;
470                 r.x = componentInnards.x;
471             }
472         } // end of VERTICAL
473

474         return r;
475     }
476
477     /**
478      * Returns the length
479      * of the "bouncing box" to be painted.
480      * This method is invoked by the
481      * default implementation of <code>paintIndeterminate</code>
482      * to get the width (if the progress bar is horizontal)
483      * or height (if vertical) of the box.
484      * For example:
485      * <blockquote>
486      * <pre>
487      *boxRect.width = getBoxLength(componentInnards.width,
488      * componentInnards.height);
489      * </pre>
490      * </blockquote>
491      *
492      * @param availableLength the amount of space available
493      * for the bouncing box to move in;
494      * for a horizontal progress bar,
495      * for example,
496      * this should be
497      * the inside width of the progress bar
498      * (the component width minus borders)
499      * @param otherDimension for a horizontal progress bar, this should be
500      * the inside height of the progress bar; this
501      * value might be used to constrain or determine
502      * the return value
503      *
504      * @return the size of the box dimension being determined;
505      * must be no larger than <code>availableLength</code>
506      *
507      * @see javax.swing.SwingUtilities#calculateInnerArea
508      * @since 1.5
509      */

510     protected int getBoxLength(int availableLength, int otherDimension) {
511         return (int)Math.round(availableLength/6.0);
512     }
513
514     /**
515      * All purpose paint method that should do the right thing for all
516      * linear bouncing-box progress bars.
517      * Override this if you are making another kind of
518      * progress bar.
519      *
520      * @see #paintDeterminate
521      *
522      * @since 1.4
523      */

524     protected void paintIndeterminate(Graphics g, JComponent c) {
525         if (!(g instanceof Graphics2D)) {
526             return;
527         }
528
529     Insets b = progressBar.getInsets(); // area for border
530
int barRectWidth = progressBar.getWidth() - (b.right + b.left);
531     int barRectHeight = progressBar.getHeight() - (b.top + b.bottom);
532
533         Graphics2D g2 = (Graphics2D)g;
534
535         // Paint the bouncing box.
536
boxRect = getBox(boxRect);
537         if (boxRect != null) {
538             g2.setColor(progressBar.getForeground());
539             g2.fillRect(boxRect.x, boxRect.y,
540                        boxRect.width, boxRect.height);
541         }
542
543     // Deal with possible text painting
544
if (progressBar.isStringPainted()) {
545             if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
546                 paintString(g2, b.left, b.top,
547                             barRectWidth, barRectHeight,
548                             boxRect.x, boxRect.width, b);
549             }
550             else {
551                 paintString(g2, b.left, b.top,
552                             barRectWidth, barRectHeight,
553                             boxRect.y, boxRect.height, b);
554             }
555         }
556     }
557
558
559     /**
560      * All purpose paint method that should do the right thing for almost
561      * all linear, determinate progress bars. By setting a few values in
562      * the defaults
563      * table, things should work just fine to paint your progress bar.
564      * Naturally, override this if you are making a circular or
565      * semi-circular progress bar.
566      *
567      * @see #paintIndeterminate
568      *
569      * @since 1.4
570      */

571     protected void paintDeterminate(Graphics g, JComponent c) {
572         if (!(g instanceof Graphics2D)) {
573             return;
574         }
575
576     Insets b = progressBar.getInsets(); // area for border
577
int barRectWidth = progressBar.getWidth() - (b.right + b.left);
578     int barRectHeight = progressBar.getHeight() - (b.top + b.bottom);
579
580         int cellLength = getCellLength();
581         int cellSpacing = getCellSpacing();
582     // amount of progress to draw
583
int amountFull = getAmountFull(b, barRectWidth, barRectHeight);
584     
585         Graphics2D g2 = (Graphics2D)g;
586     g2.setColor(progressBar.getForeground());
587
588     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
589         // draw the cells
590
if (cellSpacing == 0 && amountFull > 0) {
591                 // draw one big Rect because there is no space between cells
592
g2.setStroke(new BasicStroke((float)barRectHeight,
593                         BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
594         } else {
595                 // draw each individual cell
596
g2.setStroke(new BasicStroke((float)barRectHeight,
597                         BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
598                         0.f, new float[] { cellLength, cellSpacing }, 0.f));
599             }
600
601             if (BasicGraphicsUtils.isLeftToRight(c)) {
602                 g2.drawLine(b.left, (barRectHeight/2) + b.top,
603                         amountFull + b.left, (barRectHeight/2) + b.top);
604             } else {
605                 g2.drawLine((barRectWidth + b.left),
606                         (barRectHeight/2) + b.top,
607                         barRectWidth + b.left - amountFull,
608                         (barRectHeight/2) + b.top);
609             }
610             
611     } else { // VERTICAL
612
// draw the cells
613
if (cellSpacing == 0 && amountFull > 0) {
614                 // draw one big Rect because there is no space between cells
615
g2.setStroke(new BasicStroke((float)barRectWidth,
616                         BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
617         } else {
618                 // draw each individual cell
619
g2.setStroke(new BasicStroke((float)barRectWidth,
620                         BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
621                         0f, new float[] { cellLength, cellSpacing }, 0f));
622         }
623
624             g2.drawLine(barRectWidth/2 + b.left,
625                     b.top + barRectHeight,
626                     barRectWidth/2 + b.left,
627                     b.top + barRectHeight - amountFull);
628     }
629     
630     // Deal with possible text painting
631
if (progressBar.isStringPainted()) {
632         paintString(g, b.left, b.top,
633             barRectWidth, barRectHeight,
634             amountFull, b);
635     }
636     }
637
638
639     protected void paintString(Graphics g, int x, int y,
640                    int width, int height,
641                    int amountFull, Insets b) {
642     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
643             if (BasicGraphicsUtils.isLeftToRight(progressBar)) {
644                 if (progressBar.isIndeterminate()) {
645                     boxRect = getBox(boxRect);
646                     paintString(g, x, y, width, height,
647                             boxRect.x, boxRect.width, b);
648                 } else {
649                     paintString(g, x, y, width, height, x, amountFull, b);
650                 }
651             }
652             else {
653                 paintString(g, x, y, width, height, x + width - amountFull,
654                             amountFull, b);
655             }
656         }
657         else {
658             if (progressBar.isIndeterminate()) {
659                 boxRect = getBox(boxRect);
660                 paintString(g, x, y, width, height,
661                         boxRect.y, boxRect.height, b);
662             } else {
663                 paintString(g, x, y, width, height, y + height - amountFull,
664                         amountFull, b);
665             }
666         }
667     }
668
669     /**
670      * Paints the progress string.
671      *
672      * @param g Graphics used for drawing.
673      * @param x x location of bounding box
674      * @param y y location of bounding box
675      * @param width width of bounding box
676      * @param height height of bounding box
677      * @param fillStart start location, in x or y depending on orientation,
678      * of the filled portion of the progress bar.
679      * @param amountFull size of the fill region, either width or height
680      * depending upon orientation.
681      * @param b Insets of the progress bar.
682      */

683     private void paintString(Graphics g, int x, int y, int width, int height,
684                              int fillStart, int amountFull, Insets b) {
685         if (!(g instanceof Graphics2D)) {
686             return;
687         }
688
689         Graphics2D g2 = (Graphics2D)g;
690     String JavaDoc progressString = progressBar.getString();
691     g2.setFont(progressBar.getFont());
692     Point renderLocation = getStringPlacement(g2, progressString,
693                           x, y, width, height);
694     Rectangle oldClip = g2.getClipBounds();
695     
696     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
697         g2.setColor(getSelectionBackground());
698         SwingUtilities2.drawString(progressBar, g2, progressString,
699                                        renderLocation.x, renderLocation.y);
700         g2.setColor(getSelectionForeground());
701             g2.clipRect(fillStart, y, amountFull, height);
702         SwingUtilities2.drawString(progressBar, g2, progressString,
703                                        renderLocation.x, renderLocation.y);
704     } else { // VERTICAL
705
g2.setColor(getSelectionBackground());
706             AffineTransform JavaDoc rotate =
707                     AffineTransform.getRotateInstance(Math.PI/2);
708             g2.setFont(progressBar.getFont().deriveFont(rotate));
709         renderLocation = getStringPlacement(g2, progressString,
710                           x, y, width, height);
711         SwingUtilities2.drawString(progressBar, g2, progressString,
712                                        renderLocation.x, renderLocation.y);
713         g2.setColor(getSelectionForeground());
714         g2.clipRect(x, fillStart, width, amountFull);
715         SwingUtilities2.drawString(progressBar, g2, progressString,
716                                        renderLocation.x, renderLocation.y);
717     }
718     g2.setClip(oldClip);
719     }
720     
721     
722     /**
723      * Designate the place where the progress string will be painted.
724      * This implementation places it at the center of the progress
725      * bar (in both x and y). Override this if you want to right,
726      * left, top, or bottom align the progress string or if you need
727      * to nudge it around for any reason.
728      */

729     protected Point getStringPlacement(Graphics g, String JavaDoc progressString,
730                        int x,int y,int width,int height) {
731     FontMetrics fontSizer = SwingUtilities2.getFontMetrics(progressBar, g,
732                                         progressBar.getFont());
733     int stringWidth = SwingUtilities2.stringWidth(progressBar, fontSizer,
734                                                       progressString);
735
736     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
737         return new Point(x + Math.round(width/2 - stringWidth/2),
738                  y + ((height +
739                                  fontSizer.getAscent() -
740                                  fontSizer.getLeading() -
741                                  fontSizer.getDescent()) / 2));
742     } else { // VERTICAL
743
return new Point(x + ((width - fontSizer.getAscent() +
744                     fontSizer.getLeading() + fontSizer.getDescent()) / 2),
745             y + Math.round(height/2 - stringWidth/2));
746     }
747     }
748     
749     
750     public Dimension getPreferredSize(JComponent c) {
751     Dimension size;
752     Insets border = progressBar.getInsets();
753     FontMetrics fontSizer = progressBar.getFontMetrics(
754                                               progressBar.getFont());
755     
756     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
757         size = new Dimension(getPreferredInnerHorizontal());
758         // Ensure that the progress string will fit
759
if (progressBar.isStringPainted()) {
760         // I'm doing this for completeness.
761
String JavaDoc progString = progressBar.getString();
762         int stringWidth = SwingUtilities2.stringWidth(
763                           progressBar, fontSizer, progString);
764         if (stringWidth > size.width) {
765             size.width = stringWidth;
766         }
767         // This uses both Height and Descent to be sure that
768
// there is more than enough room in the progress bar
769
// for everything.
770
// This does have a strange dependency on
771
// getStringPlacememnt() in a funny way.
772
int stringHeight = fontSizer.getHeight() +
773                            fontSizer.getDescent();
774         if (stringHeight > size.height) {
775             size.height = stringHeight;
776         }
777         }
778     } else {
779         size = new Dimension(getPreferredInnerVertical());
780         // Ensure that the progress string will fit.
781
if (progressBar.isStringPainted()) {
782         String JavaDoc progString = progressBar.getString();
783         int stringHeight = fontSizer.getHeight() +
784                         fontSizer.getDescent();
785         if (stringHeight > size.width) {
786             size.width = stringHeight;
787         }
788         // This is also for completeness.
789
int stringWidth = SwingUtilities2.stringWidth(
790                                        progressBar, fontSizer, progString);
791         if (stringWidth > size.height) {
792             size.height = stringWidth;
793         }
794         }
795     }
796
797     size.width += border.left + border.right;
798     size.height += border.top + border.bottom;
799     return size;
800     }
801
802     /**
803      * The Minimum size for this component is 10. The rationale here
804      * is that there should be at least one pixel per 10 percent.
805      */

806     public Dimension getMinimumSize(JComponent c) {
807     Dimension pref = getPreferredSize(progressBar);
808     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
809         pref.width = 10;
810     } else {
811         pref.height = 10;
812     }
813     return pref;
814     }
815
816     public Dimension getMaximumSize(JComponent c) {
817     Dimension pref = getPreferredSize(progressBar);
818     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
819         pref.width = Short.MAX_VALUE;
820     } else {
821         pref.height = Short.MAX_VALUE;
822     }
823     return pref;
824     }
825
826     /**
827      * Gets the index of the current animation frame.
828      *
829      * @since 1.4
830      */

831     protected int getAnimationIndex() {
832     return animationIndex;
833     }
834
835     /**
836      * Sets the index of the current animation frame
837      * to the specified value and requests that the
838      * progress bar be repainted.
839      * Subclasses that don't use the default painting code
840      * might need to override this method
841      * to change the way that the <code>repaint</code> method
842      * is invoked.
843      *
844      * @param newValue the new animation index; no checking
845      * is performed on its value
846      * @see #incrementAnimationIndex
847      *
848      * @since 1.4
849      */

850     protected void setAnimationIndex(int newValue) {
851         if (animationIndex != newValue) {
852             if (sizeChanged()) {
853                 animationIndex = newValue;
854                 maxPosition = 0; //needs to be recalculated
855
delta = 0.0; //needs to be recalculated
856
progressBar.repaint();
857                 return;
858             }
859
860             //Get the previous box drawn.
861
nextPaintRect = getBox(nextPaintRect);
862
863             //Update the frame number.
864
animationIndex = newValue;
865                 
866             //Get the next box to draw.
867
if (nextPaintRect != null) {
868                 boxRect = getBox(boxRect);
869                 if (boxRect != null) {
870                     nextPaintRect.add(boxRect);
871                 }
872             }
873         } else { //animationIndex == newValue
874
return;
875         }
876
877         if (nextPaintRect != null) {
878             progressBar.repaint(nextPaintRect);
879         } else {
880             progressBar.repaint();
881         }
882     }
883
884     private boolean sizeChanged() {
885         if ((oldComponentInnards == null) || (componentInnards == null)) {
886         return true;
887     }
888
889         oldComponentInnards.setRect(componentInnards);
890         componentInnards = SwingUtilities.calculateInnerArea(progressBar,
891                                                          componentInnards);
892         return !oldComponentInnards.equals(componentInnards);
893     }
894
895     /**
896      * Sets the index of the current animation frame,
897      * to the next valid value,
898      * which results in the progress bar being repainted.
899      * The next valid value is, by default,
900      * the current animation index plus one.
901      * If the new value would be too large,
902      * this method sets the index to 0.
903      * Subclasses might need to override this method
904      * to ensure that the index does not go over
905      * the number of frames needed for the particular
906      * progress bar instance.
907      * This method is invoked by the default animation thread
908      * every <em>X</em> milliseconds,
909      * where <em>X</em> is specified by the "ProgressBar.repaintInterval"
910      * UI default.
911      *
912      * @see #setAnimationIndex
913      * @since 1.4
914      */

915     protected void incrementAnimationIndex() {
916         int newValue = getAnimationIndex() + 1;
917
918         if (newValue < numFrames) {
919             setAnimationIndex(newValue);
920         } else {
921             setAnimationIndex(0);
922         }
923     }
924
925     /**
926      * Returns the desired number of milliseconds between repaints.
927      * This value is meaningful
928      * only if the progress bar is in indeterminate mode.
929      * The repaint interval determines how often the
930      * default animation thread's timer is fired.
931      * It's also used by the default indeterminate progress bar
932      * painting code when determining
933      * how far to move the bouncing box per frame.
934      * The repaint interval is specified by
935      * the "ProgressBar.repaintInterval" UI default.
936      *
937      * @return the repaint interval, in milliseconds
938      */

939     private int getRepaintInterval() {
940         return repaintInterval;
941     }
942
943     private int initRepaintInterval() {
944         repaintInterval = DefaultLookup.getInt(progressBar,
945                 this, "ProgressBar.repaintInterval", 50);
946         return repaintInterval;
947     }
948
949     /**
950      * Returns the number of milliseconds per animation cycle.
951      * This value is meaningful
952      * only if the progress bar is in indeterminate mode.
953      * The cycle time is used by the default indeterminate progress bar
954      * painting code when determining
955      * how far to move the bouncing box per frame.
956      * The cycle time is specified by
957      * the "ProgressBar.cycleTime" UI default
958      * and adjusted, if necessary,
959      * by the initIndeterminateDefaults method.
960      *
961      * @return the cycle time, in milliseconds
962      */

963     private int getCycleTime() {
964         return cycleTime;
965     }
966
967     private int initCycleTime() {
968         cycleTime = DefaultLookup.getInt(progressBar, this,
969                 "ProgressBar.cycleTime", 3000);
970         return cycleTime;
971     }
972
973
974     /** Initialize cycleTime, repaintInterval, numFrames, animationIndex. */
975     private void initIndeterminateDefaults() {
976         initRepaintInterval(); //initialize repaint interval
977
initCycleTime(); //initialize cycle length
978

979         // Make sure repaintInterval is reasonable.
980
if (repaintInterval <= 0) {
981             repaintInterval = 100;
982         }
983
984         // Make sure cycleTime is reasonable.
985
if (repaintInterval > cycleTime) {
986             cycleTime = repaintInterval * 20;
987         } else {
988             // Force cycleTime to be a even multiple of repaintInterval.
989
int factor = (int)Math.ceil(
990                                  ((double)cycleTime)
991                                / ((double)repaintInterval*2));
992             cycleTime = repaintInterval*factor*2;
993         }
994     }
995
996     /**
997      * Invoked by PropertyChangeHandler.
998      *
999      * NOTE: This might not be invoked until after the first
1000     * paintIndeterminate call.
1001     */

1002    private void initIndeterminateValues() {
1003        initIndeterminateDefaults();
1004        //assert cycleTime/repaintInterval is a whole multiple of 2.
1005
numFrames = cycleTime/repaintInterval;
1006        initAnimationIndex();
1007            
1008        boxRect = new Rectangle();
1009        nextPaintRect = new Rectangle();
1010        componentInnards = new Rectangle();
1011        oldComponentInnards = new Rectangle();
1012
1013        // we only bother installing the HierarchyChangeListener if we
1014
// are indeterminate
1015
progressBar.addHierarchyListener(getHandler());
1016
1017        // start the animation thread if necessary
1018
if (progressBar.isDisplayable()) {
1019            startAnimationTimer();
1020        }
1021    }
1022
1023    /** Invoked by PropertyChangeHandler. */
1024    private void cleanUpIndeterminateValues() {
1025        // stop the animation thread if necessary
1026
if (progressBar.isDisplayable()) {
1027            stopAnimationTimer();
1028        }
1029
1030        cycleTime = repaintInterval = 0;
1031        numFrames = animationIndex = 0;
1032        maxPosition = 0;
1033        delta = 0.0;
1034
1035        boxRect = nextPaintRect = null;
1036        componentInnards = oldComponentInnards = null;
1037
1038        progressBar.removeHierarchyListener(getHandler());
1039    }
1040
1041    // Called from initIndeterminateValues to initialize the animation index.
1042
// This assumes that numFrames is set to a correct value.
1043
private void initAnimationIndex() {
1044        if ((progressBar.getOrientation() == JProgressBar.HORIZONTAL) &&
1045            (BasicGraphicsUtils.isLeftToRight(progressBar))) {
1046            // If this is a left-to-right progress bar,
1047
// start at the first frame.
1048
setAnimationIndex(0);
1049        } else {
1050            // If we go right-to-left or vertically, start at the right/bottom.
1051
setAnimationIndex(numFrames/2);
1052        }
1053    }
1054
1055    //
1056
// Animation Thread
1057
//
1058
/**
1059     * Implements an animation thread that invokes repaint
1060     * at a fixed rate. If ADJUSTTIMER is true, this thread
1061     * will continuously adjust the repaint interval to
1062     * try to make the actual time between repaints match
1063     * the requested rate.
1064     */

1065    private class Animator implements ActionListener {
1066        private Timer timer;
1067        private long previousDelay; //used to tune the repaint interval
1068
private int interval; //the fixed repaint interval
1069
private long lastCall; //the last time actionPerformed was called
1070
private int MINIMUM_DELAY = 5;
1071
1072    /**
1073     * Creates a timer if one doesn't already exist,
1074     * then starts the timer thread.
1075     */

1076        private void start(int interval) {
1077            previousDelay = interval;
1078            lastCall = 0;
1079
1080        if (timer == null) {
1081                timer = new Timer(interval, this);
1082        } else {
1083                timer.setDelay(interval);
1084        }
1085
1086        if (ADJUSTTIMER) {
1087        timer.setRepeats(false);
1088                timer.setCoalesce(false);
1089        }
1090
1091        timer.start();
1092    }
1093
1094    /**
1095     * Stops the timer thread.
1096     */

1097    private void stop() {
1098        timer.stop();
1099    }
1100
1101    /**
1102     * Reacts to the timer's action events.
1103     */

1104    public void actionPerformed(ActionEvent e) {
1105            if (ADJUSTTIMER) {
1106                long time = System.currentTimeMillis();
1107
1108                if (lastCall > 0) { //adjust nextDelay
1109
//XXX maybe should cache this after a while
1110
//actual = time - lastCall
1111
//difference = actual - interval
1112
//nextDelay = previousDelay - difference
1113
// = previousDelay - (time - lastCall - interval)
1114
int nextDelay = (int)(previousDelay
1115                                          - time + lastCall
1116                                          + getRepaintInterval());
1117                    if (nextDelay < MINIMUM_DELAY) {
1118                        nextDelay = MINIMUM_DELAY;
1119                    }
1120                    timer.setInitialDelay(nextDelay);
1121                    previousDelay = nextDelay;
1122                }
1123                timer.start();
1124                lastCall = time;
1125            }
1126
1127        incrementAnimationIndex(); //paint next frame
1128
}
1129    }
1130
1131
1132    /**
1133     * This inner class is marked &quot;public&quot; due to a compiler bug.
1134     * This class should be treated as a &quot;protected&quot; inner class.
1135     * Instantiate it only within subclasses of BasicProgressBarUI.
1136     */

1137    public class ChangeHandler implements ChangeListener JavaDoc {
1138        // NOTE: This class exists only for backward compatability. All
1139
// its functionality has been moved into Handler. If you need to add
1140
// new functionality add it to the Handler, but make sure this
1141
// class calls into the Handler.
1142
public void stateChanged(ChangeEvent JavaDoc e) {
1143            getHandler().stateChanged(e);
1144    }
1145    }
1146
1147
1148    private class Handler implements ChangeListener JavaDoc, PropertyChangeListener JavaDoc, HierarchyListener {
1149        // ChangeListener
1150
public void stateChanged(ChangeEvent JavaDoc e) {
1151        BoundedRangeModel model = progressBar.getModel();
1152        int newRange = model.getMaximum() - model.getMinimum();
1153        int newPercent;
1154        int oldPercent = getCachedPercent();
1155        
1156        if (newRange > 0) {
1157        newPercent = (int)((100 * (long)model.getValue()) / newRange);
1158        } else {
1159        newPercent = 0;
1160        }
1161        
1162        if (newPercent != oldPercent) {
1163        setCachedPercent(newPercent);
1164        progressBar.repaint();
1165        }
1166    }
1167
1168        // PropertyChangeListener
1169
public void propertyChange(PropertyChangeEvent JavaDoc e) {
1170        String JavaDoc prop = e.getPropertyName();
1171        if ("indeterminate" == prop) {
1172        if (progressBar.isIndeterminate()) {
1173                    initIndeterminateValues();
1174            } else {
1175                    //clean up
1176
cleanUpIndeterminateValues();
1177                }
1178                progressBar.repaint();
1179            }
1180        }
1181        
1182        // we don't want the animation to keep running if we're not displayable
1183
public void hierarchyChanged(HierarchyEvent he) {
1184            if ((he.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
1185                if (progressBar.isIndeterminate()) {
1186                    if (progressBar.isDisplayable()) {
1187                        startAnimationTimer();
1188                    } else {
1189                        stopAnimationTimer();
1190                    }
1191                }
1192            }
1193        }
1194    }
1195}
1196
Popular Tags