source: josm/trunk/src/org/openstreetmap/josm/gui/progress/swing/PleaseWaitProgressMonitor.java@ 12675

Last change on this file since 12675 was 12675, checked in by Don-vip, 7 years ago

see #15182 - move the Swing-based ProgressMonitor implementations from gui.progress to gui.progress.swing. Progress monitor concept is used in very large parts of JOSM, a console-based implementation could be added later

  • Property svn:eol-style set to native
File size: 13.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.progress.swing;
3
4import java.awt.Component;
5import java.awt.GraphicsEnvironment;
6import java.awt.event.ActionListener;
7import java.awt.event.WindowAdapter;
8import java.awt.event.WindowEvent;
9import java.awt.event.WindowListener;
10
11import javax.swing.SwingUtilities;
12
13import org.openstreetmap.josm.Main;
14import org.openstreetmap.josm.gui.MainApplication;
15import org.openstreetmap.josm.gui.MapFrame;
16import org.openstreetmap.josm.gui.MapStatus.BackgroundProgressMonitor;
17import org.openstreetmap.josm.gui.PleaseWaitDialog;
18import org.openstreetmap.josm.gui.progress.AbstractProgressMonitor;
19import org.openstreetmap.josm.gui.progress.CancelHandler;
20import org.openstreetmap.josm.gui.progress.ProgressException;
21import org.openstreetmap.josm.gui.progress.ProgressTaskId;
22import org.openstreetmap.josm.gui.util.GuiHelper;
23import org.openstreetmap.josm.tools.bugreport.BugReport;
24
25/**
26 * A progress monitor used in {@link org.openstreetmap.josm.gui.PleaseWaitRunnable}.
27 * <p>
28 * Progress is displayed in a dialog window ({@link PleaseWaitDialog}).
29 * @since 12675 (moved from {@code gui.progress} package}
30 */
31public class PleaseWaitProgressMonitor extends AbstractProgressMonitor {
32
33 /**
34 * Implemented by both foreground dialog and background progress dialog (in status bar)
35 */
36 public interface ProgressMonitorDialog {
37 /**
38 * Sets the visibility of this dialog
39 * @param visible The visibility, <code>true</code> to show it, <code>false</code> to hide it
40 */
41 void setVisible(boolean visible);
42
43 /**
44 * Updates the progress value to the specified progress.
45 * @param progress The progress as integer. Between 0 and {@link PleaseWaitProgressMonitor#PROGRESS_BAR_MAX}
46 */
47 void updateProgress(int progress);
48
49 /**
50 * Sets the description of what is done
51 * @param text The description of the task
52 */
53 void setCustomText(String text);
54
55 /**
56 * Sets the current action that is done
57 * @param text The current action
58 */
59 void setCurrentAction(String text);
60
61 /**
62 * Display that the current progress cannot be determined
63 * @param newValue wether the progress cannot be determined
64 */
65 void setIndeterminate(boolean newValue);
66
67 /**
68 * Append a message to the progress log
69 * <p>
70 * TODO Not implemented properly in background monitor, log message will get lost if progress runs in background
71 * @param message The message
72 */
73 void appendLogMessage(String message);
74 }
75
76 /**
77 * The maximum value the progress bar that displays the current progress should have.
78 */
79 public static final int PROGRESS_BAR_MAX = 10_000;
80
81 /**
82 * The progress monitor being currently displayed.
83 */
84 static PleaseWaitProgressMonitor currentProgressMonitor;
85
86 private final Component dialogParent;
87
88 private int currentProgressValue;
89 private String customText;
90 private String title;
91 private boolean indeterminate;
92
93 private boolean isInBackground;
94 private PleaseWaitDialog dialog;
95 private String windowTitle;
96 protected ProgressTaskId taskId;
97
98 private boolean cancelable;
99
100 /**
101 * Returns the progress monitor being currently displayed.
102 * @return the progress monitor being currently displayed
103 * @since 12638
104 */
105 public static PleaseWaitProgressMonitor getCurrent() {
106 return currentProgressMonitor;
107 }
108
109 private void doInEDT(Runnable runnable) {
110 // This must be invoke later even if current thread is EDT because inside there is dialog.setVisible
111 // which freeze current code flow until modal dialog is closed
112 SwingUtilities.invokeLater(() -> {
113 try {
114 runnable.run();
115 } catch (RuntimeException e) { // NOPMD
116 throw BugReport.intercept(e).put("monitor", this);
117 }
118 });
119 }
120
121 private void setDialogVisible(boolean visible) {
122 if (dialog.isVisible() != visible) {
123 dialog.setVisible(visible);
124 }
125 }
126
127 private ProgressMonitorDialog getDialog() {
128
129 BackgroundProgressMonitor backgroundMonitor = null;
130 MapFrame map = MainApplication.getMap();
131 if (map != null) {
132 backgroundMonitor = map.statusLine.progressMonitor;
133 }
134
135 if (backgroundMonitor != null) {
136 backgroundMonitor.setVisible(isInBackground);
137 }
138 if (dialog != null) {
139 setDialogVisible(!isInBackground || backgroundMonitor == null);
140 }
141
142 if (isInBackground && backgroundMonitor != null) {
143 backgroundMonitor.setVisible(true);
144 if (dialog != null) {
145 setDialogVisible(false);
146 }
147 return backgroundMonitor;
148 } else if (backgroundMonitor != null) {
149 backgroundMonitor.setVisible(false);
150 if (dialog != null) {
151 setDialogVisible(true);
152 }
153 return dialog;
154 } else if (dialog != null) {
155 setDialogVisible(true);
156 return dialog;
157 } else
158 return null;
159 }
160
161 /**
162 * Constructs a new {@code PleaseWaitProgressMonitor}.
163 */
164 public PleaseWaitProgressMonitor() {
165 this("");
166 }
167
168 /**
169 * Constructs a new {@code PleaseWaitProgressMonitor}.
170 * @param windowTitle window title
171 */
172 public PleaseWaitProgressMonitor(String windowTitle) {
173 this(Main.parent);
174 this.windowTitle = windowTitle;
175 }
176
177 /**
178 * Constructs a new {@code PleaseWaitProgressMonitor}.
179 * @param dialogParent component to get parent frame from
180 */
181 public PleaseWaitProgressMonitor(Component dialogParent) {
182 super(new CancelHandler());
183 if (GraphicsEnvironment.isHeadless()) {
184 this.dialogParent = dialogParent;
185 } else {
186 this.dialogParent = GuiHelper.getFrameForComponent(dialogParent);
187 }
188 this.cancelable = true;
189 }
190
191 /**
192 * Constructs a new {@code PleaseWaitProgressMonitor}.
193 * @param dialogParent component to get parent frame from
194 * @param windowTitle window title
195 */
196 public PleaseWaitProgressMonitor(Component dialogParent, String windowTitle) {
197 this(GuiHelper.getFrameForComponent(dialogParent));
198 this.windowTitle = windowTitle;
199 }
200
201 private final ActionListener cancelListener = e -> cancel();
202
203 private final ActionListener inBackgroundListener = e -> {
204 isInBackground = true;
205 ProgressMonitorDialog dlg = getDialog();
206 if (dlg != null) {
207 reset();
208 dlg.setVisible(true);
209 }
210 };
211
212 private final WindowListener windowListener = new WindowAdapter() {
213 @Override public void windowClosing(WindowEvent e) {
214 cancel();
215 }
216 };
217
218 /**
219 * See if this task is canceleable
220 * @return <code>true</code> if it can be canceled
221 */
222 public final boolean isCancelable() {
223 return cancelable;
224 }
225
226 /**
227 * Sets this task to be cancelable
228 * @param cancelable Whether it can be canceled
229 */
230 public final void setCancelable(boolean cancelable) {
231 this.cancelable = cancelable;
232 }
233
234 @Override
235 public void doBeginTask() {
236 doInEDT(() -> {
237 currentProgressMonitor = this;
238 if (GraphicsEnvironment.isHeadless()) {
239 return;
240 }
241 if (dialogParent != null && dialog == null) {
242 dialog = new PleaseWaitDialog(dialogParent);
243 } else {
244 throw new ProgressException("PleaseWaitDialog parent must be set");
245 }
246
247 if (windowTitle != null) {
248 dialog.setTitle(windowTitle);
249 }
250 dialog.setCancelEnabled(cancelable);
251 dialog.setCancelCallback(cancelListener);
252 dialog.setInBackgroundCallback(inBackgroundListener);
253 dialog.setCustomText("");
254 dialog.addWindowListener(windowListener);
255 dialog.setMaximumProgress(PROGRESS_BAR_MAX);
256 dialog.setVisible(true);
257 });
258 }
259
260 @Override
261 public void doFinishTask() {
262 // do nothing
263 }
264
265 @Override
266 protected void updateProgress(double progressValue) {
267 final int newValue = (int) (progressValue * PROGRESS_BAR_MAX);
268 if (newValue != currentProgressValue) {
269 currentProgressValue = newValue;
270 doInEDT(() -> {
271 ProgressMonitorDialog dlg = getDialog();
272 if (dlg != null) {
273 dlg.updateProgress(currentProgressValue);
274 }
275 });
276 }
277 }
278
279 @Override
280 protected void doSetCustomText(final String title) {
281 checkState(State.IN_TASK, State.IN_SUBTASK);
282 this.customText = title;
283 doInEDT(() -> {
284 ProgressMonitorDialog dlg = getDialog();
285 if (dlg != null) {
286 dlg.setCustomText(title);
287 }
288 });
289 }
290
291 @Override
292 protected void doSetTitle(final String title) {
293 checkState(State.IN_TASK, State.IN_SUBTASK);
294 this.title = title;
295 doInEDT(() -> {
296 ProgressMonitorDialog dlg = getDialog();
297 if (dlg != null) {
298 dlg.setCurrentAction(title);
299 }
300 });
301 }
302
303 @Override
304 protected void doSetIntermediate(final boolean value) {
305 this.indeterminate = value;
306 doInEDT(() -> {
307 // Enable only if progress is at the beginning. Doing intermediate progress in the middle
308 // will hide already reached progress
309 ProgressMonitorDialog dlg = getDialog();
310 if (dlg != null) {
311 dlg.setIndeterminate(value && currentProgressValue == 0);
312 }
313 });
314 }
315
316 @Override
317 public void appendLogMessage(final String message) {
318 doInEDT(() -> {
319 ProgressMonitorDialog dlg = getDialog();
320 if (dlg != null) {
321 dlg.appendLogMessage(message);
322 }
323 });
324 }
325
326 /**
327 * Update the dialog values
328 */
329 public void reset() {
330 if (dialog != null) {
331 dialog.setTitle(title);
332 dialog.setCustomText(customText);
333 dialog.updateProgress(currentProgressValue);
334 dialog.setIndeterminate(indeterminate && currentProgressValue == 0);
335 }
336 BackgroundProgressMonitor backgroundMonitor = null;
337 MapFrame map = MainApplication.getMap();
338 if (map != null) {
339 backgroundMonitor = map.statusLine.progressMonitor;
340 }
341 if (backgroundMonitor != null) {
342 backgroundMonitor.setCurrentAction(title);
343 backgroundMonitor.setCustomText(customText);
344 backgroundMonitor.updateProgress(currentProgressValue);
345 backgroundMonitor.setIndeterminate(indeterminate && currentProgressValue == 0);
346 }
347 }
348
349 /**
350 * Close the progress dialog window.
351 */
352 public void close() {
353 doInEDT(() -> {
354 if (dialog != null) {
355 dialog.setVisible(false);
356 dialog.setCancelCallback(null);
357 dialog.setInBackgroundCallback(null);
358 dialog.removeWindowListener(windowListener);
359 dialog.dispose();
360 dialog = null;
361 currentProgressMonitor = null;
362 MapFrame map = MainApplication.getMap();
363 if (map != null) {
364 map.statusLine.progressMonitor.setVisible(false);
365 }
366 }
367 });
368 }
369
370 /**
371 * Show the progress dialog in foreground
372 */
373 public void showForegroundDialog() {
374 isInBackground = false;
375 doInEDT(() -> {
376 if (dialog != null) {
377 dialog.setInBackgroundPossible(taskId != null && MainApplication.isDisplayingMapView());
378 reset();
379 getDialog();
380 }
381 });
382 }
383
384 @Override
385 public void setProgressTaskId(ProgressTaskId taskId) {
386 this.taskId = taskId;
387 doInEDT(() -> {
388 if (dialog != null) {
389 dialog.setInBackgroundPossible(taskId != null && MainApplication.isDisplayingMapView());
390 }
391 });
392 }
393
394 @Override
395 public ProgressTaskId getProgressTaskId() {
396 return taskId;
397 }
398
399 @Override
400 public Component getWindowParent() {
401 Component parent = dialog;
402 if (isInBackground || parent == null)
403 return Main.parent;
404 else
405 return parent;
406 }
407
408 @Override
409 public String toString() {
410 return "PleaseWaitProgressMonitor [currentProgressValue=" + currentProgressValue + ", customText=" + customText
411 + ", title=" + title + ", indeterminate=" + indeterminate + ", isInBackground=" + isInBackground
412 + ", windowTitle=" + windowTitle + ", taskId=" + taskId + ", cancelable=" + cancelable + ", state="
413 + state + "]";
414 }
415}
Note: See TracBrowser for help on using the repository browser.