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 = 100; 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 }