History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/plaf/basic/BasicProgressBarUI.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicProgressBarUI.java+1275−0
@@ -0,0 +1,1275 @@
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
26package javax.swing.plaf.basic;
27
28import sun.swing.SwingUtilities2;
29import java.awt.*;
30import java.awt.geom.AffineTransform;
31import java.awt.event.*;
32import javax.swing.*;
33import javax.swing.event.*;
34import javax.swing.plaf.*;
35import java.beans.PropertyChangeListener;
36import java.beans.PropertyChangeEvent;
37import java.io.Serializable;
38import sun.swing.DefaultLookup;
39
40/**
41 * A Basic L&F implementation of ProgressBarUI.
42 *
43 * @author Michael C. Albers
44 * @author Kathy Walrath
45 */
46public 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}