001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.progress; 003 004import java.awt.Component; 005import java.awt.Dialog; 006import java.awt.Frame; 007import java.awt.Window; 008import java.awt.event.ActionEvent; 009import java.awt.event.ActionListener; 010import java.awt.event.WindowAdapter; 011import java.awt.event.WindowEvent; 012import java.awt.event.WindowListener; 013 014import javax.swing.JOptionPane; 015import javax.swing.SwingUtilities; 016 017import org.openstreetmap.josm.Main; 018import org.openstreetmap.josm.gui.MapFrame; 019import org.openstreetmap.josm.gui.MapStatus.BackgroundProgressMonitor; 020import org.openstreetmap.josm.gui.PleaseWaitDialog; 021 022public 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 @Override 119 public void actionPerformed(ActionEvent e) { 120 cancel(); 121 } 122 }; 123 124 private ActionListener inBackgroundListener = new ActionListener() { 125 @Override 126 public void actionPerformed(ActionEvent e) { 127 isInBackground = true; 128 ProgressMonitorDialog dialog = getDialog(); 129 if (dialog != null) { 130 reset(); 131 dialog.setVisible(true); 132 } 133 } 134 }; 135 136 private WindowListener windowListener = new WindowAdapter(){ 137 @Override public void windowClosing(WindowEvent e) { 138 cancel(); 139 } 140 }; 141 142 public final boolean isCancelable() { 143 return cancelable; 144 } 145 146 public final void setCancelable(boolean cancelable) { 147 this.cancelable = cancelable; 148 } 149 150 @Override 151 public void doBeginTask() { 152 doInEDT(new Runnable() { 153 @Override 154 public void run() { 155 Main.currentProgressMonitor = PleaseWaitProgressMonitor.this; 156 if (dialogParent instanceof Frame && dialog == null) { 157 dialog = new PleaseWaitDialog(dialogParent); 158 } else if (dialogParent instanceof Dialog && dialog == null) { 159 dialog = new PleaseWaitDialog(dialogParent); 160 } else 161 throw new ProgressException("PleaseWaitDialog parent must be either Frame or Dialog"); 162 163 if (windowTitle != null) { 164 dialog.setTitle(windowTitle); 165 } 166 dialog.setCancelEnabled(cancelable); 167 dialog.setCancelCallback(cancelListener); 168 dialog.setInBackgroundCallback(inBackgroundListener); 169 dialog.setCustomText(""); 170 dialog.addWindowListener(windowListener); 171 dialog.progress.setMaximum(PROGRESS_BAR_MAX); 172 dialog.setVisible(true); 173 } 174 }); 175 } 176 177 @Override 178 public void doFinishTask() { 179 // do nothing 180 } 181 182 @Override 183 protected void updateProgress(double progressValue) { 184 final int newValue = (int)(progressValue * PROGRESS_BAR_MAX); 185 if (newValue != currentProgressValue) { 186 currentProgressValue = newValue; 187 doInEDT(new Runnable() { 188 @Override 189 public void run() { 190 ProgressMonitorDialog dialog = getDialog(); 191 if (dialog != null) { 192 dialog.updateProgress(currentProgressValue); 193 } 194 } 195 }); 196 } 197 } 198 199 @Override 200 protected void doSetCustomText(final String title) { 201 checkState(State.IN_TASK, State.IN_SUBTASK); 202 this.customText = title; 203 doInEDT(new Runnable() { 204 @Override 205 public void run() { 206 ProgressMonitorDialog dialog = getDialog(); 207 if (dialog != null) { 208 dialog.setCustomText(title); 209 } 210 } 211 }); 212 } 213 214 @Override 215 protected void doSetTitle(final String title) { 216 checkState(State.IN_TASK, State.IN_SUBTASK); 217 this.title = title; 218 doInEDT(new Runnable() { 219 @Override 220 public void run() { 221 ProgressMonitorDialog dialog = getDialog(); 222 if (dialog != null) { 223 dialog.setCurrentAction(title); 224 } 225 } 226 }); 227 } 228 229 @Override 230 protected void doSetIntermediate(final boolean value) { 231 this.indeterminate = value; 232 doInEDT(new Runnable() { 233 @Override 234 public void run() { 235 // Enable only if progress is at the beginning. Doing intermediate progress in the middle 236 // will hide already reached progress 237 ProgressMonitorDialog dialog = getDialog(); 238 if (dialog != null) { 239 dialog.setIndeterminate(value && currentProgressValue == 0); 240 } 241 } 242 }); 243 } 244 245 @Override 246 public void appendLogMessage(final String message) { 247 doInEDT(new Runnable() { 248 @Override 249 public void run() { 250 ProgressMonitorDialog dialog = getDialog(); 251 if (dialog != null) { 252 dialog.appendLogMessage(message); 253 } 254 } 255 }); 256 } 257 258 public void reset() { 259 if (dialog != null) { 260 dialog.setTitle(title); 261 dialog.setCustomText(customText); 262 dialog.updateProgress(currentProgressValue); 263 dialog.setIndeterminate(indeterminate && currentProgressValue == 0); 264 } 265 BackgroundProgressMonitor backgroundMonitor = null; 266 MapFrame map = Main.map; 267 if (map != null) { 268 backgroundMonitor = map.statusLine.progressMonitor; 269 } 270 if (backgroundMonitor != null) { 271 backgroundMonitor.setCurrentAction(title); 272 backgroundMonitor.setCustomText(customText); 273 backgroundMonitor.updateProgress(currentProgressValue); 274 backgroundMonitor.setIndeterminate(indeterminate && currentProgressValue == 0); 275 } 276 277 } 278 279 public void close() { 280 doInEDT(new Runnable() { 281 @Override 282 public void run() { 283 if (dialog != null) { 284 dialog.setVisible(false); 285 dialog.setCancelCallback(null); 286 dialog.setInBackgroundCallback(null); 287 dialog.removeWindowListener(windowListener); 288 dialog.dispose(); 289 dialog = null; 290 Main.currentProgressMonitor = null; 291 MapFrame map = Main.map; 292 if (map != null) { 293 map.statusLine.progressMonitor.setVisible(false); 294 } 295 } 296 } 297 }); 298 } 299 300 public void showForegroundDialog() { 301 isInBackground = false; 302 doInEDT(new Runnable() { 303 @Override 304 public void run() { 305 if (dialog != null) { 306 dialog.setInBackgroundPossible(PleaseWaitProgressMonitor.this.taskId != null && Main.isDisplayingMapView()); 307 reset(); 308 getDialog(); 309 } 310 } 311 }); 312 313 } 314 315 @Override 316 public void setProgressTaskId(ProgressTaskId taskId) { 317 this.taskId = taskId; 318 doInEDT(new Runnable() { 319 @Override 320 public void run() { 321 if (dialog != null) { 322 dialog.setInBackgroundPossible(PleaseWaitProgressMonitor.this.taskId != null && Main.isDisplayingMapView()); 323 } 324 } 325 }); 326 } 327 328 @Override 329 public ProgressTaskId getProgressTaskId() { 330 return taskId; 331 } 332 333 334 @Override 335 public Component getWindowParent() { 336 Component parent = dialog; 337 if (isInBackground || parent == null) 338 return Main.parent; 339 else 340 return parent; 341 } 342}