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