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