Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4804458

REGRESSION: Cancel doesn't work on ProgressMonitor started from Modal Dialog

XMLWordPrintable

    • tiger
    • x86
    • windows_2000



      Name: jk109818 Date: 01/16/2003


      FULL PRODUCT VERSION :
      C:\>java -version
      java version "1.3.0_02"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0_02)
      Java HotSpot(TM) Client VM (build 1.3.0_02, mixed mode)

      NOTE: 1.3.0_02 is the earliest version where I see the problem. I also see it
      in all versions I tested later than 1.3.0_02, including 1.3.0_03, 1.3.0_05,
      1.3.1, and 1.4.1.

      I do NOT see the problem in 1.3.0_01.

      FULL OPERATING SYSTEM VERSION :
      Microsoft Windows 2000 [Version 5.00.2195]
      Service Pack 2
      ADDITIONAL OPERATING SYSTEMS :
      Microsoft Windows NT



      A DESCRIPTION OF THE PROBLEM :
      If you start a swing ProgressMonitor from a modal dialog,
      the cancel button is ignored. From a modeless dialog, it
      works as expected. The problem appears to have been
      introduced in J2SE 1.3.0_02. In 1.3.0_01, the cancel
      button works from both modal and modeless dialogs.

      REGRESSION. Last worked in version 1.3

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. The included source illustrates the problem. It is a
      modified version of the ProgressMonitor demo code from the
      Java Tutorial. The version in the tutorial creates the
      ProgressMonitor from a JFrame. The included source creates
      the ProgressMonitor from either a modal or modeless JDialog.
      2. Compile and run the included source.
      3. Click on "Create Modeless JDialog".
      4. Click on "Start long task from Modeless".
      5. When ProgressMonitor appears, click on "Cancel".
      6. The progress monitor window will be disposed and the
      task ends ("Task Canceled" appears in text area)
      7. Close (click "X") the ProgressMonitorDemo JDialog.
      8. Click on "Create Modal JDialog".
      9. Click on "Start long task from Modal".
      10. When ProgressMonitor appears, click on "Cancel".
      11. Nothing happens. (Eventually, task completes and the
      window dismisses. "Task Completed" appears in text area.)

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      At step 10, I expect the progress monitor window to be
      disposed and the task to stop when I click on "Cancel".

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.awt.*;
      import java.awt.event.*;
      import javax.swing.*;
      import javax.swing.SwingUtilities;

      public class StartProgressMonitorDemo extends JFrame {
          private JButton startModalButton,startModelessButton;
          private String newline = "\n";
          private ProgressMonitorDemo pmd;

          public StartProgressMonitorDemo() {
              super("StartProgressMonitorDemo");

              //Create the demo's UI.
              startModalButton = new JButton("Create Modal JDialog");;
              startModalButton.addActionListener(new ButtonListener(this));
              
              startModelessButton = new JButton("Create Modeless JDialog");
              startModelessButton.addActionListener(new ButtonListener(this));
              

              JPanel contentPane = new JPanel();
              contentPane.setLayout(new BorderLayout());
              contentPane.add(startModalButton, BorderLayout.NORTH);
              contentPane.add(startModelessButton, BorderLayout.SOUTH);
              contentPane.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
              setContentPane(contentPane);
              
          }

          /**
           * The actionPerformed method in this class
           * is called when the user presses the start button.
           */
          class ButtonListener implements ActionListener {
           JFrame myFrame;
           ButtonListener(JFrame f) {
           myFrame = f;
           }
              public void actionPerformed(ActionEvent evt) {
      Dialog frame = new ProgressMonitorDemo(myFrame,
      (evt.getSource() ==
      startModalButton)?true:false);
      frame.pack();
      frame.setVisible(true);
              }
          }
          
          public static void main(String[] args) {
              JFrame frame = new StartProgressMonitorDemo();
              frame.addWindowListener(new WindowAdapter() {
                  public void windowClosing(WindowEvent e) {
                      System.exit(0);
                  }
              });

              frame.pack();
      frame.setSize(new Dimension(400,200));
              frame.setVisible(true);
          }
      }

      /****************************************************************************/

      class ProgressMonitorDemo extends JDialog {
          public final static int ONE_SECOND = 1000;

          private ProgressMonitor progressMonitor;
          private Timer timer;
          private JButton startButton;
          private LongTask task;
          private JTextArea taskOutput;
          private String newline = "\n";
          
          private JFrame myFrame;
          private Container contentPane;

          public ProgressMonitorDemo(JFrame frame,boolean modal) {
              super(frame,"ProgressMonitorDemo",modal);
              myFrame = frame;
              task = new LongTask();

              //Create the demo's UI.
              startButton = new JButton("Start long task from " +
      ((modal)?"Modal":"Modeless"));
              startButton.setActionCommand("start");
              startButton.addActionListener(new ButtonListener());

              taskOutput = new JTextArea(5, 20);
              taskOutput.setMargin(new Insets(5,5,5,5));
              taskOutput.setEditable(false);

      contentPane = getContentPane();
              contentPane.setLayout(new BorderLayout());
              contentPane.add(startButton, BorderLayout.NORTH);
              contentPane.add(new JScrollPane(taskOutput), BorderLayout.CENTER);
              
              setDefaultCloseOperation(DISPOSE_ON_CLOSE);

              //Create a timer.
              timer = new Timer(ONE_SECOND, new TimerListener());
          }

          /**
           * The actionPerformed method in this class
           * is called each time the Timer "goes off".
           */
          class TimerListener implements ActionListener {
              public void actionPerformed(ActionEvent evt) {
               boolean taskdone=task.done();
                  if (progressMonitor.isCanceled() || taskdone) {
                      progressMonitor.close();
                      task.stop();
                      Toolkit.getDefaultToolkit().beep();
                      timer.stop();
                      taskOutput.append((taskdone)?"Task completed.":"Task canceled."
      + newline);
                      startButton.setEnabled(true);
                  } else {
                      progressMonitor.setNote(task.getMessage());
                      progressMonitor.setProgress(task.getCurrent());
                      taskOutput.append(task.getMessage() + newline);
                      taskOutput.setCaretPosition(
                          taskOutput.getDocument().getLength());
                  }
              }
          }

          /**
           * The actionPerformed method in this class
           * is called when the user presses the start button.
           */
          class ButtonListener implements ActionListener {
              public void actionPerformed(ActionEvent evt) {
                  progressMonitor = new ProgressMonitor(ProgressMonitorDemo.this,
                                            "Running a Long Task",
                                            "", 0, task.getLengthOfTask());
                  progressMonitor.setProgress(0);
                  progressMonitor.setMillisToDecideToPopup(2 * ONE_SECOND);

                  startButton.setEnabled(false);
                  task.go();
                  timer.start();
              }
          }
      }


      /****************************************************************************/
      /** Uses a SwingWorker to perform a time-consuming (and utterly fake) task. */

      class LongTask {
          private int lengthOfTask;
          private int current = 0;
          private String statMessage;

          LongTask() {
              //Compute length of task...
              //In a real program, this would figure out
              //the number of bytes to read or whatever.
              lengthOfTask = 1000;
          }

          /**
           * Called from ProgressBarDemo to start the task.
           */
          void go() {
              current = 0;
              final SwingWorker worker = new SwingWorker() {
                  public Object construct() {
                      return new ActualTask();
                  }
              };
              worker.start();
          }

          /**
           * Called from ProgressBarDemo to find out how much work needs
           * to be done.
           */
          int getLengthOfTask() {
              return lengthOfTask;
          }

          /**
           * Called from ProgressBarDemo to find out how much has been done.
           */
          int getCurrent() {
              return current;
          }

          void stop() {
              current = lengthOfTask;
          }

          /**
           * Called from ProgressBarDemo to find out if the task has completed.
           */
          boolean done() {
              if (current >= lengthOfTask)
                  return true;
              else
                  return false;
          }

          String getMessage() {
              return statMessage;
          }

          /**
           * The actual long running task. This runs in a SwingWorker thread.
           */
          class ActualTask {
              ActualTask () {
                  //Fake a long task,
                  //making a random amount of progress every second.
                  while (current < lengthOfTask) {
                      try {
                          Thread.sleep(1000); //sleep for a second
                          current += Math.random() * 100; //make some progress
                          if (current > lengthOfTask) {
                              current = lengthOfTask;
                          }
                          statMessage = "Completed " + current +
                                        " out of " + lengthOfTask + ".";
                      } catch (InterruptedException e) {}
                  }
              }
          }
      }


      /****************************************************************************/

      /**
       * This is the 3rd version of SwingWorker (also known as
       * SwingWorker 3), an abstract class that you subclass to
       * perform GUI-related work in a dedicated thread. For
       * instructions on using this class, see:
       *
       * http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
       *
       * Note that the API changed slightly in the 3rd version:
       * You must now invoke start() on the SwingWorker after
       * creating it.
       */
      abstract class SwingWorker {
          private Object value; // see getValue(), setValue()
          private Thread thread;

          /**
           * Class to maintain reference to current worker thread
           * under separate synchronization control.
           */
          private static class ThreadVar {
              private Thread thread;
              ThreadVar(Thread t) { thread = t; }
              synchronized Thread get() { return thread; }
              synchronized void clear() { thread = null; }
          }

          private ThreadVar threadVar;

          /**
           * Get the value produced by the worker thread, or null if it
           * hasn't been constructed yet.
           */
          protected synchronized Object getValue() {
              return value;
          }

          /**
           * Set the value produced by worker thread
           */
          private synchronized void setValue(Object x) {
              value = x;
          }

          /**
           * Compute the value to be returned by the <code>get</code> method.
           */
          public abstract Object construct();

          /**
           * Called on the event dispatching thread (not on the worker thread)
           * after the <code>construct</code> method has returned.
           */
          public void finished() {
          }

          /**
           * A new method that interrupts the worker thread. Call this method
           * to force the worker to stop what it's doing.
           */
          public void interrupt() {
              Thread t = threadVar.get();
              if (t != null) {
                  t.interrupt();
              }
              threadVar.clear();
          }

          /**
           * Return the value created by the <code>construct</code> method.
           * Returns null if either the constructing thread or the current
           * thread was interrupted before a value was produced.
           *
           * @return the value created by the <code>construct</code> method
           */
          public Object get() {
              while (true) {
                  Thread t = threadVar.get();
                  if (t == null) {
                      return getValue();
                  }
                  try {
                      t.join();
                  }
                  catch (InterruptedException e) {
                      Thread.currentThread().interrupt(); // propagate
                      return null;
                  }
              }
          }


          /**
           * Start a thread that will call the <code>construct</code> method
           * and then exit.
           */
          public SwingWorker() {
              final Runnable doFinished = new Runnable() {
                 public void run() { finished(); }
              };

              Runnable doConstruct = new Runnable() {
                  public void run() {
                      try {
                          setValue(construct());
                      }
                      finally {
                          threadVar.clear();
                      }

                      SwingUtilities.invokeLater(doFinished);
                  }
              };

              Thread t = new Thread(doConstruct);
              threadVar = new ThreadVar(t);
          }

          /**
           * Start the worker thread.
           */
          public void start() {
              Thread t = threadVar.get();
              if (t != null) {
                  t.start();
              }
          }
      }

      ---------- END SOURCE ----------

      Release Regression From : 1.3.0_01
      The above release value was the last known release where this
      bug was known to work. Since then there has been a regression.

      (Review ID: 166855)
      ======================================================================

            apikalev Andrey Pikalev
            jkimsunw Jeffrey Kim (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: