Frames | No Frames |
1: /* ProgressMonitor.java -- 2: Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.swing; 39: 40: import java.awt.Component; 41: import java.awt.event.ActionEvent; 42: import java.awt.event.ActionListener; 43: 44: import javax.accessibility.AccessibleContext; 45: 46: /** 47: * <p>Using this class you can easily monitor tasks where you cannot 48: * estimate the duration exactly.</p> 49: * 50: * <p>A ProgressMonitor instance waits until the first time setProgress 51: * is called. When <code>millisToDecideToPopup</code> time elapsed the 52: * instance estimates the duration until the whole operation is completed. 53: * If this duration exceeds <code>millisToPopup</code> a non-modal dialog 54: * with a message and a progress bar is shown.</p> 55: * 56: * <p>The value of <code>millisToDecideToPopup</code> defaults to 57: * <code>500</code> and <code>millisToPopup</code> to 58: * <code>2000</code>.</p> 59: * 60: * @author Andrew Selkirk 61: * @author Robert Schuster (robertschuster@fsfe.org) 62: * @since 1.2 63: * @status updated to 1.2 64: */ 65: public class ProgressMonitor 66: { 67: 68: /** 69: * The accessible content for this component 70: */ 71: protected AccessibleContext accessibleContext; 72: 73: /** 74: * parentComponent 75: */ 76: Component component; 77: 78: /** 79: * note 80: */ 81: String note; 82: 83: /** 84: * message 85: */ 86: Object message; 87: 88: /** 89: * millisToDecideToPopup 90: */ 91: int millisToDecideToPopup = 500; 92: 93: /** 94: * millisToPopup 95: */ 96: int millisToPopup = 2000; 97: 98: int min, max, progress; 99: 100: JProgressBar progressBar; 101: 102: JLabel noteLabel; 103: 104: JDialog progressDialog; 105: 106: Timer timer; 107: 108: boolean canceled; 109: 110: /** 111: * Creates a new <code>ProgressMonitor</code> instance. This is used to 112: * monitor a task and pops up a dialog if the task is taking a long time to 113: * run. 114: * 115: * @param component The parent component of the progress dialog or 116: * <code>null</code>. 117: * @param message A constant message object which works in the way it does 118: * in {@link JOptionPane}. 119: * @param note A string message which can be changed while the operation goes 120: * on. 121: * @param minimum The minimum value for the operation (start value). 122: * @param maximum The maximum value for the operation (end value). 123: */ 124: public ProgressMonitor(Component component, Object message, 125: String note, int minimum, int maximum) 126: { 127: 128: // Set data. 129: this.component = component; 130: this.message = message; 131: this.note = note; 132: 133: min = minimum; 134: max = maximum; 135: } 136: 137: /** 138: * <p>Hides the dialog and stops any measurements.</p> 139: * 140: * <p>Has no effect when <code>setProgress</code> is not at least 141: * called once.</p> 142: */ 143: public void close() 144: { 145: if (progressDialog != null) 146: { 147: progressDialog.setVisible(false); 148: } 149: 150: if (timer != null) 151: { 152: timer.stop(); 153: timer = null; 154: } 155: } 156: 157: /** 158: * <p>Updates the progress value.</p> 159: * 160: * <p>When called for the first time this initializes a timer 161: * which decides after <code>millisToDecideToPopup</code> time 162: * whether to show a progress dialog or not.</p> 163: * 164: * <p>If the progress value equals or exceeds the maximum 165: * value the progress dialog is closed automatically.</p> 166: * 167: * @param progress New progress value. 168: */ 169: public void setProgress(int progress) 170: { 171: this.progress = progress; 172: 173: // Initializes and starts a timer with a task 174: // which measures the duration and displays 175: // a progress dialog if neccessary. 176: if (timer == null && progressDialog == null) 177: { 178: timer = new Timer(25, null); 179: timer.addActionListener(new TimerListener()); 180: timer.start(); 181: } 182: 183: // Cancels timer and hides progress dialog if the 184: // maximum value is reached. 185: if (progressBar != null && this.progress >= progressBar.getMaximum()) 186: { 187: // The reason for using progressBar.getMaximum() instead of max is that 188: // we want to prevent that changes to the value have any effect after the 189: // progress dialog is visible (This is how the JDK behaves.). 190: close(); 191: } 192: 193: } 194: 195: /** 196: * Returns the minimum or start value of the operation. 197: * 198: * @return Minimum or start value of the operation. 199: */ 200: public int getMinimum() 201: { 202: return min; 203: } 204: 205: /** 206: * <p>Use this method to set the minimum or start value of 207: * your operation.</p> 208: * 209: * <p>For typical application like copy operation this will be 210: * zero.</p> 211: * 212: * <p>Keep in mind that changing this value after the progress 213: * dialog is made visible has no effect upon the progress bar.</p> 214: * 215: * @param minimum The new minimum value. 216: */ 217: public void setMinimum(int minimum) 218: { 219: min = minimum; 220: } 221: 222: /** 223: * Return the maximum or end value of your operation. 224: * 225: * @return Maximum or end value. 226: */ 227: public int getMaximum() 228: { 229: return max; 230: } 231: 232: /** 233: * <p>Sets the maximum or end value of the operation to the 234: * given integer.</p> 235: * 236: * @param maximum 237: */ 238: public void setMaximum(int maximum) 239: { 240: max = maximum; 241: } 242: 243: /** 244: * Returns whether the user canceled the operation. 245: * 246: * @return Whether the operation was canceled. 247: */ 248: public boolean isCanceled() 249: { 250: // The value is predefined to false 251: // and changes only when the user clicks 252: // the cancel button in the progress dialog. 253: return canceled; 254: } 255: 256: /** 257: * Returns the amount of milliseconds to wait 258: * until the ProgressMonitor should decide whether 259: * a progress dialog is to be shown or not. 260: * 261: * @return The duration in milliseconds. 262: */ 263: public int getMillisToDecideToPopup() 264: { 265: return millisToDecideToPopup; 266: } 267: 268: /** 269: * Sets the amount of milliseconds to wait until the 270: * ProgressMonitor should decide whether a progress dialog 271: * is to be shown or not. 272: * 273: * <p>This method has no effect when the progress dialog 274: * is already visible.</p> 275: * 276: * @param time The duration in milliseconds. 277: */ 278: public void setMillisToDecideToPopup(int time) 279: { 280: millisToDecideToPopup = time; 281: } 282: 283: /** 284: * Returns the number of milliseconds to wait before displaying the progress 285: * dialog. The default value is 2000. 286: * 287: * @return The number of milliseconds. 288: * 289: * @see #setMillisToPopup(int) 290: */ 291: public int getMillisToPopup() 292: { 293: return millisToPopup; 294: } 295: 296: /** 297: * Sets the number of milliseconds to wait before displaying the progress 298: * dialog. 299: * 300: * @param time the number of milliseconds. 301: * 302: * @see #getMillisToPopup() 303: */ 304: public void setMillisToPopup(int time) 305: { 306: millisToPopup = time; 307: } 308: 309: /** 310: * Returns a message which is shown in the progress dialog. 311: * 312: * @return The changeable message visible in the progress dialog. 313: */ 314: public String getNote() 315: { 316: return note; 317: } 318: 319: /** 320: * <p>Set the message shown in the progess dialog.</p> 321: * 322: * <p>Changing the note while the progress dialog is visible 323: * is possible.</p> 324: * 325: * @param note A message shown in the progress dialog. 326: */ 327: public void setNote(String note) 328: { 329: if (noteLabel != null) 330: { 331: noteLabel.setText(note); 332: } 333: else 334: { 335: this.note = note; 336: } 337: } 338: 339: /** 340: * Internal method that creates the progress dialog. 341: */ 342: void createDialog() 343: { 344: // If there is no note we suppress the generation of the 345: // label. 346: Object[] tmp = (note == null) ? 347: new Object[] 348: { 349: message, 350: progressBar = new JProgressBar(min, max) 351: } 352: : 353: new Object[] 354: { 355: message, 356: noteLabel = new JLabel(note), 357: progressBar = new JProgressBar(min, max) 358: }; 359: 360: JOptionPane pane = new JOptionPane(tmp, JOptionPane.INFORMATION_MESSAGE); 361: 362: // FIXME: Internationalize the button 363: JButton cancelButton = new JButton("Cancel"); 364: cancelButton.addActionListener(new ActionListener() 365: { 366: public void actionPerformed(ActionEvent ae) 367: { 368: canceled = true; 369: } 370: }); 371: 372: pane.setOptions(new Object[] { cancelButton }); 373: 374: // FIXME: Internationalize the title 375: progressDialog = pane.createDialog(component, "Progress ..."); 376: progressDialog.setModal(false); 377: progressDialog.setResizable(true); 378: 379: progressDialog.pack(); 380: progressDialog.setVisible(true); 381: 382: } 383: 384: /** An ActionListener implementation which does the measurements 385: * and estimations of the ProgressMonitor. 386: */ 387: class TimerListener implements ActionListener 388: { 389: long timestamp; 390: 391: int lastProgress; 392: 393: boolean first = true; 394: 395: TimerListener() 396: { 397: timestamp = System.currentTimeMillis(); 398: } 399: 400: public void actionPerformed(ActionEvent ae) 401: { 402: long now = System.currentTimeMillis(); 403: 404: if (first) 405: { 406: if ((now - timestamp) > millisToDecideToPopup) 407: { 408: first = false; 409: 410: 411: long expected = (progress - min == 0) ? 412: (now - timestamp) * (max - min) : 413: (now - timestamp) * (max - min) / (progress - min); 414: 415: if (expected > millisToPopup) 416: { 417: createDialog(); 418: } 419: } 420: else 421: { 422: // We have not waited long enough to make a decision, 423: // so return and try again when the timer is invoked. 424: return; 425: } 426: } 427: else if (progressDialog != null) 428: { 429: // The progress dialog is being displayed. We now calculate 430: // whether setting the progress bar to the current progress 431: // value would result in a visual difference. 432: int delta = progress - progressBar.getValue(); 433: 434: if ((delta * progressBar.getWidth() / (max - min)) > 0) 435: { 436: // At least one pixel would change. 437: progressBar.setValue(progress); 438: } 439: } 440: else 441: { 442: // No dialog necessary 443: timer.stop(); 444: timer = null; 445: } 446: 447: timestamp = now; 448: } 449: } 450: 451: /** 452: * Gets the accessible context. 453: * 454: * @return the accessible context. 455: */ 456: public AccessibleContext getAccessibleContext() 457: { 458: return accessibleContext; 459: } 460: }