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