001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.gui.progress;
003
004 import java.awt.Component;
005 import java.awt.Dialog;
006 import java.awt.Frame;
007 import java.awt.Window;
008 import java.awt.event.ActionEvent;
009 import java.awt.event.ActionListener;
010 import java.awt.event.WindowAdapter;
011 import java.awt.event.WindowEvent;
012 import java.awt.event.WindowListener;
013
014 import javax.swing.JOptionPane;
015 import javax.swing.SwingUtilities;
016
017 import org.openstreetmap.josm.Main;
018 import org.openstreetmap.josm.gui.MapFrame;
019 import org.openstreetmap.josm.gui.MapStatus.BackgroundProgressMonitor;
020 import org.openstreetmap.josm.gui.PleaseWaitDialog;
021
022 public class PleaseWaitProgressMonitor extends AbstractProgressMonitor {
023
024 /**
025 * Implemented by both foreground dialog and background progress dialog (in status bar)
026 */
027 public interface ProgressMonitorDialog {
028 void setVisible(boolean visible);
029 void updateProgress(int progress);
030 void setCustomText(String text);
031 void setCurrentAction(String text);
032 void setIndeterminate(boolean newValue);
033 void appendLogMessage(String message); //TODO Not implemented properly in background monitor, log message will get lost if progress runs in background
034 }
035
036 public static final int PROGRESS_BAR_MAX = 10000;
037 private final Window dialogParent;
038
039 private int currentProgressValue = 0;
040 private String customText;
041 private String title;
042 private boolean indeterminate;
043
044 private boolean isInBackground;
045 private PleaseWaitDialog dialog;
046 private String windowTitle;
047 protected ProgressTaskId taskId;
048
049 private boolean cancelable;
050
051 private void doInEDT(Runnable runnable) {
052 // This must be invoke later even if current thread is EDT because inside there is dialog.setVisible which freeze current code flow until modal dialog is closed
053 SwingUtilities.invokeLater(runnable);
054 }
055
056
057 private void setDialogVisible(boolean visible) {
058 if (dialog.isVisible() != visible) {
059 dialog.setVisible(visible);
060 }
061 }
062
063 private ProgressMonitorDialog getDialog() {
064
065 BackgroundProgressMonitor backgroundMonitor = null;
066 MapFrame map = Main.map;
067 if (map != null) {
068 backgroundMonitor = map.statusLine.progressMonitor;
069 }
070
071 if (backgroundMonitor != null) {
072 backgroundMonitor.setVisible(isInBackground);
073 }
074 if (dialog != null) {
075 setDialogVisible(!isInBackground || backgroundMonitor == null);
076 }
077
078 if (isInBackground && backgroundMonitor != null) {
079 backgroundMonitor.setVisible(true);
080 if (dialog != null) {
081 setDialogVisible(false);
082 }
083 return backgroundMonitor;
084 } else if (backgroundMonitor != null) {
085 backgroundMonitor.setVisible(false);
086 if (dialog != null) {
087 setDialogVisible(true);
088 }
089 return dialog;
090 } else if (dialog != null) {
091 setDialogVisible(true);
092 return dialog;
093 } else
094 return null;
095 }
096
097 public PleaseWaitProgressMonitor() {
098 this("");
099 }
100
101 public PleaseWaitProgressMonitor(String windowTitle) {
102 this(Main.parent);
103 this.windowTitle = windowTitle;
104 }
105
106 public PleaseWaitProgressMonitor(Component dialogParent) {
107 super(new CancelHandler());
108 this.dialogParent = JOptionPane.getFrameForComponent(dialogParent);
109 this.cancelable = true;
110 }
111
112 public PleaseWaitProgressMonitor(Component dialogParent, String windowTitle) {
113 this(JOptionPane.getFrameForComponent(dialogParent));
114 this.windowTitle = windowTitle;
115 }
116
117 private ActionListener cancelListener = new ActionListener(){
118 public void actionPerformed(ActionEvent e) {
119 cancel();
120 }
121 };
122
123 private ActionListener inBackgroundListener = new ActionListener() {
124 @Override
125 public void actionPerformed(ActionEvent e) {
126 isInBackground = true;
127 ProgressMonitorDialog dialog = getDialog();
128 if (dialog != null) {
129 reset();
130 dialog.setVisible(true);
131 }
132 }
133 };
134
135 private WindowListener windowListener = new WindowAdapter(){
136 @Override public void windowClosing(WindowEvent e) {
137 cancel();
138 }
139 };
140
141 public final boolean isCancelable() {
142 return cancelable;
143 }
144
145 public final void setCancelable(boolean cancelable) {
146 this.cancelable = cancelable;
147 }
148
149 @Override
150 public void doBeginTask() {
151 doInEDT(new Runnable() {
152 public void run() {
153 Main.currentProgressMonitor = PleaseWaitProgressMonitor.this;
154 if (dialogParent instanceof Frame && dialog == null) {
155 dialog = new PleaseWaitDialog(dialogParent);
156 } else if (dialogParent instanceof Dialog && dialog == null) {
157 dialog = new PleaseWaitDialog(dialogParent);
158 } else
159 throw new ProgressException("PleaseWaitDialog parent must be either Frame or Dialog");
160
161 if (windowTitle != null) {
162 dialog.setTitle(windowTitle);
163 }
164 dialog.setCancelEnabled(cancelable);
165 dialog.setCancelCallback(cancelListener);
166 dialog.setInBackgroundCallback(inBackgroundListener);
167 dialog.setCustomText("");
168 dialog.addWindowListener(windowListener);
169 dialog.progress.setMaximum(PROGRESS_BAR_MAX);
170 dialog.setVisible(true);
171 }
172 });
173 }
174
175 @Override
176 public void doFinishTask() {
177 // do nothing
178 }
179
180 @Override
181 protected void updateProgress(double progressValue) {
182 final int newValue = (int)(progressValue * PROGRESS_BAR_MAX);
183 if (newValue != currentProgressValue) {
184 currentProgressValue = newValue;
185 doInEDT(new Runnable() {
186 public void run() {
187 ProgressMonitorDialog dialog = getDialog();
188 if (dialog != null) {
189 dialog.updateProgress(currentProgressValue);
190 }
191 }
192 });
193 }
194 }
195
196 @Override
197 protected void doSetCustomText(final String title) {
198 checkState(State.IN_TASK, State.IN_SUBTASK);
199 this.customText = title;
200 doInEDT(new Runnable() {
201 public void run() {
202 ProgressMonitorDialog dialog = getDialog();
203 if (dialog != null) {
204 dialog.setCustomText(title);
205 }
206 }
207 });
208 }
209
210 @Override
211 protected void doSetTitle(final String title) {
212 checkState(State.IN_TASK, State.IN_SUBTASK);
213 this.title = title;
214 doInEDT(new Runnable() {
215 public void run() {
216 ProgressMonitorDialog dialog = getDialog();
217 if (dialog != null) {
218 dialog.setCurrentAction(title);
219 }
220 }
221 });
222 }
223
224 @Override
225 protected void doSetIntermediate(final boolean value) {
226 this.indeterminate = value;
227 doInEDT(new Runnable() {
228 public void run() {
229 // Enable only if progress is at the beginning. Doing intermediate progress in the middle
230 // will hide already reached progress
231 ProgressMonitorDialog dialog = getDialog();
232 if (dialog != null) {
233 dialog.setIndeterminate(value && currentProgressValue == 0);
234 }
235 }
236 });
237 }
238
239 @Override
240 public void appendLogMessage(final String message) {
241 doInEDT(new Runnable() {
242 public void run() {
243 ProgressMonitorDialog dialog = getDialog();
244 if (dialog != null) {
245 dialog.appendLogMessage(message);
246 }
247 }
248 });
249 }
250
251 public void reset() {
252 if (dialog != null) {
253 dialog.setTitle(title);
254 dialog.setCustomText(customText);
255 dialog.updateProgress(currentProgressValue);
256 dialog.setIndeterminate(indeterminate && currentProgressValue == 0);
257 }
258 BackgroundProgressMonitor backgroundMonitor = null;
259 MapFrame map = Main.map;
260 if (map != null) {
261 backgroundMonitor = map.statusLine.progressMonitor;
262 }
263 if (backgroundMonitor != null) {
264 backgroundMonitor.setCurrentAction(title);
265 backgroundMonitor.setCustomText(customText);
266 backgroundMonitor.updateProgress(currentProgressValue);
267 backgroundMonitor.setIndeterminate(indeterminate && currentProgressValue == 0);
268 }
269
270 }
271
272 public void close() {
273 doInEDT(new Runnable() {
274 @Override
275 public void run() {
276 if (dialog != null) {
277 dialog.setVisible(false);
278 dialog.setCancelCallback(null);
279 dialog.setInBackgroundCallback(null);
280 dialog.removeWindowListener(windowListener);
281 dialog.dispose();
282 dialog = null;
283 Main.currentProgressMonitor = null;
284 MapFrame map = Main.map;
285 if (map != null) {
286 map.statusLine.progressMonitor.setVisible(false);
287 }
288 }
289 }
290 });
291 }
292
293 public void showForegroundDialog() {
294 isInBackground = false;
295 doInEDT(new Runnable() {
296 @Override
297 public void run() {
298 if (dialog != null) {
299 dialog.setInBackgroundPossible(PleaseWaitProgressMonitor.this.taskId != null && Main.isDisplayingMapView());
300 reset();
301 getDialog();
302 }
303 }
304 });
305
306 }
307
308 @Override
309 public void setProgressTaskId(ProgressTaskId taskId) {
310 this.taskId = taskId;
311 doInEDT(new Runnable() {
312 @Override
313 public void run() {
314 dialog.setInBackgroundPossible(PleaseWaitProgressMonitor.this.taskId != null && Main.isDisplayingMapView());
315 }
316 });
317 }
318
319 @Override
320 public ProgressTaskId getProgressTaskId() {
321 return taskId;
322 }
323
324
325 @Override
326 public Component getWindowParent() {
327 Component parent = dialog;
328 if (isInBackground || parent == null)
329 return Main.parent;
330 else
331 return parent;
332 }
333 }