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.logging.LogHelper;
19 import net.sf.openrocket.startup.Application;
20 import net.sf.openrocket.util.MathUtil;
24 * A modal dialog that runs specific SwingWorkers and waits until they complete.
25 * A message and progress bar is provided and a cancel button. If the cancel button
26 * is pressed, the currently running worker is interrupted and the later workers are not
29 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
31 public class SwingWorkerDialog extends JDialog implements PropertyChangeListener {
32 private static final LogHelper log = Application.getLogger();
34 /** Number of milliseconds to wait at a time between checking worker status */
35 private static final int DELAY = 100;
37 /** Minimum number of milliseconds to wait before estimating work length */
38 private static final int ESTIMATION_DELAY = 190;
40 /** Open the dialog if estimated remaining time is longer than this */
41 private static final int REMAINING_TIME_FOR_DIALOG = 1000;
43 /** Open the dialog if estimated total time is longed than this */
44 private static final int TOTAL_TIME_FOR_DIALOG = 2000;
47 private final SwingWorker<?, ?> worker;
48 private final JProgressBar progressBar;
50 private boolean cancelled = false;
53 private SwingWorkerDialog(Window parent, String title, String label, SwingWorker<?, ?> w) {
54 super(parent, title, ModalityType.APPLICATION_MODAL);
57 w.addPropertyChangeListener(this);
59 JPanel panel = new JPanel(new MigLayout("fill"));
62 panel.add(new JLabel(label), "wrap para");
65 progressBar = new JProgressBar();
66 panel.add(progressBar, "growx, wrap para");
68 JButton cancel = new JButton("Cancel");
69 cancel.addActionListener(new ActionListener() {
71 public void actionPerformed(ActionEvent e) {
72 log.user("User cancelled SwingWorker operation");
76 panel.add(cancel, "right");
79 this.setMinimumSize(new Dimension(250, 100));
81 this.setLocationRelativeTo(parent);
82 this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
86 public void propertyChange(PropertyChangeEvent evt) {
87 if (worker.getState() == SwingWorker.StateValue.DONE) {
90 progressBar.setValue(worker.getProgress());
93 private void cancel() {
99 private void close() {
100 worker.removePropertyChangeListener(this);
101 this.setVisible(false);
102 // For some reason setVisible(false) is not always enough...
108 * Run a SwingWorker and if necessary show a dialog displaying the progress of
109 * the worker. The progress information is obtained from the SwingWorker's
110 * progress property. The dialog is shown only if the worker is estimated to
111 * take a notable amount of time.
113 * The dialog contains a cancel button. Clicking it will call
114 * <code>worker.cancel(true)</code> and close the dialog immediately.
116 * @param parent the parent window for the dialog, or <code>null</code>.
117 * @param title the title for the dialog.
118 * @param label an additional label for the dialog, or <code>null</code>.
119 * @param worker the SwingWorker to execute.
120 * @return <code>true</code> if the worker has completed normally,
121 * <code>false</code> if the user cancelled the operation
123 public static boolean runWorker(Window parent, String title, String label,
124 SwingWorker<?, ?> worker) {
126 log.info("Running SwingWorker " + worker);
128 // Start timing the worker
129 final long startTime = System.currentTimeMillis();
132 // Monitor worker thread before opening the dialog
137 } catch (InterruptedException e) {
138 // Should never occur
139 log.error("EDT was interrupted", e);
142 if (worker.isDone()) {
143 // Worker has completed within time limits
144 log.info("Worker completed before opening dialog");
148 // Check whether enough time has gone to get realistic estimate
149 long elapsed = System.currentTimeMillis() - startTime;
150 if (elapsed < ESTIMATION_DELAY)
154 // Calculate and check estimated remaining time
155 int progress = MathUtil.clamp(worker.getProgress(), 1, 100); // Avoid div-by-zero
156 long estimate = elapsed * 100 / progress;
157 long remaining = estimate - elapsed;
159 log.debug("Estimated run time, estimate=" + estimate + " remaining=" + remaining);
161 if (estimate >= TOTAL_TIME_FOR_DIALOG)
164 if (remaining >= REMAINING_TIME_FOR_DIALOG)
169 // Dialog is required
171 log.info("Opening dialog for SwingWorker " + worker);
172 SwingWorkerDialog dialog = new SwingWorkerDialog(parent, title, label, worker);
173 dialog.setVisible(true);
174 log.info("Worker done, cancelled=" + dialog.cancelled);
176 return !dialog.cancelled;