1. /*
  2. * @(#)ProgressMonitor.java 1.27 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 javax.swing;
  8. import java.io.*;
  9. import java.awt.BorderLayout;
  10. import java.awt.Frame;
  11. import java.awt.Component;
  12. import java.awt.Container;
  13. import java.beans.PropertyChangeEvent;
  14. import java.beans.PropertyChangeListener;
  15. import java.awt.event.WindowAdapter;
  16. import java.awt.event.WindowEvent;
  17. /** A class to monitor the progress of some operation. If it looks
  18. * like the operation will take a while, a progress dialog will be popped up.
  19. * When the ProgressMonitor is created it is given a numeric range and a
  20. * descriptive string. As the operation progresses, call the setProgress method
  21. * to indicate how far along the [min,max] range the operation is.
  22. * Initially, there is no ProgressDialog. After the first millisToDecideToPopup
  23. * milliseconds (default 500) the progress monitor will predict how long
  24. * the operation will take. If it is longer than millisToPopup (default 2000,
  25. * 2 seconds) a ProgressDialog will be popped up.
  26. * <p>
  27. * From time to time, when the Dialog box is visible, the progress bar will
  28. * be updated when setProgress is called. setProgress won't always update
  29. * the progress bar, it will only be done if the amount of progress is
  30. * visibly significant.
  31. *
  32. * <p>
  33. *
  34. * For further documentation and examples see
  35. * <a
  36. href="http://java.sun.com/docs/books/tutorial/uiswing/components/progress.html">How to Monitor Progress</a>,
  37. * a section in <em>The Java Tutorial.</em>
  38. *
  39. * @see ProgressMonitorInputStream
  40. * @author James Gosling
  41. * @version 1.27 01/23/03
  42. */
  43. public class ProgressMonitor extends Object
  44. {
  45. private ProgressMonitor root;
  46. private JDialog dialog;
  47. private JOptionPane pane;
  48. private JProgressBar myBar;
  49. private JLabel noteLabel;
  50. private Component parentComponent;
  51. private String note;
  52. private Object[] cancelOption = null;
  53. private Object message;
  54. private long T0;
  55. private int millisToDecideToPopup = 500;
  56. private int millisToPopup = 2000;
  57. private int min;
  58. private int max;
  59. private int v;
  60. private int lastDisp;
  61. private int reportDelta;
  62. /**
  63. * Constructs a graphic object that shows progress, typically by filling
  64. * in a rectangular bar as the process nears completion.
  65. *
  66. * @param parentComponent the parent component for the dialog box
  67. * @param message a descriptive message that will be shown
  68. * to the user to indicate what operation is being monitored.
  69. * This does not change as the operation progresses.
  70. * See the message parameters to methods in
  71. * {@link JOptionPane#message}
  72. * for the range of values.
  73. * @param note a short note describing the state of the
  74. * operation. As the operation progresses, you can call
  75. * setNote to change the note displayed. This is used,
  76. * for example, in operations that iterate through a
  77. * list of files to show the name of the file being processes.
  78. * If note is initially null, there will be no note line
  79. * in the dialog box and setNote will be ineffective
  80. * @param min the lower bound of the range
  81. * @param max the upper bound of the range
  82. * @see JDialog
  83. * @see JOptionPane
  84. */
  85. public ProgressMonitor(Component parentComponent,
  86. Object message,
  87. String note,
  88. int min,
  89. int max) {
  90. this(parentComponent, message, note, min, max, null);
  91. }
  92. private ProgressMonitor(Component parentComponent,
  93. Object message,
  94. String note,
  95. int min,
  96. int max,
  97. ProgressMonitor group) {
  98. this.min = min;
  99. this.max = max;
  100. this.parentComponent = parentComponent;
  101. cancelOption = new Object[1];
  102. cancelOption[0] = UIManager.getString("OptionPane.cancelButtonText");
  103. reportDelta = (max - min) / 100;
  104. if (reportDelta < 1) reportDelta = 1;
  105. v = min;
  106. this.message = message;
  107. this.note = note;
  108. if (group != null) {
  109. root = (group.root != null) ? group.root : group;
  110. T0 = root.T0;
  111. dialog = root.dialog;
  112. }
  113. else {
  114. T0 = System.currentTimeMillis();
  115. }
  116. }
  117. private class ProgressOptionPane extends JOptionPane
  118. {
  119. ProgressOptionPane(Object messageList) {
  120. super(messageList,
  121. JOptionPane.INFORMATION_MESSAGE,
  122. JOptionPane.DEFAULT_OPTION,
  123. null,
  124. ProgressMonitor.this.cancelOption,
  125. null);
  126. }
  127. public int getMaxCharactersPerLineCount() {
  128. return 60;
  129. }
  130. // Equivalent to JOptionPane.createDialog,
  131. // but create a modeless dialog.
  132. // This is necessary because the Solaris implementation doesn't
  133. // support Dialog.setModal yet.
  134. public JDialog createDialog(Component parentComponent, String title) {
  135. Frame frame = JOptionPane.getFrameForComponent(parentComponent);
  136. final JDialog dialog = new JDialog(frame, title, false);
  137. Container contentPane = dialog.getContentPane();
  138. contentPane.setLayout(new BorderLayout());
  139. contentPane.add(this, BorderLayout.CENTER);
  140. dialog.pack();
  141. dialog.setLocationRelativeTo(parentComponent);
  142. dialog.addWindowListener(new WindowAdapter() {
  143. boolean gotFocus = false;
  144. public void windowClosing(WindowEvent we) {
  145. setValue(cancelOption[0]);
  146. }
  147. public void windowActivated(WindowEvent we) {
  148. // Once window gets focus, set initial focus
  149. if (!gotFocus) {
  150. selectInitialValue();
  151. gotFocus = true;
  152. }
  153. }
  154. });
  155. addPropertyChangeListener(new PropertyChangeListener() {
  156. public void propertyChange(PropertyChangeEvent event) {
  157. if(dialog.isVisible() &&
  158. event.getSource() == ProgressOptionPane.this &&
  159. (event.getPropertyName().equals(VALUE_PROPERTY) ||
  160. event.getPropertyName().equals(INPUT_VALUE_PROPERTY))){
  161. dialog.setVisible(false);
  162. dialog.dispose();
  163. }
  164. }
  165. });
  166. return dialog;
  167. }
  168. }
  169. /**
  170. * Indicate the progress of the operation being monitored.
  171. * If the specified value is >= the maximum, the progress
  172. * monitor is closed.
  173. * @param nv an int specifying the current value, between the
  174. * maximum and minimum specified for this component
  175. * @see #setMinimum
  176. * @see #setMaximum
  177. * @see #close
  178. */
  179. public void setProgress(int nv) {
  180. v = nv;
  181. if (nv >= max) {
  182. close();
  183. }
  184. else if (nv >= lastDisp + reportDelta) {
  185. lastDisp = nv;
  186. if (myBar != null) {
  187. myBar.setValue(nv);
  188. }
  189. else {
  190. long T = System.currentTimeMillis();
  191. long dT = (int)(T-T0);
  192. if (dT >= millisToDecideToPopup) {
  193. int predictedCompletionTime;
  194. if (nv > min) {
  195. predictedCompletionTime = (int)((long)dT *
  196. (max - min) /
  197. (nv - min));
  198. }
  199. else {
  200. predictedCompletionTime = millisToPopup;
  201. }
  202. if (predictedCompletionTime >= millisToPopup) {
  203. myBar = new JProgressBar();
  204. myBar.setMinimum(min);
  205. myBar.setMaximum(max);
  206. myBar.setValue(nv);
  207. if (note != null) noteLabel = new JLabel(note);
  208. pane = new ProgressOptionPane(new Object[] {message,
  209. noteLabel,
  210. myBar});
  211. dialog = pane.createDialog(parentComponent,
  212. UIManager.getString(
  213. "ProgressMonitor.progressText"));
  214. dialog.show();
  215. }
  216. }
  217. }
  218. }
  219. }
  220. /**
  221. * Indicate that the operation is complete. This happens automatically
  222. * when the value set by setProgress is >= max, but it may be called
  223. * earlier if the operation ends early.
  224. */
  225. public void close() {
  226. if (dialog != null) {
  227. dialog.setVisible(false);
  228. dialog.dispose();
  229. dialog = null;
  230. pane = null;
  231. myBar = null;
  232. }
  233. }
  234. /**
  235. * Returns the minimum value -- the lower end of the progress value.
  236. *
  237. * @return an int representing the minimum value
  238. * @see #setMinimum
  239. */
  240. public int getMinimum() {
  241. return min;
  242. }
  243. /**
  244. * Specifies the minimum value.
  245. *
  246. * @param m an int specifying the minimum value
  247. * @see #getMinimum
  248. */
  249. public void setMinimum(int m) {
  250. min = m;
  251. }
  252. /**
  253. * Returns the maximum value -- the higher end of the progress value.
  254. *
  255. * @return an int representing the maximum value
  256. * @see #setMaximum
  257. */
  258. public int getMaximum() {
  259. return max;
  260. }
  261. /**
  262. * Specifies the maximum value.
  263. *
  264. * @param m an int specifying the maximum value
  265. * @see #getMaximum
  266. */
  267. public void setMaximum(int m) {
  268. max = m;
  269. }
  270. /**
  271. * Returns true if the user hits the Cancel button in the progress dialog.
  272. */
  273. public boolean isCanceled() {
  274. if (pane == null) return false;
  275. Object v = pane.getValue();
  276. return ((v != null) &&
  277. (cancelOption.length == 1) &&
  278. (v.equals(cancelOption[0])));
  279. }
  280. /**
  281. * Specifies the amount of time to wait before deciding whether or
  282. * not to popup a progress monitor.
  283. *
  284. * @param millisToDecideToPopup an int specifying the time to wait,
  285. * in milliseconds
  286. * @see #getMillisToDecideToPopup
  287. */
  288. public void setMillisToDecideToPopup(int millisToDecideToPopup) {
  289. this.millisToDecideToPopup = millisToDecideToPopup;
  290. }
  291. /**
  292. * Returns the amount of time this object waits before deciding whether
  293. * or not to popup a progress monitor.
  294. *
  295. * @see #setMillisToDecideToPopup
  296. */
  297. public int getMillisToDecideToPopup() {
  298. return millisToDecideToPopup;
  299. }
  300. /**
  301. * Specifies the amount of time it will take for the popup to appear.
  302. * (If the predicted time remaining is less than this time, the popup
  303. * won't be displayed.)
  304. *
  305. * @param millisToPopup an int specifying the time in milliseconds
  306. * @see #getMillisToPopup
  307. */
  308. public void setMillisToPopup(int millisToPopup) {
  309. this.millisToPopup = millisToPopup;
  310. }
  311. /**
  312. * Returns the amount of time it will take for the popup to appear.
  313. *
  314. * @see #setMillisToPopup
  315. */
  316. public int getMillisToPopup() {
  317. return millisToPopup;
  318. }
  319. /**
  320. * Specifies the additional note that is displayed along with the
  321. * progress message. Used, for example, to show which file the
  322. * is currently being copied during a multiple-file copy.
  323. *
  324. * @param note a String specifying the note to display
  325. * @see #getNote
  326. */
  327. public void setNote(String note) {
  328. this.note = note;
  329. if (noteLabel != null) {
  330. noteLabel.setText(note);
  331. }
  332. }
  333. /**
  334. * Specifies the additional note that is displayed along with the
  335. * progress message.
  336. *
  337. * @return a String specifying the note to display
  338. * @see #setNote
  339. */
  340. public String getNote() {
  341. return note;
  342. }
  343. }