1 package net.sf.openrocket.gui.dialogs;
3 import java.awt.Dimension;
4 import java.awt.Window;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.beans.PropertyChangeEvent;
8 import java.beans.PropertyChangeListener;
10 import javax.swing.JButton;
11 import javax.swing.JDialog;
12 import javax.swing.JLabel;
13 import javax.swing.JPanel;
14 import javax.swing.JProgressBar;
15 import javax.swing.SwingWorker;
17 import net.miginfocom.swing.MigLayout;
18 import net.sf.openrocket.util.MathUtil;
22 * A modal dialog that runs specific SwingWorkers and waits until they complete.
23 * A message and progress bar is provided and a cancel button. If the cancel button
24 * is pressed, the currently running worker is interrupted and the later workers are not
27 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
29 public class SwingWorkerDialog extends JDialog implements PropertyChangeListener {
31 /** Number of milliseconds to wait at a time between checking worker status */
32 private static final int DELAY = 100;
34 /** Minimum number of milliseconds to wait before estimating work length */
35 private static final int ESTIMATION_DELAY = 190;
37 /** Open the dialog if estimated remaining time is longer than this */
38 private static final int REMAINING_TIME_FOR_DIALOG = 1000;
40 /** Open the dialog if estimated total time is longed than this */
41 private static final int TOTAL_TIME_FOR_DIALOG = 2000;
44 private final SwingWorker<?,?> worker;
45 private final JProgressBar progressBar;
47 private boolean cancelled = false;
50 private SwingWorkerDialog(Window parent, String title, String label,
52 super(parent, title, ModalityType.APPLICATION_MODAL);
55 w.addPropertyChangeListener(this);
57 JPanel panel = new JPanel(new MigLayout("fill"));
60 panel.add(new JLabel(label), "wrap para");
63 progressBar = new JProgressBar();
64 panel.add(progressBar, "growx, wrap para");
66 JButton cancel = new JButton("Cancel");
67 cancel.addActionListener(new ActionListener() {
69 public void actionPerformed(ActionEvent e) {
73 panel.add(cancel, "right");
76 this.setMinimumSize(new Dimension(250,100));
78 this.setLocationRelativeTo(parent);
79 this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
83 public void propertyChange(PropertyChangeEvent evt) {
84 if (worker.getState() == SwingWorker.StateValue.DONE) {
87 progressBar.setValue(worker.getProgress());
90 private void cancel() {
96 private void close() {
97 worker.removePropertyChangeListener(this);
98 this.setVisible(false);
99 // For some reason setVisible(false) is not always enough...
105 * Run a SwingWorker and if necessary show a dialog displaying the progress of
106 * the worker. The progress information is obtained from the SwingWorker's
107 * progress property. The dialog is shown only if the worker is estimated to
108 * take a notable amount of time.
110 * The dialog contains a cancel button. Clicking it will call
111 * <code>worker.cancel(true)</code> and close the dialog immediately.
113 * @param parent the parent window for the dialog, or <code>null</code>.
114 * @param title the title for the dialog.
115 * @param label an additional label for the dialog, or <code>null</code>.
116 * @param worker the SwingWorker to execute.
117 * @return <code>true</code> if the worker has completed normally,
118 * <code>false</code> if the user cancelled the operation
120 public static boolean runWorker(Window parent, String title, String label,
121 SwingWorker<?,?> worker) {
123 // Start timing the worker
124 final long startTime = System.currentTimeMillis();
127 // Monitor worker thread before opening the dialog
132 } catch (InterruptedException e) {
133 // Should never occur
137 if (worker.isDone()) {
138 // Worker has completed within time limits
142 // Check whether enough time has gone to get realistic estimate
143 long elapsed = System.currentTimeMillis() - startTime;
144 if (elapsed < ESTIMATION_DELAY)
148 // Calculate and check estimated remaining time
149 int progress = MathUtil.clamp(worker.getProgress(), 1, 100); // Avoid div-by-zero
150 long estimate = elapsed * 100 / progress;
151 long remaining = estimate - elapsed;
153 if (estimate >= TOTAL_TIME_FOR_DIALOG)
156 if (remaining >= REMAINING_TIME_FOR_DIALOG)
161 // Dialog is required
163 SwingWorkerDialog dialog = new SwingWorkerDialog(parent, title, label, worker);
164 dialog.setVisible(true);
166 return !dialog.cancelled;