// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.progress; import java.awt.Component; import java.awt.GraphicsEnvironment; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import javax.swing.SwingUtilities; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.gui.MapFrame; import org.openstreetmap.josm.gui.MapStatus.BackgroundProgressMonitor; import org.openstreetmap.josm.gui.PleaseWaitDialog; import org.openstreetmap.josm.gui.util.GuiHelper; import org.openstreetmap.josm.tools.bugreport.BugReport; public class PleaseWaitProgressMonitor extends AbstractProgressMonitor { /** * Implemented by both foreground dialog and background progress dialog (in status bar) */ public interface ProgressMonitorDialog { void setVisible(boolean visible); /** * Updates the progress value to the specified progress. * @param progress The progress as integer. Between 0 and {@link PleaseWaitProgressMonitor#PROGRESS_BAR_MAX} */ void updateProgress(int progress); void setCustomText(String text); void setCurrentAction(String text); void setIndeterminate(boolean newValue); // TODO Not implemented properly in background monitor, log message will get lost if progress runs in background void appendLogMessage(String message); } /** * The maximum value the progress bar that displays the current progress should have. */ public static final int PROGRESS_BAR_MAX = 10_000; private final Component dialogParent; private int currentProgressValue; private String customText; private String title; private boolean indeterminate; private boolean isInBackground; private PleaseWaitDialog dialog; private String windowTitle; protected ProgressTaskId taskId; private boolean cancelable; private void doInEDT(Runnable runnable) { // 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 SwingUtilities.invokeLater(() -> { try { runnable.run(); } catch (RuntimeException e) { // NOPMD throw BugReport.intercept(e).put("monitor", this); } }); } private void setDialogVisible(boolean visible) { if (dialog.isVisible() != visible) { dialog.setVisible(visible); } } private ProgressMonitorDialog getDialog() { BackgroundProgressMonitor backgroundMonitor = null; MapFrame map = Main.map; if (map != null) { backgroundMonitor = map.statusLine.progressMonitor; } if (backgroundMonitor != null) { backgroundMonitor.setVisible(isInBackground); } if (dialog != null) { setDialogVisible(!isInBackground || backgroundMonitor == null); } if (isInBackground && backgroundMonitor != null) { backgroundMonitor.setVisible(true); if (dialog != null) { setDialogVisible(false); } return backgroundMonitor; } else if (backgroundMonitor != null) { backgroundMonitor.setVisible(false); if (dialog != null) { setDialogVisible(true); } return dialog; } else if (dialog != null) { setDialogVisible(true); return dialog; } else return null; } /** * Constructs a new {@code PleaseWaitProgressMonitor}. */ public PleaseWaitProgressMonitor() { this(""); } /** * Constructs a new {@code PleaseWaitProgressMonitor}. * @param windowTitle window title */ public PleaseWaitProgressMonitor(String windowTitle) { this(Main.parent); this.windowTitle = windowTitle; } /** * Constructs a new {@code PleaseWaitProgressMonitor}. * @param dialogParent component to get parent frame from */ public PleaseWaitProgressMonitor(Component dialogParent) { super(new CancelHandler()); if (GraphicsEnvironment.isHeadless()) { this.dialogParent = dialogParent; } else { this.dialogParent = GuiHelper.getFrameForComponent(dialogParent); } this.cancelable = true; } /** * Constructs a new {@code PleaseWaitProgressMonitor}. * @param dialogParent component to get parent frame from * @param windowTitle window title */ public PleaseWaitProgressMonitor(Component dialogParent, String windowTitle) { this(GuiHelper.getFrameForComponent(dialogParent)); this.windowTitle = windowTitle; } private final ActionListener cancelListener = e -> cancel(); private final ActionListener inBackgroundListener = e -> { isInBackground = true; ProgressMonitorDialog dlg = getDialog(); if (dlg != null) { reset(); dlg.setVisible(true); } }; private final WindowListener windowListener = new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { cancel(); } }; public final boolean isCancelable() { return cancelable; } public final void setCancelable(boolean cancelable) { this.cancelable = cancelable; } @Override public void doBeginTask() { doInEDT(() -> { Main.currentProgressMonitor = this; if (GraphicsEnvironment.isHeadless()) { return; } if (dialogParent != null && dialog == null) { dialog = new PleaseWaitDialog(dialogParent); } else { throw new ProgressException("PleaseWaitDialog parent must be set"); } if (windowTitle != null) { dialog.setTitle(windowTitle); } dialog.setCancelEnabled(cancelable); dialog.setCancelCallback(cancelListener); dialog.setInBackgroundCallback(inBackgroundListener); dialog.setCustomText(""); dialog.addWindowListener(windowListener); dialog.setMaximumProgress(PROGRESS_BAR_MAX); dialog.setVisible(true); }); } @Override public void doFinishTask() { // do nothing } @Override protected void updateProgress(double progressValue) { final int newValue = (int) (progressValue * PROGRESS_BAR_MAX); if (newValue != currentProgressValue) { currentProgressValue = newValue; doInEDT(() -> { ProgressMonitorDialog dlg = getDialog(); if (dlg != null) { dlg.updateProgress(currentProgressValue); } }); } } @Override protected void doSetCustomText(final String title) { checkState(State.IN_TASK, State.IN_SUBTASK); this.customText = title; doInEDT(() -> { ProgressMonitorDialog dlg = getDialog(); if (dlg != null) { dlg.setCustomText(title); } }); } @Override protected void doSetTitle(final String title) { checkState(State.IN_TASK, State.IN_SUBTASK); this.title = title; doInEDT(() -> { ProgressMonitorDialog dlg = getDialog(); if (dlg != null) { dlg.setCurrentAction(title); } }); } @Override protected void doSetIntermediate(final boolean value) { this.indeterminate = value; doInEDT(() -> { // Enable only if progress is at the beginning. Doing intermediate progress in the middle // will hide already reached progress ProgressMonitorDialog dlg = getDialog(); if (dlg != null) { dlg.setIndeterminate(value && currentProgressValue == 0); } }); } @Override public void appendLogMessage(final String message) { doInEDT(() -> { ProgressMonitorDialog dlg = getDialog(); if (dlg != null) { dlg.appendLogMessage(message); } }); } public void reset() { if (dialog != null) { dialog.setTitle(title); dialog.setCustomText(customText); dialog.updateProgress(currentProgressValue); dialog.setIndeterminate(indeterminate && currentProgressValue == 0); } BackgroundProgressMonitor backgroundMonitor = null; MapFrame map = Main.map; if (map != null) { backgroundMonitor = map.statusLine.progressMonitor; } if (backgroundMonitor != null) { backgroundMonitor.setCurrentAction(title); backgroundMonitor.setCustomText(customText); backgroundMonitor.updateProgress(currentProgressValue); backgroundMonitor.setIndeterminate(indeterminate && currentProgressValue == 0); } } public void close() { doInEDT(() -> { if (dialog != null) { dialog.setVisible(false); dialog.setCancelCallback(null); dialog.setInBackgroundCallback(null); dialog.removeWindowListener(windowListener); dialog.dispose(); dialog = null; Main.currentProgressMonitor = null; MapFrame map = Main.map; if (map != null) { map.statusLine.progressMonitor.setVisible(false); } } }); } public void showForegroundDialog() { isInBackground = false; doInEDT(() -> { if (dialog != null) { dialog.setInBackgroundPossible(taskId != null && Main.isDisplayingMapView()); reset(); getDialog(); } }); } @Override public void setProgressTaskId(ProgressTaskId taskId) { this.taskId = taskId; doInEDT(() -> { if (dialog != null) { dialog.setInBackgroundPossible(taskId != null && Main.isDisplayingMapView()); } }); } @Override public ProgressTaskId getProgressTaskId() { return taskId; } @Override public Component getWindowParent() { Component parent = dialog; if (isInBackground || parent == null) return Main.parent; else return parent; } @Override public String toString() { return "PleaseWaitProgressMonitor [currentProgressValue=" + currentProgressValue + ", customText=" + customText + ", title=" + title + ", indeterminate=" + indeterminate + ", isInBackground=" + isInBackground + ", windowTitle=" + windowTitle + ", taskId=" + taskId + ", cancelable=" + cancelable + ", state=" + state + "]"; } }