001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.awt.GridBagLayout;
008import java.awt.event.KeyEvent;
009import java.beans.PropertyChangeListener;
010import java.io.File;
011
012import javax.swing.JCheckBox;
013import javax.swing.JLabel;
014import javax.swing.JPanel;
015import javax.swing.SwingConstants;
016
017import org.openstreetmap.josm.data.gpx.GpxConstants;
018import org.openstreetmap.josm.gui.ExtendedDialog;
019import org.openstreetmap.josm.gui.MainApplication;
020import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
021import org.openstreetmap.josm.gui.layer.GpxLayer;
022import org.openstreetmap.josm.gui.layer.Layer;
023import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
024import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
025import org.openstreetmap.josm.gui.layer.SaveToFile;
026import org.openstreetmap.josm.gui.util.GuiHelper;
027import org.openstreetmap.josm.spi.preferences.Config;
028import org.openstreetmap.josm.tools.GBC;
029import org.openstreetmap.josm.tools.Shortcut;
030
031/**
032 * Export the data as an OSM xml file.
033 *
034 * @author imi
035 */
036public final class SaveAction extends SaveActionBase {
037    private static final SaveAction instance = new SaveAction();
038
039    private final PropertyChangeListener updateOnRequireSaveChange = evt -> {
040        if (AbstractModifiableLayer.REQUIRES_SAVE_TO_DISK_PROP.equals(evt.getPropertyName())) {
041            updateEnabledState();
042        }
043    };
044
045    /**
046     * Construct the action with "Save" as label.
047     */
048    private SaveAction() {
049        super(tr("Save"), "save", tr("Save the current data."),
050                Shortcut.registerShortcut("system:save", tr("File: {0}", tr("Save")), KeyEvent.VK_S, Shortcut.CTRL),
051                true);
052        setHelpId(ht("/Action/Save"));
053    }
054
055    /**
056     * Returns the unique instance.
057     * @return the unique instance
058     */
059    public static SaveAction getInstance() {
060        return instance;
061    }
062
063    @Override
064    protected LayerChangeAdapter buildLayerChangeAdapter() {
065        return new LayerChangeAdapter() {
066            @Override
067            public void layerAdded(LayerAddEvent e) {
068                Layer l = e.getAddedLayer();
069                if (l instanceof AbstractModifiableLayer) {
070                    l.addPropertyChangeListener(updateOnRequireSaveChange);
071                }
072                super.layerAdded(e);
073            }
074
075            @Override
076            public void layerRemoving(LayerRemoveEvent e) {
077                Layer l = e.getRemovedLayer();
078                if (l instanceof AbstractModifiableLayer) {
079                    l.removePropertyChangeListener(updateOnRequireSaveChange);
080                }
081                super.layerRemoving(e);
082            }
083        };
084    }
085
086    @Override
087    protected boolean listenToSelectionChange() {
088        return false;
089    }
090
091    @Override
092    protected void updateEnabledState() {
093        Layer activeLayer = getLayerManager().getActiveLayer();
094        boolean en = activeLayer != null
095                && activeLayer.isSavable() && !(activeLayer.getAssociatedFile() != null
096                && activeLayer instanceof SaveToFile && !((SaveToFile) activeLayer).requiresSaveToFile());
097        GuiHelper.runInEDT(() -> setEnabled(en));
098    }
099
100    @Override
101    public File getFile(Layer layer) {
102        File f = layer.getAssociatedFile();
103        if (f != null && !f.exists()) {
104            f = null;
105        }
106
107        // Ask for overwrite in case of GpxLayer
108        if (f != null
109                && layer instanceof GpxLayer
110                && (((GpxLayer) layer).data == null
111                || !GpxConstants.JOSM_CREATOR_NAME.equals(((GpxLayer) layer).data.creator))
112                && !Config.getPref().getBoolean("gpx.export.overwrite", false)) {
113
114            JPanel p = new JPanel(new GridBagLayout());
115            JLabel label = new JLabel("<html>"
116                    + tr("The file \"{0}\" will be modified.<br>Would you like to overwrite the existing file?", f.getName())
117                    + "</html>");
118            label.setHorizontalAlignment(SwingConstants.CENTER);
119            JCheckBox remember = new JCheckBox(tr("Always overwrite GPX files without asking"));
120            remember.setHorizontalAlignment(SwingConstants.CENTER);
121            p.add(label, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 10));
122            p.add(remember, GBC.eop().fill(GBC.HORIZONTAL));
123            ExtendedDialog dialog = new ExtendedDialog(
124                    MainApplication.getMainFrame(),
125                    tr("Overwrite"),
126                    tr("Overwrite"), tr("Save As..."), tr("Cancel"))
127                .setButtonIcons("save", "save_as", "cancel")
128                .setContent(p);
129            int val = dialog.showDialog().getValue();
130            if (val == 1) {
131                Config.getPref().putBoolean("gpx.export.overwrite", remember.isSelected());
132            } else if (val == 2) {
133                f = null;
134            } else {
135                return null;
136            }
137        }
138        return f == null ? layer.createAndOpenSaveFileChooser() : f;
139    }
140}