Source for javax.swing.ProgressMonitor

   1: /* ProgressMonitor.java --
   2:    Copyright (C) 2002, 2004, 2005 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.ActionListener;
  42: import java.awt.event.ActionEvent;
  43: 
  44: /**
  45:  * <p>Using this class you can easily monitor tasks where you cannot
  46:  * estimate the duration exactly.</p>
  47:  *
  48:  * <p>A ProgressMonitor instance waits until the first time setProgress
  49:  * is called. When <code>millisToDecideToPopup</code> time elapsed the
  50:  * instance estimates the duration until the whole operation is completed.
  51:  * If this duration exceeds <code>millisToPopup</code> a non-modal dialog
  52:  * with a message and a progress bar is shown.</p>
  53:  *
  54:  * <p>The value of <code>millisToDecideToPopup</code> defaults to
  55:  * <code>500</code> and <code>millisToPopup</code> to
  56:  * <code>2000</code>.</p>
  57:  *
  58:  * @author Andrew Selkirk
  59:  * @author Robert Schuster (robertschuster@fsfe.org)
  60:  * @since 1.2
  61:  * @status updated to 1.2
  62:  */
  63: public class ProgressMonitor
  64: {
  65:   /**
  66:    * parentComponent
  67:    */
  68:   Component component;
  69: 
  70:   /**
  71:    * note
  72:    */
  73:   String note;
  74: 
  75:   /**
  76:    * message
  77:    */
  78:   Object message;
  79: 
  80:   /**
  81:    * millisToDecideToPopup
  82:    */
  83:   int millisToDecideToPopup = 500;
  84: 
  85:   /**
  86:    * millisToPopup
  87:    */
  88:   int millisToPopup = 2000;
  89: 
  90:   int min, max, progress;
  91: 
  92:   JProgressBar progressBar;
  93: 
  94:   JLabel noteLabel;
  95: 
  96:   JDialog progressDialog;
  97: 
  98:   Timer timer;
  99: 
 100:   boolean canceled;
 101: 
 102:   /**
 103:    * Constructor ProgressMonitor
 104:    * @param component The parent component of the progress dialog or <code>null</code>.
 105:    * @param message A constant message object which works in the way it does in <code>JOptionPane</code>.
 106:    * @param note A string message which can be changed while the operation goes on.
 107:    * @param minimum The minimum value for the operation (start value).
 108:    * @param maximum The maximum value for the operation (end value).
 109:    */
 110:   public ProgressMonitor(Component component, Object message,
 111:                          String note, int minimum, int maximum)
 112:   {
 113: 
 114:     // Set data.
 115:     this.component = component;
 116:     this.message = message;
 117:     this.note = note;
 118: 
 119:     min = minimum;
 120:     max = maximum;
 121:   }
 122: 
 123:   /**
 124:    * <p>Hides the dialog and stops any measurements.</p>
 125:    *
 126:    * <p>Has no effect when <code>setProgress</code> is not at least
 127:    * called once.</p>
 128:    */
 129:   public void close()
 130:   {
 131:     if ( progressDialog != null )
 132:       {
 133:         progressDialog.setVisible(false);
 134:       }
 135: 
 136:     if ( timer != null )
 137:       {
 138:         timer.stop();
 139:         timer = null;
 140:       }
 141:   }
 142: 
 143:   /**
 144:    * <p>Updates the progress value.</p>
 145:    *
 146:    * <p>When called for the first time this initializes a timer
 147:    * which decides after <code>millisToDecideToPopup</code> time
 148:    * whether to show a progress dialog or not.</p>
 149:    *
 150:    * <p>If the progress value equals or exceeds the maximum
 151:    * value the progress dialog is closed automatically.</p>
 152:    *
 153:    * @param progress New progress value.
 154:    */
 155:   public void setProgress(int progress)
 156:   {
 157:     this.progress = progress;
 158: 
 159:     // Initializes and starts a timer with a task
 160:     // which measures the duration and displays
 161:     // a progress dialog if neccessary.
 162:     if ( timer == null && progressDialog == null )
 163:       {
 164:         timer = new Timer(25, null);
 165:         timer.addActionListener(new TimerListener());
 166:         timer.start();
 167:       }
 168: 
 169:     // Cancels timer and hides progress dialog if the
 170:     // maximum value is reached.
 171:     if ( progressBar != null && this.progress >= progressBar.getMaximum() )
 172:       {
 173:         // The reason for using progressBar.getMaximum() instead of max is that
 174:         // we want to prevent that changes to the value have any effect after the
 175:         // progress dialog is visible (This is how the JDK behaves.).
 176:         close();
 177:       }
 178: 
 179:   }
 180: 
 181:   /** Returns the minimum or start value of the operation.
 182:    *
 183:    * @returns Minimum or start value of the operation.
 184:    */
 185:   public int getMinimum()
 186:   {
 187:     return min;
 188:   }
 189: 
 190:   /**
 191:    * <p>Use this method to set the minimum or start value of
 192:    * your operation.</p>
 193:    *
 194:    * <p>For typical application like copy operation this will be
 195:    * zero.</p>
 196:    *
 197:    * <p>Keep in mind that changing this value after the progress
 198:    * dialog is made visible has no effect upon the progress bar.</p>
 199:    *
 200:    * @param minimum The new minimum value.
 201:    */
 202:   public void setMinimum(int minimum)
 203:   {
 204:     min = minimum;
 205:   }
 206: 
 207:   /**
 208:    * Return the maximum or end value of your operation.
 209:    *
 210:    * @returns Maximum or end value.
 211:    */
 212:   public int getMaximum()
 213:   {
 214:     return max;
 215:   }
 216: 
 217:   /**
 218:    * <p>Sets the maximum or end value of the operation to the
 219:    * given integer.</p>
 220:    *
 221:    * @param maximum
 222:    */
 223:   public void setMaximum(int maximum)
 224:   {
 225:     max = maximum;
 226:   }
 227: 
 228:   /**
 229:    * Returns whether the user canceled the operation.
 230:    *
 231:    * @returns Whether the operation was canceled.
 232:    */
 233:   public boolean isCanceled()
 234:   {
 235:     // The value is predefined to false
 236:     // and changes only when the user clicks
 237:     // the cancel button in the progress dialog.
 238:     return canceled;
 239:   }
 240: 
 241:   /**
 242:    * Returns the amount of milliseconds to wait
 243:    * until the ProgressMonitor should decide whether
 244:    * a progress dialog is to be shown or not.
 245:    *
 246:    * @returns The duration in milliseconds.
 247:    */
 248:   public int getMillisToDecideToPopup()
 249:   {
 250:     return millisToDecideToPopup;
 251:   }
 252: 
 253:   /**
 254:    * Sets the amount of milliseconds to wait until the
 255:    * ProgressMonitor should decide whether a progress dialog
 256:    * is to be shown or not.
 257:    *
 258:    * <p>This method has no effect when the progress dialog
 259:    * is already visible.</p>
 260:    *
 261:    * @param time The duration in milliseconds.
 262:    */
 263:   public void setMillisToDecideToPopup(int time)
 264:   {
 265:     millisToDecideToPopup = time;
 266:   }
 267: 
 268:   /**
 269:    * getMillisToPopup
 270:    * @returns int
 271:    */
 272:   public int getMillisToPopup()
 273:   {
 274:     return millisToPopup;
 275:   }
 276: 
 277:   /**
 278:    * setMillisToPopup
 279:    * @param time TODO
 280:    */
 281:   public void setMillisToPopup(int time)
 282:   {
 283:     millisToPopup = time;
 284:   }
 285: 
 286:   /**
 287:    * Returns a message which is shown in the progress dialog.
 288:    *
 289:    * @returns The changeable message visible in the progress dialog.
 290:    */
 291:   public String getNote()
 292:   {
 293:     return note;
 294:   }
 295: 
 296:   /**
 297:    * <p>Set the message shown in the progess dialog.</p>
 298:    *
 299:    * <p>Changing the note while the progress dialog is visible
 300:    * is possible.</p>
 301:    *
 302:    * @param note A message shown in the progress dialog.
 303:    */
 304:   public void setNote(String note)
 305:   {
 306:     if ( noteLabel != null )
 307:       {
 308:         noteLabel.setText(note);
 309:       }
 310:     else
 311:       {
 312:         this.note = note;
 313:       }
 314:   }
 315: 
 316:   /** Internal method that creates the progress dialog.
 317:    */
 318:   void createDialog()
 319:   {
 320:     // If there is no note we suppress the generation of the
 321:     // label.
 322:     Object[] tmp = (note == null) ?
 323:       new Object[]
 324:         {
 325:           message,
 326:           progressBar = new JProgressBar(min, max)
 327:         }
 328:       :
 329:       new Object[]
 330:         {
 331:           message,
 332:           noteLabel = new JLabel(note),
 333:           progressBar = new JProgressBar(min, max)
 334:         };
 335: 
 336:     JOptionPane pane = new JOptionPane(tmp, JOptionPane.INFORMATION_MESSAGE);
 337: 
 338:     // FIXME: Internationalize the button
 339:     JButton cancelButton = new JButton("Cancel");
 340:     cancelButton.addActionListener(new ActionListener()
 341:     {
 342:       public void actionPerformed(ActionEvent ae)
 343:       {
 344:         canceled = true;
 345:       }
 346:     });
 347: 
 348:     pane.setOptions(new Object[] { cancelButton });
 349: 
 350:     // FIXME: Internationalize the title
 351:     progressDialog = pane.createDialog(component, "Progress ...");
 352:     progressDialog.setModal(false);
 353:     progressDialog.setResizable(true);
 354: 
 355:     progressDialog.pack();
 356:     progressDialog.setVisible(true);
 357: 
 358:   }
 359: 
 360:   /** An ActionListener implementation which does the measurements
 361:    * and estimations of the ProgressMonitor.
 362:    */
 363:   class TimerListener implements ActionListener
 364:   {
 365:     long timestamp;
 366: 
 367:     int lastProgress;
 368: 
 369:     boolean first = true;
 370: 
 371:     TimerListener()
 372:     {
 373:        timestamp = System.currentTimeMillis();
 374:     }
 375: 
 376:     public void actionPerformed(ActionEvent ae)
 377:     {
 378:        long now = System.currentTimeMillis();
 379: 
 380:        if ( first )
 381:        {
 382:          if (( now - timestamp ) > millisToDecideToPopup )
 383:          {
 384:            first = false;
 385:            long expected = ( now - timestamp ) * ( max - min ) / ( progress - min );
 386: 
 387:            if ( expected > millisToPopup )
 388:            {
 389:              createDialog();
 390:            }
 391:          }
 392:          else
 393:          {
 394:            // We have not waited long enough to make a decision,
 395:            // so return and try again when the timer is invoked.
 396:            return;
 397:          }
 398:        }
 399:        else if ( progressDialog != null )
 400:        {
 401:          // The progress dialog is being displayed. We now calculate
 402:          // whether setting the progress bar to the current progress
 403:          // value would result in a visual difference. 
 404:          int delta = progress - progressBar.getValue();
 405: 
 406:          if ( ( delta * progressBar.getWidth() / (max - min) ) > 0 )
 407:          {
 408:            // At least one pixel would change.
 409:            progressBar.setValue(progress);
 410:          }
 411:        }
 412:        else
 413:        {
 414:          // No dialog necessary
 415:          timer.stop();
 416:          timer = null;
 417:        }
 418: 
 419:       timestamp = now;
 420:     }
 421:   }
 422: 
 423: }