Source for javax.swing.ProgressMonitor

   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: }