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

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

sonar - squid:S1854 - Dead stores should be removed

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