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) {
75 panel.add(cancel, "right");
78 this.setMinimumSize(new Dimension(250,100));
80 this.setLocationRelativeTo(parent);
81 this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
85 public void propertyChange(PropertyChangeEvent evt) {
86 if (worker.getState() == SwingWorker.StateValue.DONE) {
89 progressBar.setValue(worker.getProgress());
92 private void close() {
93 worker.removePropertyChangeListener(this);
94 this.setVisible(false);
99 * Run a SwingWorker and if necessary show a dialog displaying the progress of
100 * the worker. The progress information is obtained from the SwingWorker's
101 * progress property. The dialog is shown only if the worker is estimated to
102 * take a notable amount of time.
104 * The dialog contains a cancel button. Clicking it will call
105 * <code>worker.cancel(true)</code> and close the dialog immediately.
107 * @param parent the parent window for the dialog, or <code>null</code>.
108 * @param title the title for the dialog.
109 * @param label an additional label for the dialog, or <code>null</code>.
110 * @param worker the SwingWorker to execute.
111 * @return <code>true</code> if the worker has completed normally,
112 * <code>false</code> if the user cancelled the operation
114 public static boolean runWorker(Window parent, String title, String label,
115 SwingWorker<?,?> worker) {
117 // Start timing the worker
118 final long startTime = System.currentTimeMillis();
121 // Monitor worker thread before opening the dialog
126 } catch (InterruptedException e) {
127 // Should never occur
131 if (worker.isDone()) {
132 // Worker has completed within time limits
136 // Check whether enough time has gone to get realistic estimate
137 long elapsed = System.currentTimeMillis() - startTime;
138 if (elapsed < ESTIMATION_DELAY)
142 // Calculate and check estimated remaining time
143 int progress = MathUtil.clamp(worker.getProgress(), 1, 100); // Avoid div-by-zero
144 long estimate = elapsed * 100 / progress;
145 long remaining = estimate - elapsed;
147 if (estimate >= TOTAL_TIME_FOR_DIALOG)
150 if (remaining >= REMAINING_TIME_FOR_DIALOG)
155 // Dialog is required
157 SwingWorkerDialog dialog = new SwingWorkerDialog(parent, title, label, worker);
158 dialog.setVisible(true);
160 return !dialog.cancelled;