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