source: josm/trunk/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java@ 7017

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

see #8465 - fix easy compilation warnings

  • Property svn:eol-style set to native
File size: 23.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.BorderLayout;
8import java.awt.Component;
9import java.awt.Dimension;
10import java.awt.FlowLayout;
11import java.awt.Graphics2D;
12import java.awt.GridBagConstraints;
13import java.awt.GridBagLayout;
14import java.awt.Image;
15import java.awt.event.ActionEvent;
16import java.awt.event.WindowAdapter;
17import java.awt.event.WindowEvent;
18import java.awt.image.BufferedImage;
19import java.beans.PropertyChangeEvent;
20import java.beans.PropertyChangeListener;
21import java.util.List;
22import java.util.concurrent.CancellationException;
23import java.util.concurrent.ExecutorService;
24import java.util.concurrent.Executors;
25import java.util.concurrent.Future;
26
27import javax.swing.AbstractAction;
28import javax.swing.DefaultListCellRenderer;
29import javax.swing.ImageIcon;
30import javax.swing.JButton;
31import javax.swing.JComponent;
32import javax.swing.JDialog;
33import javax.swing.JLabel;
34import javax.swing.JList;
35import javax.swing.JOptionPane;
36import javax.swing.JPanel;
37import javax.swing.JScrollPane;
38import javax.swing.KeyStroke;
39import javax.swing.WindowConstants;
40import javax.swing.event.TableModelEvent;
41import javax.swing.event.TableModelListener;
42
43import org.openstreetmap.josm.Main;
44import org.openstreetmap.josm.actions.UploadAction;
45import org.openstreetmap.josm.data.APIDataSet;
46import org.openstreetmap.josm.gui.ExceptionDialogUtil;
47import org.openstreetmap.josm.gui.io.SaveLayersModel.Mode;
48import org.openstreetmap.josm.gui.progress.ProgressMonitor;
49import org.openstreetmap.josm.gui.progress.SwingRenderingProgressMonitor;
50import org.openstreetmap.josm.gui.util.GuiHelper;
51import org.openstreetmap.josm.tools.ImageProvider;
52import org.openstreetmap.josm.tools.WindowGeometry;
53
54public class SaveLayersDialog extends JDialog implements TableModelListener {
55 public static enum UserAction {
56 /** save/upload layers was successful, proceed with operation */
57 PROCEED,
58 /** save/upload of layers was not successful or user canceled operation */
59 CANCEL
60 }
61
62 private SaveLayersModel model;
63 private UserAction action = UserAction.CANCEL;
64 private UploadAndSaveProgressRenderer pnlUploadLayers;
65
66 private SaveAndProceedAction saveAndProceedAction;
67 private DiscardAndProceedAction discardAndProceedAction;
68 private CancelAction cancelAction;
69 private SaveAndUploadTask saveAndUploadTask;
70
71 /**
72 * builds the GUI
73 */
74 protected void build() {
75 WindowGeometry geometry = WindowGeometry.centerOnScreen(new Dimension(650,300));
76 geometry.applySafe(this);
77 getContentPane().setLayout(new BorderLayout());
78
79 model = new SaveLayersModel();
80 SaveLayersTable table = new SaveLayersTable(model);
81 JScrollPane pane = new JScrollPane(table);
82 model.addPropertyChangeListener(table);
83 table.getModel().addTableModelListener(this);
84
85 getContentPane().add(pane, BorderLayout.CENTER);
86 getContentPane().add(buildButtonRow(), BorderLayout.SOUTH);
87
88 addWindowListener(new WindowClosingAdapter());
89 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
90 }
91
92 private JButton saveAndProceedActionButton = null;
93
94 /**
95 * builds the button row
96 *
97 * @return the panel with the button row
98 */
99 protected JPanel buildButtonRow() {
100 JPanel pnl = new JPanel();
101 pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
102
103 saveAndProceedAction = new SaveAndProceedAction();
104 model.addPropertyChangeListener(saveAndProceedAction);
105 pnl.add(saveAndProceedActionButton = new JButton(saveAndProceedAction));
106
107 discardAndProceedAction = new DiscardAndProceedAction();
108 model.addPropertyChangeListener(discardAndProceedAction);
109 pnl.add(new JButton(discardAndProceedAction));
110
111 cancelAction = new CancelAction();
112 pnl.add(new JButton(cancelAction));
113
114 JPanel pnl2 = new JPanel();
115 pnl2.setLayout(new BorderLayout());
116 pnl2.add(pnlUploadLayers = new UploadAndSaveProgressRenderer(), BorderLayout.CENTER);
117 model.addPropertyChangeListener(pnlUploadLayers);
118 pnl2.add(pnl, BorderLayout.SOUTH);
119 return pnl2;
120 }
121
122 public void prepareForSavingAndUpdatingLayersBeforeExit() {
123 setTitle(tr("Unsaved changes - Save/Upload before exiting?"));
124 this.saveAndProceedAction.initForSaveAndExit();
125 this.discardAndProceedAction.initForDiscardAndExit();
126 }
127
128 public void prepareForSavingAndUpdatingLayersBeforeDelete() {
129 setTitle(tr("Unsaved changes - Save/Upload before deleting?"));
130 this.saveAndProceedAction.initForSaveAndDelete();
131 this.discardAndProceedAction.initForDiscardAndDelete();
132 }
133
134 public SaveLayersDialog(Component parent) {
135 super(JOptionPane.getFrameForComponent(parent), ModalityType.DOCUMENT_MODAL);
136 build();
137 }
138
139 public UserAction getUserAction() {
140 return this.action;
141 }
142
143 public SaveLayersModel getModel() {
144 return model;
145 }
146
147 protected void launchSafeAndUploadTask() {
148 ProgressMonitor monitor = new SwingRenderingProgressMonitor(pnlUploadLayers);
149 monitor.beginTask(tr("Uploading and saving modified layers ..."));
150 this.saveAndUploadTask = new SaveAndUploadTask(model, monitor);
151 new Thread(saveAndUploadTask).start();
152 }
153
154 protected void cancelSafeAndUploadTask() {
155 if (this.saveAndUploadTask != null) {
156 this.saveAndUploadTask.cancel();
157 }
158 model.setMode(Mode.EDITING_DATA);
159 }
160
161 private static class LayerListWarningMessagePanel extends JPanel {
162 private JLabel lblMessage;
163 private JList<SaveLayerInfo> lstLayers;
164
165 protected void build() {
166 setLayout(new GridBagLayout());
167 GridBagConstraints gc = new GridBagConstraints();
168 gc.gridx = 0;
169 gc.gridy = 0;
170 gc.fill = GridBagConstraints.HORIZONTAL;
171 gc.weightx = 1.0;
172 gc.weighty = 0.0;
173 add(lblMessage = new JLabel(), gc);
174 lblMessage.setHorizontalAlignment(JLabel.LEFT);
175 lstLayers = new JList<>();
176 lstLayers.setCellRenderer(
177 new DefaultListCellRenderer() {
178 @Override
179 public Component getListCellRendererComponent(JList list, Object value, int index,
180 boolean isSelected, boolean cellHasFocus) {
181 SaveLayerInfo info = (SaveLayerInfo)value;
182 setIcon(info.getLayer().getIcon());
183 setText(info.getName());
184 return this;
185 }
186 }
187 );
188 gc.gridx = 0;
189 gc.gridy = 1;
190 gc.fill = GridBagConstraints.HORIZONTAL;
191 gc.weightx = 1.0;
192 gc.weighty = 1.0;
193 add(lstLayers,gc);
194 }
195
196 public LayerListWarningMessagePanel(String msg, List<SaveLayerInfo> infos) {
197 build();
198 lblMessage.setText(msg);
199 lstLayers.setListData(infos.toArray(new SaveLayerInfo[0]));
200 }
201 }
202
203 protected void warnLayersWithConflictsAndUploadRequest(List<SaveLayerInfo> infos) {
204 String msg = trn("<html>{0} layer has unresolved conflicts.<br>"
205 + "Either resolve them first or discard the modifications.<br>"
206 + "Layer with conflicts:</html>",
207 "<html>{0} layers have unresolved conflicts.<br>"
208 + "Either resolve them first or discard the modifications.<br>"
209 + "Layers with conflicts:</html>",
210 infos.size(),
211 infos.size());
212 JOptionPane.showConfirmDialog(
213 Main.parent,
214 new LayerListWarningMessagePanel(msg, infos),
215 tr("Unsaved data and conflicts"),
216 JOptionPane.DEFAULT_OPTION,
217 JOptionPane.WARNING_MESSAGE
218 );
219 }
220
221 protected void warnLayersWithoutFilesAndSaveRequest(List<SaveLayerInfo> infos) {
222 String msg = trn("<html>{0} layer needs saving but has no associated file.<br>"
223 + "Either select a file for this layer or discard the changes.<br>"
224 + "Layer without a file:</html>",
225 "<html>{0} layers need saving but have no associated file.<br>"
226 + "Either select a file for each of them or discard the changes.<br>"
227 + "Layers without a file:</html>",
228 infos.size(),
229 infos.size());
230 JOptionPane.showConfirmDialog(
231 Main.parent,
232 new LayerListWarningMessagePanel(msg, infos),
233 tr("Unsaved data and missing associated file"),
234 JOptionPane.DEFAULT_OPTION,
235 JOptionPane.WARNING_MESSAGE
236 );
237 }
238
239 protected void warnLayersWithIllegalFilesAndSaveRequest(List<SaveLayerInfo> infos) {
240 String msg = trn("<html>{0} layer needs saving but has an associated file<br>"
241 + "which cannot be written.<br>"
242 + "Either select another file for this layer or discard the changes.<br>"
243 + "Layer with a non-writable file:</html>",
244 "<html>{0} layers need saving but have associated files<br>"
245 + "which cannot be written.<br>"
246 + "Either select another file for each of them or discard the changes.<br>"
247 + "Layers with non-writable files:</html>",
248 infos.size(),
249 infos.size());
250 JOptionPane.showConfirmDialog(
251 Main.parent,
252 new LayerListWarningMessagePanel(msg, infos),
253 tr("Unsaved data non-writable files"),
254 JOptionPane.DEFAULT_OPTION,
255 JOptionPane.WARNING_MESSAGE
256 );
257 }
258
259 protected boolean confirmSaveLayerInfosOK() {
260 List<SaveLayerInfo> layerInfos = model.getLayersWithConflictsAndUploadRequest();
261 if (!layerInfos.isEmpty()) {
262 warnLayersWithConflictsAndUploadRequest(layerInfos);
263 return false;
264 }
265
266 layerInfos = model.getLayersWithoutFilesAndSaveRequest();
267 if (!layerInfos.isEmpty()) {
268 warnLayersWithoutFilesAndSaveRequest(layerInfos);
269 return false;
270 }
271
272 layerInfos = model.getLayersWithIllegalFilesAndSaveRequest();
273 if (!layerInfos.isEmpty()) {
274 warnLayersWithIllegalFilesAndSaveRequest(layerInfos);
275 return false;
276 }
277
278 return true;
279 }
280
281 protected void setUserAction(UserAction action) {
282 this.action = action;
283 }
284
285 /**
286 * Closes this dialog and frees all native screen resources.
287 */
288 public void closeDialog() {
289 setVisible(false);
290 dispose();
291 }
292
293 class WindowClosingAdapter extends WindowAdapter {
294 @Override
295 public void windowClosing(WindowEvent e) {
296 cancelAction.cancel();
297 }
298 }
299
300 class CancelAction extends AbstractAction {
301 public CancelAction() {
302 putValue(NAME, tr("Cancel"));
303 putValue(SHORT_DESCRIPTION, tr("Close this dialog and resume editing in JOSM"));
304 putValue(SMALL_ICON, ImageProvider.get("cancel"));
305 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
306 .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
307 getRootPane().getActionMap().put("ESCAPE", this);
308 }
309
310 protected void cancelWhenInEditingModel() {
311 setUserAction(UserAction.CANCEL);
312 closeDialog();
313 }
314
315 protected void cancelWhenInSaveAndUploadingMode() {
316 cancelSafeAndUploadTask();
317 }
318
319 public void cancel() {
320 switch(model.getMode()) {
321 case EDITING_DATA: cancelWhenInEditingModel(); break;
322 case UPLOADING_AND_SAVING: cancelSafeAndUploadTask(); break;
323 }
324 }
325
326 @Override
327 public void actionPerformed(ActionEvent e) {
328 cancel();
329 }
330 }
331
332 class DiscardAndProceedAction extends AbstractAction implements PropertyChangeListener {
333 public DiscardAndProceedAction() {
334 initForDiscardAndExit();
335 }
336
337 public void initForDiscardAndExit() {
338 putValue(NAME, tr("Exit now!"));
339 putValue(SHORT_DESCRIPTION, tr("Exit JOSM without saving. Unsaved changes are lost."));
340 putValue(SMALL_ICON, ImageProvider.get("exit"));
341 }
342
343 public void initForDiscardAndDelete() {
344 putValue(NAME, tr("Delete now!"));
345 putValue(SHORT_DESCRIPTION, tr("Delete layers without saving. Unsaved changes are lost."));
346 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
347 }
348
349 @Override
350 public void actionPerformed(ActionEvent e) {
351 setUserAction(UserAction.PROCEED);
352 closeDialog();
353 }
354 @Override
355 public void propertyChange(PropertyChangeEvent evt) {
356 if (evt.getPropertyName().equals(SaveLayersModel.MODE_PROP)) {
357 Mode mode = (Mode)evt.getNewValue();
358 switch(mode) {
359 case EDITING_DATA: setEnabled(true); break;
360 case UPLOADING_AND_SAVING: setEnabled(false); break;
361 }
362 }
363 }
364 }
365
366 final class SaveAndProceedAction extends AbstractAction implements PropertyChangeListener {
367 private static final int is = 24; // icon size
368 private static final String BASE_ICON = "BASE_ICON";
369 private final Image save = ImageProvider.get("save").getImage();
370 private final Image upld = ImageProvider.get("upload").getImage();
371 private final Image saveDis = new BufferedImage(is, is, BufferedImage.TYPE_4BYTE_ABGR);
372 private final Image upldDis = new BufferedImage(is, is, BufferedImage.TYPE_4BYTE_ABGR);
373
374 public SaveAndProceedAction() {
375 // get disabled versions of icons
376 new JLabel(ImageProvider.get("save")).getDisabledIcon().paintIcon(new JPanel(), saveDis.getGraphics(), 0, 0);
377 new JLabel(ImageProvider.get("upload")).getDisabledIcon().paintIcon(new JPanel(), upldDis.getGraphics(), 0, 0);
378 initForSaveAndExit();
379 }
380
381 public void initForSaveAndExit() {
382 putValue(NAME, tr("Perform actions before exiting"));
383 putValue(SHORT_DESCRIPTION, tr("Exit JOSM with saving. Unsaved changes are uploaded and/or saved."));
384 putValue(BASE_ICON, ImageProvider.get("exit"));
385 redrawIcon();
386 }
387
388 public void initForSaveAndDelete() {
389 putValue(NAME, tr("Perform actions before deleting"));
390 putValue(SHORT_DESCRIPTION, tr("Save/Upload layers before deleting. Unsaved changes are not lost."));
391 putValue(BASE_ICON, ImageProvider.get("dialogs", "delete"));
392 redrawIcon();
393 }
394
395 public void redrawIcon() {
396 try { // Can fail if model is not yet setup properly
397 Image base = ((ImageIcon) getValue(BASE_ICON)).getImage();
398 BufferedImage newIco = new BufferedImage(is*3, is, BufferedImage.TYPE_4BYTE_ABGR);
399 Graphics2D g = newIco.createGraphics();
400 g.drawImage(model.getLayersToUpload().isEmpty() ? upldDis : upld, is*0, 0, is, is, null);
401 g.drawImage(model.getLayersToSave().isEmpty() ? saveDis : save, is*1, 0, is, is, null);
402 g.drawImage(base, is*2, 0, is, is, null);
403 putValue(SMALL_ICON, new ImageIcon(newIco));
404 } catch(Exception e) {
405 putValue(SMALL_ICON, getValue(BASE_ICON));
406 }
407 }
408
409 @Override
410 public void actionPerformed(ActionEvent e) {
411 if (! confirmSaveLayerInfosOK())
412 return;
413 launchSafeAndUploadTask();
414 }
415
416 @Override
417 public void propertyChange(PropertyChangeEvent evt) {
418 if (evt.getPropertyName().equals(SaveLayersModel.MODE_PROP)) {
419 SaveLayersModel.Mode mode = (SaveLayersModel.Mode)evt.getNewValue();
420 switch(mode) {
421 case EDITING_DATA: setEnabled(true); break;
422 case UPLOADING_AND_SAVING: setEnabled(false); break;
423 }
424 }
425 }
426 }
427
428 /**
429 * This is the asynchronous task which uploads modified layers to the server and
430 * saves them to files, if requested by the user.
431 *
432 */
433 protected class SaveAndUploadTask implements Runnable {
434
435 private SaveLayersModel model;
436 private ProgressMonitor monitor;
437 private ExecutorService worker;
438 private boolean canceled;
439 private Future<?> currentFuture;
440 private AbstractIOTask currentTask;
441
442 public SaveAndUploadTask(SaveLayersModel model, ProgressMonitor monitor) {
443 this.model = model;
444 this.monitor = monitor;
445 this.worker = Executors.newSingleThreadExecutor();
446 }
447
448 protected void uploadLayers(List<SaveLayerInfo> toUpload) {
449 for (final SaveLayerInfo layerInfo: toUpload) {
450 if (canceled) {
451 model.setUploadState(layerInfo.getLayer(), UploadOrSaveState.CANCELED);
452 continue;
453 }
454 monitor.subTask(tr("Preparing layer ''{0}'' for upload ...", layerInfo.getName()));
455
456 if (!new UploadAction().checkPreUploadConditions(layerInfo.getLayer())) {
457 model.setUploadState(layerInfo.getLayer(), UploadOrSaveState.FAILED);
458 continue;
459 }
460 final UploadDialog dialog = UploadDialog.getUploadDialog();
461 dialog.setUploadedPrimitives(new APIDataSet(layerInfo.getLayer().data));
462 dialog.setVisible(true);
463 if (dialog.isCanceled()) {
464 model.setUploadState(layerInfo.getLayer(), UploadOrSaveState.CANCELED);
465 continue;
466 }
467 dialog.rememberUserInput();
468
469 currentTask = new UploadLayerTask(
470 UploadDialog.getUploadDialog().getUploadStrategySpecification(),
471 layerInfo.getLayer(),
472 monitor,
473 UploadDialog.getUploadDialog().getChangeset()
474 );
475 currentFuture = worker.submit(currentTask);
476 try {
477 // wait for the asynchronous task to complete
478 //
479 currentFuture.get();
480 } catch(CancellationException e) {
481 model.setUploadState(layerInfo.getLayer(), UploadOrSaveState.CANCELED);
482 } catch(Exception e) {
483 Main.error(e);
484 model.setUploadState(layerInfo.getLayer(), UploadOrSaveState.FAILED);
485 ExceptionDialogUtil.explainException(e);
486 }
487 if (currentTask.isCanceled()) {
488 model.setUploadState(layerInfo.getLayer(), UploadOrSaveState.CANCELED);
489 } else if (currentTask.isFailed()) {
490 Main.error(currentTask.getLastException());
491 ExceptionDialogUtil.explainException(currentTask.getLastException());
492 model.setUploadState(layerInfo.getLayer(), UploadOrSaveState.FAILED);
493 } else {
494 model.setUploadState(layerInfo.getLayer(), UploadOrSaveState.OK);
495 }
496 currentTask = null;
497 currentFuture = null;
498 }
499 }
500
501 protected void saveLayers(List<SaveLayerInfo> toSave) {
502 for (final SaveLayerInfo layerInfo: toSave) {
503 if (canceled) {
504 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.CANCELED);
505 continue;
506 }
507 currentTask= new SaveLayerTask(layerInfo, monitor);
508 currentFuture = worker.submit(currentTask);
509
510 try {
511 // wait for the asynchronous task to complete
512 //
513 currentFuture.get();
514 } catch(CancellationException e) {
515 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.CANCELED);
516 } catch(Exception e) {
517 Main.error(e);
518 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.FAILED);
519 ExceptionDialogUtil.explainException(e);
520 }
521 if (currentTask.isCanceled()) {
522 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.CANCELED);
523 } else if (currentTask.isFailed()) {
524 if (currentTask.getLastException() != null) {
525 Main.error(currentTask.getLastException());
526 ExceptionDialogUtil.explainException(currentTask.getLastException());
527 }
528 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.FAILED);
529 } else {
530 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.OK);
531 }
532 this.currentTask = null;
533 this.currentFuture = null;
534 }
535 }
536
537 protected void warnBecauseOfUnsavedData() {
538 int numProblems = model.getNumCancel() + model.getNumFailed();
539 if (numProblems == 0) return;
540 String msg = trn(
541 "<html>An upload and/or save operation of one layer with modifications<br>"
542 + "was canceled or has failed.</html>",
543 "<html>Upload and/or save operations of {0} layers with modifications<br>"
544 + "were canceled or have failed.</html>",
545 numProblems,
546 numProblems
547 );
548 JOptionPane.showMessageDialog(
549 Main.parent,
550 msg,
551 tr("Incomplete upload and/or save"),
552 JOptionPane.WARNING_MESSAGE
553 );
554 }
555
556 @Override
557 public void run() {
558 GuiHelper.runInEDTAndWait(new Runnable() {
559 @Override
560 public void run() {
561 model.setMode(SaveLayersModel.Mode.UPLOADING_AND_SAVING);
562 List<SaveLayerInfo> toUpload = model.getLayersToUpload();
563 if (!toUpload.isEmpty()) {
564 uploadLayers(toUpload);
565 }
566 List<SaveLayerInfo> toSave = model.getLayersToSave();
567 if (!toSave.isEmpty()) {
568 saveLayers(toSave);
569 }
570 model.setMode(SaveLayersModel.Mode.EDITING_DATA);
571 if (model.hasUnsavedData()) {
572 warnBecauseOfUnsavedData();
573 model.setMode(Mode.EDITING_DATA);
574 if (canceled) {
575 setUserAction(UserAction.CANCEL);
576 closeDialog();
577 }
578 } else {
579 setUserAction(UserAction.PROCEED);
580 closeDialog();
581 }
582 }
583 });
584 }
585
586 public void cancel() {
587 if (currentTask != null) {
588 currentTask.cancel();
589 }
590 canceled = true;
591 }
592 }
593
594 @Override
595 public void tableChanged(TableModelEvent arg0) {
596 boolean dis = model.getLayersToSave().isEmpty() && model.getLayersToUpload().isEmpty();
597 if(saveAndProceedActionButton != null) {
598 saveAndProceedActionButton.setEnabled(!dis);
599 }
600 saveAndProceedAction.redrawIcon();
601 }
602}
Note: See TracBrowser for help on using the repository browser.