source: josm/trunk/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.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: 28.0 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.Graphics2D;
11import java.awt.GraphicsEnvironment;
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.ArrayList;
22import java.util.List;
23import java.util.concurrent.CancellationException;
24import java.util.concurrent.ExecutionException;
25import java.util.concurrent.ExecutorService;
26import java.util.concurrent.Executors;
27import java.util.concurrent.Future;
28
29import javax.swing.AbstractAction;
30import javax.swing.DefaultListCellRenderer;
31import javax.swing.ImageIcon;
32import javax.swing.JButton;
33import javax.swing.JDialog;
34import javax.swing.JLabel;
35import javax.swing.JList;
36import javax.swing.JOptionPane;
37import javax.swing.JPanel;
38import javax.swing.JScrollPane;
39import javax.swing.ListCellRenderer;
40import javax.swing.WindowConstants;
41import javax.swing.event.TableModelEvent;
42import javax.swing.event.TableModelListener;
43
44import org.openstreetmap.josm.Main;
45import org.openstreetmap.josm.actions.SessionSaveAsAction;
46import org.openstreetmap.josm.actions.UploadAction;
47import org.openstreetmap.josm.gui.ExceptionDialogUtil;
48import org.openstreetmap.josm.gui.io.SaveLayersModel.Mode;
49import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
50import org.openstreetmap.josm.gui.layer.Layer;
51import org.openstreetmap.josm.gui.progress.ProgressMonitor;
52import org.openstreetmap.josm.gui.progress.swing.SwingRenderingProgressMonitor;
53import org.openstreetmap.josm.gui.util.GuiHelper;
54import org.openstreetmap.josm.tools.GBC;
55import org.openstreetmap.josm.tools.ImageProvider;
56import org.openstreetmap.josm.tools.InputMapUtils;
57import org.openstreetmap.josm.tools.Logging;
58import org.openstreetmap.josm.tools.UserCancelException;
59import org.openstreetmap.josm.tools.Utils;
60import org.openstreetmap.josm.tools.WindowGeometry;
61
62/**
63 * Dialog that pops up when the user closes a layer with modified data.
64 *
65 * It asks for confirmation that all modification should be discarded and offers
66 * to save the layers to file or upload to server, depending on the type of layer.
67 */
68public class SaveLayersDialog extends JDialog implements TableModelListener {
69
70 /**
71 * The cause for requesting an action on unsaved modifications
72 */
73 public enum Reason {
74 /** deleting a layer */
75 DELETE,
76 /** exiting JOSM */
77 EXIT,
78 /* restarting JOSM */
79 RESTART
80 }
81
82 private enum UserAction {
83 /** save/upload layers was successful, proceed with operation */
84 PROCEED,
85 /** save/upload of layers was not successful or user canceled operation */
86 CANCEL
87 }
88
89 private final SaveLayersModel model = new SaveLayersModel();
90 private UserAction action = UserAction.CANCEL;
91 private final UploadAndSaveProgressRenderer pnlUploadLayers = new UploadAndSaveProgressRenderer();
92
93 private final SaveAndProceedAction saveAndProceedAction = new SaveAndProceedAction();
94 private final SaveSessionAction saveSessionAction = new SaveSessionAction();
95 private final DiscardAndProceedAction discardAndProceedAction = new DiscardAndProceedAction();
96 private final CancelAction cancelAction = new CancelAction();
97 private transient SaveAndUploadTask saveAndUploadTask;
98
99 private final JButton saveAndProceedActionButton = new JButton(saveAndProceedAction);
100
101 /**
102 * Asks user to perform "save layer" operations (save on disk and/or upload data to server) before data layers deletion.
103 *
104 * @param selectedLayers The layers to check. Only instances of {@link AbstractModifiableLayer} are considered.
105 * @param reason the cause for requesting an action on unsaved modifications
106 * @return {@code true} if there was nothing to save, or if the user wants to proceed to save operations.
107 * {@code false} if the user cancels.
108 * @since 11093
109 */
110 public static boolean saveUnsavedModifications(Iterable<? extends Layer> selectedLayers, Reason reason) {
111 if (!GraphicsEnvironment.isHeadless()) {
112 SaveLayersDialog dialog = new SaveLayersDialog(Main.parent);
113 List<AbstractModifiableLayer> layersWithUnmodifiedChanges = new ArrayList<>();
114 for (Layer l: selectedLayers) {
115 if (!(l instanceof AbstractModifiableLayer)) {
116 continue;
117 }
118 AbstractModifiableLayer odl = (AbstractModifiableLayer) l;
119 if (odl.isModified() &&
120 ((!odl.isSavable() && !odl.isUploadable()) ||
121 odl.requiresSaveToFile() ||
122 (odl.requiresUploadToServer() && !odl.isUploadDiscouraged()))) {
123 layersWithUnmodifiedChanges.add(odl);
124 }
125 }
126 dialog.prepareForSavingAndUpdatingLayers(reason);
127 if (!layersWithUnmodifiedChanges.isEmpty()) {
128 dialog.getModel().populate(layersWithUnmodifiedChanges);
129 dialog.setVisible(true);
130 switch(dialog.getUserAction()) {
131 case PROCEED: return true;
132 case CANCEL:
133 default: return false;
134 }
135 }
136 }
137
138 return true;
139 }
140
141 /**
142 * Constructs a new {@code SaveLayersDialog}.
143 * @param parent parent component
144 */
145 public SaveLayersDialog(Component parent) {
146 super(GuiHelper.getFrameForComponent(parent), ModalityType.DOCUMENT_MODAL);
147 build();
148 }
149
150 /**
151 * builds the GUI
152 */
153 protected void build() {
154 WindowGeometry geometry = WindowGeometry.centerOnScreen(new Dimension(650, 300));
155 geometry.applySafe(this);
156 getContentPane().setLayout(new BorderLayout());
157
158 SaveLayersTable table = new SaveLayersTable(model);
159 JScrollPane pane = new JScrollPane(table);
160 model.addPropertyChangeListener(table);
161 table.getModel().addTableModelListener(this);
162
163 getContentPane().add(pane, BorderLayout.CENTER);
164 getContentPane().add(buildButtonRow(), BorderLayout.SOUTH);
165
166 addWindowListener(new WindowClosingAdapter());
167 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
168 }
169
170 /**
171 * builds the button row
172 *
173 * @return the panel with the button row
174 */
175 protected JPanel buildButtonRow() {
176 JPanel pnl = new JPanel(new GridBagLayout());
177
178 model.addPropertyChangeListener(saveAndProceedAction);
179 pnl.add(saveAndProceedActionButton, GBC.std(0, 0).insets(5, 5, 0, 0).fill(GBC.HORIZONTAL));
180
181 pnl.add(new JButton(saveSessionAction), GBC.std(1, 0).insets(5, 5, 5, 0).fill(GBC.HORIZONTAL));
182
183 model.addPropertyChangeListener(discardAndProceedAction);
184 pnl.add(new JButton(discardAndProceedAction), GBC.std(0, 1).insets(5, 5, 0, 5).fill(GBC.HORIZONTAL));
185
186 pnl.add(new JButton(cancelAction), GBC.std(1, 1).insets(5, 5, 5, 5).fill(GBC.HORIZONTAL));
187
188 JPanel pnl2 = new JPanel(new BorderLayout());
189 pnl2.add(pnlUploadLayers, BorderLayout.CENTER);
190 model.addPropertyChangeListener(pnlUploadLayers);
191 pnl2.add(pnl, BorderLayout.SOUTH);
192 return pnl2;
193 }
194
195 public void prepareForSavingAndUpdatingLayers(final Reason reason) {
196 switch (reason) {
197 case EXIT:
198 setTitle(tr("Unsaved changes - Save/Upload before exiting?"));
199 break;
200 case DELETE:
201 setTitle(tr("Unsaved changes - Save/Upload before deleting?"));
202 break;
203 case RESTART:
204 setTitle(tr("Unsaved changes - Save/Upload before restarting?"));
205 break;
206 }
207 this.saveAndProceedAction.initForReason(reason);
208 this.discardAndProceedAction.initForReason(reason);
209 }
210
211 public UserAction getUserAction() {
212 return this.action;
213 }
214
215 public SaveLayersModel getModel() {
216 return model;
217 }
218
219 protected void launchSafeAndUploadTask() {
220 ProgressMonitor monitor = new SwingRenderingProgressMonitor(pnlUploadLayers);
221 monitor.beginTask(tr("Uploading and saving modified layers ..."));
222 this.saveAndUploadTask = new SaveAndUploadTask(model, monitor);
223 new Thread(saveAndUploadTask, saveAndUploadTask.getClass().getName()).start();
224 }
225
226 protected void cancelSafeAndUploadTask() {
227 if (this.saveAndUploadTask != null) {
228 this.saveAndUploadTask.cancel();
229 }
230 model.setMode(Mode.EDITING_DATA);
231 }
232
233 private static class LayerListWarningMessagePanel extends JPanel {
234 static final class LayerCellRenderer implements ListCellRenderer<SaveLayerInfo> {
235 private final DefaultListCellRenderer def = new DefaultListCellRenderer();
236
237 @Override
238 public Component getListCellRendererComponent(JList<? extends SaveLayerInfo> list, SaveLayerInfo info, int index,
239 boolean isSelected, boolean cellHasFocus) {
240 def.setIcon(info.getLayer().getIcon());
241 def.setText(info.getName());
242 return def;
243 }
244 }
245
246 private final JLabel lblMessage = new JLabel();
247 private final JList<SaveLayerInfo> lstLayers = new JList<>();
248
249 LayerListWarningMessagePanel(String msg, List<SaveLayerInfo> infos) {
250 super(new GridBagLayout());
251 build();
252 lblMessage.setText(msg);
253 lstLayers.setListData(infos.toArray(new SaveLayerInfo[infos.size()]));
254 }
255
256 protected void build() {
257 GridBagConstraints gc = new GridBagConstraints();
258 gc.gridx = 0;
259 gc.gridy = 0;
260 gc.fill = GridBagConstraints.HORIZONTAL;
261 gc.weightx = 1.0;
262 gc.weighty = 0.0;
263 add(lblMessage, gc);
264 lblMessage.setHorizontalAlignment(JLabel.LEFT);
265 lstLayers.setCellRenderer(new LayerCellRenderer());
266 gc.gridx = 0;
267 gc.gridy = 1;
268 gc.fill = GridBagConstraints.HORIZONTAL;
269 gc.weightx = 1.0;
270 gc.weighty = 1.0;
271 add(lstLayers, gc);
272 }
273 }
274
275 private static void warn(String msg, List<SaveLayerInfo> infos, String title) {
276 JPanel panel = new LayerListWarningMessagePanel(msg, infos);
277 // For unit test coverage in headless mode
278 if (!GraphicsEnvironment.isHeadless()) {
279 JOptionPane.showConfirmDialog(Main.parent, panel, title, JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE);
280 }
281 }
282
283 protected static void warnLayersWithConflictsAndUploadRequest(List<SaveLayerInfo> infos) {
284 warn(trn("<html>{0} layer has unresolved conflicts.<br>"
285 + "Either resolve them first or discard the modifications.<br>"
286 + "Layer with conflicts:</html>",
287 "<html>{0} layers have unresolved conflicts.<br>"
288 + "Either resolve them first or discard the modifications.<br>"
289 + "Layers with conflicts:</html>",
290 infos.size(),
291 infos.size()),
292 infos, tr("Unsaved data and conflicts"));
293 }
294
295 protected static void warnLayersWithoutFilesAndSaveRequest(List<SaveLayerInfo> infos) {
296 warn(trn("<html>{0} layer needs saving but has no associated file.<br>"
297 + "Either select a file for this layer or discard the changes.<br>"
298 + "Layer without a file:</html>",
299 "<html>{0} layers need saving but have no associated file.<br>"
300 + "Either select a file for each of them or discard the changes.<br>"
301 + "Layers without a file:</html>",
302 infos.size(),
303 infos.size()),
304 infos, tr("Unsaved data and missing associated file"));
305 }
306
307 protected static void warnLayersWithIllegalFilesAndSaveRequest(List<SaveLayerInfo> infos) {
308 warn(trn("<html>{0} layer needs saving but has an associated file<br>"
309 + "which cannot be written.<br>"
310 + "Either select another file for this layer or discard the changes.<br>"
311 + "Layer with a non-writable file:</html>",
312 "<html>{0} layers need saving but have associated files<br>"
313 + "which cannot be written.<br>"
314 + "Either select another file for each of them or discard the changes.<br>"
315 + "Layers with non-writable files:</html>",
316 infos.size(),
317 infos.size()),
318 infos, tr("Unsaved data non-writable files"));
319 }
320
321 static boolean confirmSaveLayerInfosOK(SaveLayersModel model) {
322 List<SaveLayerInfo> layerInfos = model.getLayersWithConflictsAndUploadRequest();
323 if (!layerInfos.isEmpty()) {
324 warnLayersWithConflictsAndUploadRequest(layerInfos);
325 return false;
326 }
327
328 layerInfos = model.getLayersWithoutFilesAndSaveRequest();
329 if (!layerInfos.isEmpty()) {
330 warnLayersWithoutFilesAndSaveRequest(layerInfos);
331 return false;
332 }
333
334 layerInfos = model.getLayersWithIllegalFilesAndSaveRequest();
335 if (!layerInfos.isEmpty()) {
336 warnLayersWithIllegalFilesAndSaveRequest(layerInfos);
337 return false;
338 }
339
340 return true;
341 }
342
343 protected void setUserAction(UserAction action) {
344 this.action = action;
345 }
346
347 /**
348 * Closes this dialog and frees all native screen resources.
349 */
350 public void closeDialog() {
351 setVisible(false);
352 dispose();
353 }
354
355 class WindowClosingAdapter extends WindowAdapter {
356 @Override
357 public void windowClosing(WindowEvent e) {
358 cancelAction.cancel();
359 }
360 }
361
362 class CancelAction extends AbstractAction {
363 CancelAction() {
364 putValue(NAME, tr("Cancel"));
365 putValue(SHORT_DESCRIPTION, tr("Close this dialog and resume editing in JOSM"));
366 putValue(SMALL_ICON, ImageProvider.get("cancel"));
367 InputMapUtils.addEscapeAction(getRootPane(), this);
368 }
369
370 protected void cancelWhenInEditingModel() {
371 setUserAction(UserAction.CANCEL);
372 closeDialog();
373 }
374
375 public void cancel() {
376 switch(model.getMode()) {
377 case EDITING_DATA: cancelWhenInEditingModel();
378 break;
379 case UPLOADING_AND_SAVING: cancelSafeAndUploadTask();
380 break;
381 }
382 }
383
384 @Override
385 public void actionPerformed(ActionEvent e) {
386 cancel();
387 }
388 }
389
390 class DiscardAndProceedAction extends AbstractAction implements PropertyChangeListener {
391 DiscardAndProceedAction() {
392 initForReason(Reason.EXIT);
393 }
394
395 public void initForReason(Reason reason) {
396 switch (reason) {
397 case EXIT:
398 putValue(NAME, tr("Exit now!"));
399 putValue(SHORT_DESCRIPTION, tr("Exit JOSM without saving. Unsaved changes are lost."));
400 putValue(SMALL_ICON, ImageProvider.get("exit"));
401 break;
402 case RESTART:
403 putValue(NAME, tr("Restart now!"));
404 putValue(SHORT_DESCRIPTION, tr("Restart JOSM without saving. Unsaved changes are lost."));
405 putValue(SMALL_ICON, ImageProvider.get("restart"));
406 break;
407 case DELETE:
408 putValue(NAME, tr("Delete now!"));
409 putValue(SHORT_DESCRIPTION, tr("Delete layers without saving. Unsaved changes are lost."));
410 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
411 break;
412 }
413
414 }
415
416 @Override
417 public void actionPerformed(ActionEvent e) {
418 setUserAction(UserAction.PROCEED);
419 closeDialog();
420 }
421
422 @Override
423 public void propertyChange(PropertyChangeEvent evt) {
424 if (evt.getPropertyName().equals(SaveLayersModel.MODE_PROP)) {
425 Mode mode = (Mode) evt.getNewValue();
426 switch(mode) {
427 case EDITING_DATA: setEnabled(true);
428 break;
429 case UPLOADING_AND_SAVING: setEnabled(false);
430 break;
431 }
432 }
433 }
434 }
435
436 class SaveSessionAction extends SessionSaveAsAction {
437
438 SaveSessionAction() {
439 super(false, false);
440 }
441
442 @Override
443 public void actionPerformed(ActionEvent e) {
444 try {
445 saveSession();
446 setUserAction(UserAction.PROCEED);
447 closeDialog();
448 } catch (UserCancelException ignore) {
449 Logging.trace(ignore);
450 }
451 }
452 }
453
454 final class SaveAndProceedAction extends AbstractAction implements PropertyChangeListener {
455 private static final int ICON_SIZE = 24;
456 private static final String BASE_ICON = "BASE_ICON";
457 private final transient Image save = getImage("save", false);
458 private final transient Image upld = getImage("upload", false);
459 private final transient Image saveDis = getImage("save", true);
460 private final transient Image upldDis = getImage("upload", true);
461
462 SaveAndProceedAction() {
463 initForReason(Reason.EXIT);
464 }
465
466 Image getImage(String name, boolean disabled) {
467 ImageIcon img = new ImageProvider(name).setDisabled(disabled).get();
468 return img != null ? img.getImage() : null;
469 }
470
471 public void initForReason(Reason reason) {
472 switch (reason) {
473 case EXIT:
474 putValue(NAME, tr("Perform actions before exiting"));
475 putValue(SHORT_DESCRIPTION, tr("Exit JOSM with saving. Unsaved changes are uploaded and/or saved."));
476 putValue(BASE_ICON, ImageProvider.get("exit"));
477 break;
478 case RESTART:
479 putValue(NAME, tr("Perform actions before restarting"));
480 putValue(SHORT_DESCRIPTION, tr("Restart JOSM with saving. Unsaved changes are uploaded and/or saved."));
481 putValue(BASE_ICON, ImageProvider.get("restart"));
482 break;
483 case DELETE:
484 putValue(NAME, tr("Perform actions before deleting"));
485 putValue(SHORT_DESCRIPTION, tr("Save/Upload layers before deleting. Unsaved changes are not lost."));
486 putValue(BASE_ICON, ImageProvider.get("dialogs", "delete"));
487 break;
488 }
489 redrawIcon();
490 }
491
492 public void redrawIcon() {
493 Image base = ((ImageIcon) getValue(BASE_ICON)).getImage();
494 BufferedImage newIco = new BufferedImage(ICON_SIZE*3, ICON_SIZE, BufferedImage.TYPE_4BYTE_ABGR);
495 Graphics2D g = newIco.createGraphics();
496 // CHECKSTYLE.OFF: SingleSpaceSeparator
497 g.drawImage(model.getLayersToUpload().isEmpty() ? upldDis : upld, ICON_SIZE*0, 0, ICON_SIZE, ICON_SIZE, null);
498 g.drawImage(model.getLayersToSave().isEmpty() ? saveDis : save, ICON_SIZE*1, 0, ICON_SIZE, ICON_SIZE, null);
499 g.drawImage(base, ICON_SIZE*2, 0, ICON_SIZE, ICON_SIZE, null);
500 // CHECKSTYLE.ON: SingleSpaceSeparator
501 putValue(SMALL_ICON, new ImageIcon(newIco));
502 }
503
504 @Override
505 public void actionPerformed(ActionEvent e) {
506 if (!confirmSaveLayerInfosOK(model))
507 return;
508 launchSafeAndUploadTask();
509 }
510
511 @Override
512 public void propertyChange(PropertyChangeEvent evt) {
513 if (evt.getPropertyName().equals(SaveLayersModel.MODE_PROP)) {
514 SaveLayersModel.Mode mode = (SaveLayersModel.Mode) evt.getNewValue();
515 switch(mode) {
516 case EDITING_DATA: setEnabled(true);
517 break;
518 case UPLOADING_AND_SAVING: setEnabled(false);
519 break;
520 }
521 }
522 }
523 }
524
525 /**
526 * This is the asynchronous task which uploads modified layers to the server and
527 * saves them to files, if requested by the user.
528 *
529 */
530 protected class SaveAndUploadTask implements Runnable {
531
532 private final SaveLayersModel model;
533 private final ProgressMonitor monitor;
534 private final ExecutorService worker;
535 private boolean canceled;
536 private AbstractIOTask currentTask;
537
538 public SaveAndUploadTask(SaveLayersModel model, ProgressMonitor monitor) {
539 this.model = model;
540 this.monitor = monitor;
541 this.worker = Executors.newSingleThreadExecutor(Utils.newThreadFactory(getClass() + "-%d", Thread.NORM_PRIORITY));
542 }
543
544 protected void uploadLayers(List<SaveLayerInfo> toUpload) {
545 for (final SaveLayerInfo layerInfo: toUpload) {
546 AbstractModifiableLayer layer = layerInfo.getLayer();
547 if (canceled) {
548 model.setUploadState(layer, UploadOrSaveState.CANCELED);
549 continue;
550 }
551 monitor.subTask(tr("Preparing layer ''{0}'' for upload ...", layerInfo.getName()));
552
553 if (!UploadAction.checkPreUploadConditions(layer)) {
554 model.setUploadState(layer, UploadOrSaveState.FAILED);
555 continue;
556 }
557
558 AbstractUploadDialog dialog = layer.getUploadDialog();
559 if (dialog != null) {
560 dialog.setVisible(true);
561 if (dialog.isCanceled()) {
562 model.setUploadState(layer, UploadOrSaveState.CANCELED);
563 continue;
564 }
565 dialog.rememberUserInput();
566 }
567
568 currentTask = layer.createUploadTask(monitor);
569 if (currentTask == null) {
570 model.setUploadState(layer, UploadOrSaveState.FAILED);
571 continue;
572 }
573 Future<?> currentFuture = worker.submit(currentTask);
574 try {
575 // wait for the asynchronous task to complete
576 currentFuture.get();
577 } catch (CancellationException e) {
578 Logging.trace(e);
579 model.setUploadState(layer, UploadOrSaveState.CANCELED);
580 } catch (InterruptedException | ExecutionException e) {
581 Logging.error(e);
582 model.setUploadState(layer, UploadOrSaveState.FAILED);
583 ExceptionDialogUtil.explainException(e);
584 }
585 if (currentTask.isCanceled()) {
586 model.setUploadState(layer, UploadOrSaveState.CANCELED);
587 } else if (currentTask.isFailed()) {
588 Logging.error(currentTask.getLastException());
589 ExceptionDialogUtil.explainException(currentTask.getLastException());
590 model.setUploadState(layer, UploadOrSaveState.FAILED);
591 } else {
592 model.setUploadState(layer, UploadOrSaveState.OK);
593 }
594 currentTask = null;
595 }
596 }
597
598 protected void saveLayers(List<SaveLayerInfo> toSave) {
599 for (final SaveLayerInfo layerInfo: toSave) {
600 if (canceled) {
601 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.CANCELED);
602 continue;
603 }
604 // Check save preconditions earlier to avoid a blocking reentring call to EDT (see #10086)
605 if (layerInfo.isDoCheckSaveConditions()) {
606 if (!layerInfo.getLayer().checkSaveConditions()) {
607 continue;
608 }
609 layerInfo.setDoCheckSaveConditions(false);
610 }
611 currentTask = new SaveLayerTask(layerInfo, monitor);
612 Future<?> currentFuture = worker.submit(currentTask);
613
614 try {
615 // wait for the asynchronous task to complete
616 //
617 currentFuture.get();
618 } catch (CancellationException e) {
619 Logging.trace(e);
620 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.CANCELED);
621 } catch (InterruptedException | ExecutionException e) {
622 Logging.error(e);
623 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.FAILED);
624 ExceptionDialogUtil.explainException(e);
625 }
626 if (currentTask.isCanceled()) {
627 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.CANCELED);
628 } else if (currentTask.isFailed()) {
629 if (currentTask.getLastException() != null) {
630 Logging.error(currentTask.getLastException());
631 ExceptionDialogUtil.explainException(currentTask.getLastException());
632 }
633 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.FAILED);
634 } else {
635 model.setSaveState(layerInfo.getLayer(), UploadOrSaveState.OK);
636 }
637 this.currentTask = null;
638 }
639 }
640
641 protected void warnBecauseOfUnsavedData() {
642 int numProblems = model.getNumCancel() + model.getNumFailed();
643 if (numProblems == 0)
644 return;
645 Logging.warn(numProblems + " problems occured during upload/save");
646 String msg = trn(
647 "<html>An upload and/or save operation of one layer with modifications<br>"
648 + "was canceled or has failed.</html>",
649 "<html>Upload and/or save operations of {0} layers with modifications<br>"
650 + "were canceled or have failed.</html>",
651 numProblems,
652 numProblems
653 );
654 JOptionPane.showMessageDialog(
655 Main.parent,
656 msg,
657 tr("Incomplete upload and/or save"),
658 JOptionPane.WARNING_MESSAGE
659 );
660 }
661
662 @Override
663 public void run() {
664 GuiHelper.runInEDTAndWait(() -> {
665 model.setMode(SaveLayersModel.Mode.UPLOADING_AND_SAVING);
666 List<SaveLayerInfo> toUpload = model.getLayersToUpload();
667 if (!toUpload.isEmpty()) {
668 uploadLayers(toUpload);
669 }
670 List<SaveLayerInfo> toSave = model.getLayersToSave();
671 if (!toSave.isEmpty()) {
672 saveLayers(toSave);
673 }
674 model.setMode(SaveLayersModel.Mode.EDITING_DATA);
675 if (model.hasUnsavedData()) {
676 warnBecauseOfUnsavedData();
677 model.setMode(Mode.EDITING_DATA);
678 if (canceled) {
679 setUserAction(UserAction.CANCEL);
680 closeDialog();
681 }
682 } else {
683 setUserAction(UserAction.PROCEED);
684 closeDialog();
685 }
686 });
687 worker.shutdownNow();
688 }
689
690 public void cancel() {
691 if (currentTask != null) {
692 currentTask.cancel();
693 }
694 worker.shutdown();
695 canceled = true;
696 }
697 }
698
699 @Override
700 public void tableChanged(TableModelEvent e) {
701 boolean dis = model.getLayersToSave().isEmpty() && model.getLayersToUpload().isEmpty();
702 if (saveAndProceedActionButton != null) {
703 saveAndProceedActionButton.setEnabled(!dis);
704 }
705 saveAndProceedAction.redrawIcon();
706 }
707}
Note: See TracBrowser for help on using the repository browser.