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, 2 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.