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