1. /*
  2. * @(#)BasicProgressBarUI.java 1.42 01/11/29
  3. *
  4. * Copyright 2002 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 java.awt.*;
  9. import javax.swing.*;
  10. import javax.swing.event.*;
  11. import javax.swing.plaf.*;
  12. import java.io.Serializable;
  13. /**
  14. * A Basic L&F implementation of ProgressBarUI.
  15. *
  16. * @version 1.42 11/29/01
  17. * @author Michael C. Albers
  18. */
  19. public class BasicProgressBarUI extends ProgressBarUI {
  20. private static final Dimension PREFERRED_INNER_HORIZONTAL = new Dimension(146, 12);
  21. private static final Dimension PREFERRED_INNER_VERTICAL = new Dimension(12, 146);
  22. private int cachedPercent;
  23. private int cellLength, cellSpacing;
  24. // The "selectionForeground" is the color of the text when it is drawn
  25. // over a filled area of the progress bar. The "selectionBackground"
  26. // is for the text over the unfilled progress bar area.
  27. private Color selectionForeground, selectionBackground;
  28. protected JProgressBar progressBar;
  29. protected ChangeListener changeListener;
  30. public static ComponentUI createUI(JComponent x) {
  31. return new BasicProgressBarUI();
  32. }
  33. public void installUI(JComponent c) {
  34. progressBar = (JProgressBar)c;
  35. installDefaults();
  36. installListeners();
  37. }
  38. public void uninstallUI(JComponent c) {
  39. uninstallDefaults();
  40. uninstallListeners();
  41. progressBar = null;
  42. }
  43. protected void installDefaults() {
  44. progressBar.setOpaque(true);
  45. LookAndFeel.installBorder(progressBar,"ProgressBar.border");
  46. LookAndFeel.installColorsAndFont(progressBar,
  47. "ProgressBar.background",
  48. "ProgressBar.foreground",
  49. "ProgressBar.font");
  50. cellLength = UIManager.getInt("ProgressBar.cellLength");
  51. cellSpacing = UIManager.getInt("ProgressBar.cellSpacing");
  52. selectionForeground = UIManager.getColor("ProgressBar.selectionForeground");
  53. selectionBackground = UIManager.getColor("ProgressBar.selectionBackground");
  54. }
  55. protected void uninstallDefaults() {
  56. LookAndFeel.uninstallBorder(progressBar);
  57. }
  58. protected void installListeners() {
  59. changeListener = new ChangeHandler();
  60. progressBar.addChangeListener(changeListener);
  61. }
  62. protected void uninstallListeners() {
  63. progressBar.removeChangeListener(changeListener);
  64. }
  65. // Many of the Basic*UI components have the following methods.
  66. // This component does not have these methods because *ProgressBarUI
  67. // is not a compound component and does not accept input.
  68. //
  69. // protected void installComponents()
  70. // protected void uninstallComponents()
  71. // protected void installKeyboardActions()
  72. // protected void uninstallKeyboardActions()
  73. protected Dimension getPreferredInnerHorizontal() {
  74. return PREFERRED_INNER_HORIZONTAL;
  75. }
  76. protected Dimension getPreferredInnerVertical() {
  77. return PREFERRED_INNER_VERTICAL;
  78. }
  79. /**
  80. * The "selectionForeground" is the color of the text when it is drawn
  81. * over a filled area of the progress bar.
  82. */
  83. protected Color getSelectionForeground() {
  84. return selectionForeground;
  85. }
  86. /**
  87. * The "selectionBackground" is the color of the text when it is drawn
  88. * over an unfilled area of the progress bar.
  89. */
  90. protected Color getSelectionBackground() {
  91. return selectionBackground;
  92. }
  93. private int getCachedPercent() {
  94. return cachedPercent;
  95. }
  96. private void setCachedPercent(int cachedPercent) {
  97. this.cachedPercent = cachedPercent;
  98. }
  99. /**
  100. * Returns the width (if HORIZONTAL) or height (if VERTICAL)
  101. * of each of the indivdual cells/units to be rendered in the
  102. * progress bar. However, for text rendering simplification and
  103. * aesthetic considerations, this function will return 1 when
  104. * the progress string is being rendered.
  105. *
  106. * @return the value representing the spacing between cells
  107. * @see #setCellLength
  108. * @see JProgressBar.isStringPainted
  109. */
  110. protected int getCellLength() {
  111. if (progressBar.isStringPainted()) {
  112. return 1;
  113. } else {
  114. return cellLength;
  115. }
  116. }
  117. protected void setCellLength(int cellLen) {
  118. this.cellLength = cellLen;
  119. }
  120. /**
  121. * Returns the spacing between each of the cells/units in the
  122. * progress bar. However, for text rendering simplification and
  123. * aesthetic considerations, this function will return 0 when
  124. * the progress string is being rendered.
  125. *
  126. * @return the value representing the spacing between cells
  127. * @see #setCellSpacing
  128. * @see JProgressBar.isStringPainted
  129. */
  130. protected int getCellSpacing() {
  131. if (progressBar.isStringPainted()) {
  132. return 0;
  133. } else {
  134. return cellSpacing;
  135. }
  136. }
  137. protected void setCellSpacing(int cellSpace) {
  138. this.cellSpacing = cellSpace;
  139. }
  140. /**
  141. * This determines the amount of the progress bar that should be filled
  142. * based on the percent done gathered from the model. This is a common
  143. * operation so it was abstracted out. It assumes that your progress bar
  144. * is linear. That is, if you are making a circular progress indicator,
  145. * you will want to override this method.
  146. */
  147. protected int getAmountFull(Insets b, int width, int height) {
  148. int amountFull = 0;
  149. BoundedRangeModel model = progressBar.getModel();
  150. if ( (model.getMaximum() - model.getMinimum()) != 0) {
  151. if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
  152. amountFull = (int)Math.round(width *
  153. progressBar.getPercentComplete());
  154. } else {
  155. amountFull = (int)Math.round(height *
  156. progressBar.getPercentComplete());
  157. }
  158. }
  159. return amountFull;
  160. }
  161. /**
  162. * All purpose paint method that should do the right thing for almost
  163. * all linear progress bars. By setting a few values in the defaults
  164. * table, things should work just fine to paint your progress bar.
  165. * Naturally, override this if you are making a circular or
  166. * semi-circular progress bar.
  167. */
  168. public void paint(Graphics g, JComponent c) {
  169. BoundedRangeModel model = progressBar.getModel();
  170. int barRectX = 0;
  171. int barRectY = 0;
  172. int barRectWidth = progressBar.getWidth();
  173. int barRectHeight = progressBar.getHeight();
  174. Insets b = progressBar.getInsets(); // area for border
  175. barRectX += b.left;
  176. barRectY += b.top;
  177. barRectWidth -= (b.right + barRectX);
  178. barRectHeight -= (b.bottom + barRectY);
  179. int current;
  180. // a cell and its spacing
  181. int increment = getCellLength() + getCellSpacing();
  182. // amount of progress to draw
  183. int amountFull = getAmountFull(b, barRectWidth, barRectHeight);
  184. g.setColor(progressBar.getForeground());
  185. if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
  186. // draw the cells
  187. if (getCellSpacing() == 0 && amountFull > 0) {
  188. // draw one big Rect because there is no space between cells
  189. g.fillRect(barRectX, barRectY,
  190. barRectX-b.left+amountFull, barRectHeight);
  191. } else { // draw each individual cells
  192. // the largest number to draw a cell at
  193. int max = barRectX + amountFull;
  194. for (current = barRectX;
  195. current < max;
  196. current += increment) {
  197. g.fillRect(current, barRectY,
  198. getCellLength(), barRectHeight);
  199. }
  200. }
  201. } else { // VERTICAL
  202. // draw the cells
  203. if (getCellSpacing() == 0 && amountFull > 0) {
  204. // draw one big Rect because there is no space between cells
  205. g.fillRect(barRectX, barRectHeight-amountFull+b.top,
  206. barRectWidth, amountFull);
  207. } else { // draw each individual cells
  208. // the smallest number to draw a cell at
  209. // that is, the number at the top
  210. int min = barRectHeight - amountFull;
  211. for (current = barRectHeight-getCellLength()+getCellSpacing();
  212. current >= min;
  213. current -= increment)
  214. {
  215. g.fillRect(barRectX, current,
  216. barRectWidth, getCellLength());
  217. }
  218. }
  219. }
  220. // Deal with possible text painting
  221. if (progressBar.isStringPainted()) {
  222. paintString(g, barRectX, barRectY,
  223. barRectWidth, barRectHeight,
  224. amountFull, b);
  225. }
  226. }
  227. protected void paintString(Graphics g, int x, int y,
  228. int width, int height,
  229. int amountFull, Insets b) {
  230. String progressString = progressBar.getString();
  231. g.setFont(progressBar.getFont());
  232. Point renderLocation = getStringPlacement(g, progressString,
  233. x, y, width, height);
  234. Rectangle oldClip = g.getClipBounds();
  235. if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
  236. g.setColor(getSelectionForeground());
  237. g.drawString(progressString, renderLocation.x, renderLocation.y);
  238. g.setColor(getSelectionBackground());
  239. g.setClip(amountFull+b.left, y, width, height);
  240. g.drawString(progressString, renderLocation.x, renderLocation.y);
  241. } else { // VERTICAL
  242. // do this from the top of the bar to the bottom
  243. g.setColor(getSelectionBackground());
  244. g.drawString(progressString, renderLocation.x, renderLocation.y);
  245. g.setColor(getSelectionForeground());
  246. g.setClip(x, height-amountFull+b.top, width, height);
  247. g.drawString(progressString, renderLocation.x, renderLocation.y);
  248. }
  249. g.setClip(oldClip.x, oldClip.y, oldClip.width, oldClip.height);
  250. }
  251. /**
  252. * Designate the place where the progress string will be drawn.
  253. * This implementation places it at the center of the progress
  254. * bar (in both x and y). Override this if you want to right,
  255. * left, top, or bottom align the progress string or if you need
  256. * to nudge it around for any reason.
  257. */
  258. protected Point getStringPlacement(Graphics g, String progressString,
  259. int x,int y,int width,int height) {
  260. FontMetrics fontSizer = progressBar.getFontMetrics(
  261. progressBar.getFont());
  262. int stringWidth = fontSizer.stringWidth(progressString);
  263. int stringHeight = fontSizer.getAscent() + fontSizer.getDescent();
  264. if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
  265. return new Point(x + Math.round(width2 - stringWidth2),
  266. y + fontSizer.getHeight() -
  267. Math.round(fontSizer.getDescent()/2) );
  268. } else { // VERTICAL
  269. return new Point(x + Math.round(width2 - stringWidth2),
  270. y + Math.round(height2 + stringHeight2) );
  271. }
  272. }
  273. public Dimension getPreferredSize(JComponent c) {
  274. Dimension size;
  275. Insets border = progressBar.getInsets();
  276. FontMetrics fontSizer = progressBar.getFontMetrics(
  277. progressBar.getFont());
  278. if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
  279. size = new Dimension(getPreferredInnerHorizontal());
  280. // Ensure that the progress string will fit
  281. if (progressBar.isStringPainted()) {
  282. // I'm doing this for completeness.
  283. String progString = progressBar.getString();
  284. int stringWidth = fontSizer.stringWidth(progString);
  285. if (stringWidth > size.width) {
  286. size.width = stringWidth;
  287. }
  288. // This uses both Height and Descent to be sure that
  289. // there is more than enough room in the progress bar
  290. // for everything.
  291. // This does have a strange dependency on
  292. // getStringPlacememnt() in a funny way.
  293. int stringHeight = fontSizer.getHeight() +
  294. fontSizer.getDescent();;
  295. if (stringHeight > size.height) {
  296. size.height = stringHeight;
  297. }
  298. }
  299. } else {
  300. size = new Dimension(getPreferredInnerVertical());
  301. // Ensure that the progress string will fit.
  302. // I can't, with any honesty, suggest that you ask for
  303. // the progress string on a VERTICAL ProgressBar. If you
  304. // do, you get what you deserve.
  305. if (progressBar.isStringPainted()) {
  306. // This is an attempt to ensure that there will be enough
  307. // room for the "standard" XXX% string that is provided by
  308. // progress bar. Yes, it's inefficient.
  309. String progString = new String("100%");
  310. int stringWidth = fontSizer.stringWidth(progString);
  311. if (stringWidth > size.width) {
  312. size.width = stringWidth;
  313. }
  314. progString = progressBar.getString();
  315. stringWidth = fontSizer.stringWidth(progString);
  316. if (stringWidth > size.width) {
  317. size.width = stringWidth;
  318. }
  319. // This is also for completeness.
  320. int stringHeight = fontSizer.getHeight() +
  321. fontSizer.getDescent();;
  322. if (stringHeight > size.height) {
  323. size.height = stringHeight;
  324. }
  325. }
  326. }
  327. size.width += border.left + border.right;
  328. size.height += border.top + border.bottom;
  329. return size;
  330. }
  331. /**
  332. * The Minimum size for this component is 10. The rationale here
  333. * is that there should be at least one pixel per 10 percent.
  334. */
  335. public Dimension getMinimumSize(JComponent c) {
  336. Dimension pref = getPreferredSize(progressBar);
  337. if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
  338. pref.width = 10;
  339. } else {
  340. pref.height = 10;
  341. }
  342. return pref;
  343. }
  344. public Dimension getMaximumSize(JComponent c) {
  345. Dimension pref = getPreferredSize(progressBar);
  346. if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
  347. pref.width = Short.MAX_VALUE;
  348. } else {
  349. pref.height = Short.MAX_VALUE;
  350. }
  351. return pref;
  352. }
  353. //
  354. // Change Events
  355. //
  356. /**
  357. * This inner class is marked "public" due to a compiler bug.
  358. * This class should be treated as a "protected" inner class.
  359. * Instantiate it only within subclasses of BasicProgressBarUI.
  360. */
  361. public class ChangeHandler implements ChangeListener {
  362. public void stateChanged(ChangeEvent e) {
  363. BoundedRangeModel model = progressBar.getModel();
  364. int newRange = model.getMaximum() - model.getMinimum();
  365. int newPercent;
  366. int oldPercent = getCachedPercent();
  367. if (newRange > 0) {
  368. newPercent = (100 * model.getValue()) / newRange;
  369. } else {
  370. newPercent = 0;
  371. }
  372. if (newPercent != oldPercent) {
  373. setCachedPercent(newPercent);
  374. progressBar.repaint();
  375. }
  376. }
  377. }
  378. }