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.GpxData.GpxDataChangeListener;
018import org.openstreetmap.josm.gui.ExtendedDialog;
019import org.openstreetmap.josm.gui.MainApplication;
020import org.openstreetmap.josm.gui.layer.GpxLayer;
021import org.openstreetmap.josm.gui.layer.Layer;
022import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
023import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
024import org.openstreetmap.josm.gui.layer.OsmDataLayer;
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 SaveAction instance = new SaveAction();
038
039    private final PropertyChangeListener updateOnRequireSaveChange = evt -> {
040        if (OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP.equals(evt.getPropertyName())) {
041            updateEnabledState();
042        }
043    };
044
045    private final GpxDataChangeListener updateOnRequireSaveChangeGpx = evt -> updateEnabledState();
046
047    /**
048     * Construct the action with "Save" as label.
049     */
050    private SaveAction() {
051        super(tr("Save"), "save", tr("Save the current data."),
052                Shortcut.registerShortcut("system:save", tr("File: {0}", tr("Save")), KeyEvent.VK_S, Shortcut.CTRL),
053                true);
054        setHelpId(ht("/Action/Save"));
055    }
056
057    /**
058     * Returns the unique instance.
059     * @return the unique instance
060     */
061    public static SaveAction getInstance() {
062        return instance;
063    }
064
065    @Override
066    protected LayerChangeAdapter buildLayerChangeAdapter() {
067        return new LayerChangeAdapter() {
068            @Override
069            public void layerAdded(LayerAddEvent e) {
070                Layer l = e.getAddedLayer();
071                if (l instanceof OsmDataLayer) {
072                    l.addPropertyChangeListener(updateOnRequireSaveChange);
073                }
074                if (l instanceof GpxLayer) {
075                    ((GpxLayer) l).data.addWeakChangeListener(updateOnRequireSaveChangeGpx);
076                }
077                super.layerAdded(e);
078            }
079
080            @Override
081            public void layerRemoving(LayerRemoveEvent e) {
082                Layer l = e.getRemovedLayer();
083                if (l instanceof OsmDataLayer) {
084                    l.removePropertyChangeListener(updateOnRequireSaveChange);
085                }
086                if (l instanceof GpxLayer) {
087                    ((GpxLayer) l).data.removeChangeListener(updateOnRequireSaveChangeGpx);
088                }
089                super.layerRemoving(e);
090            }
091        };
092    }
093
094    @Override
095    protected void updateEnabledState() {
096        Layer activeLayer = getLayerManager().getActiveLayer();
097        boolean en = activeLayer != null
098                && activeLayer.isSavable() && (!(activeLayer.getAssociatedFile() != null
099                && activeLayer instanceof SaveToFile && !((SaveToFile) activeLayer).requiresSaveToFile()));
100        GuiHelper.runInEDT(() -> setEnabled(en));
101    }
102
103    @Override
104    public File getFile(Layer layer) {
105        File f = layer.getAssociatedFile();
106        if (f != null && !f.exists()) {
107            f = null;
108        }
109
110        // Ask for overwrite in case of GpxLayer
111        if (f != null && layer instanceof GpxLayer && !Config.getPref().getBoolean("gpx.export.overwrite", false)) {
112            JPanel p = new JPanel(new GridBagLayout());
113            JLabel label = new JLabel(tr("File {0} exists. Overwrite?", f.getName()));
114            label.setHorizontalAlignment(SwingConstants.CENTER);
115            JCheckBox remember = new JCheckBox(tr("Remember choice"));
116            remember.setHorizontalAlignment(SwingConstants.CENTER);
117            p.add(label, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 10));
118            p.add(remember, GBC.eop().fill(GBC.HORIZONTAL));
119            ExtendedDialog dialog = new ExtendedDialog(
120                    MainApplication.getMainFrame(),
121                    tr("Overwrite"),
122                    tr("Overwrite"), tr("Cancel"))
123                .setButtonIcons("save_as", "cancel")
124                .setContent(p);
125            if (dialog.showDialog().getValue() != 1) {
126                f = null;
127            } else if (remember.isSelected()) {
128                Config.getPref().putBoolean("gpx.export.overwrite", true);
129            }
130        }
131        return f == null ? layer.createAndOpenSaveFileChooser() : f;
132    }
133}