001//License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.event.ActionEvent;
007import java.io.File;
008import java.io.IOException;
009import java.util.Collection;
010import java.util.LinkedList;
011import java.util.List;
012
013import javax.swing.JFileChooser;
014import javax.swing.JOptionPane;
015import javax.swing.filechooser.FileFilter;
016
017import org.openstreetmap.josm.Main;
018import org.openstreetmap.josm.gui.ExtendedDialog;
019import org.openstreetmap.josm.gui.layer.Layer;
020import org.openstreetmap.josm.gui.layer.OsmDataLayer;
021import org.openstreetmap.josm.io.FileExporter;
022import org.openstreetmap.josm.tools.Shortcut;
023
024public abstract class SaveActionBase extends DiskAccessAction {
025    private File file;
026
027    public SaveActionBase(String name, String iconName, String tooltip, Shortcut shortcut) {
028        super(name, iconName, tooltip, shortcut);
029    }
030
031    @Override
032    public void actionPerformed(ActionEvent e) {
033        if (!isEnabled())
034            return;
035        boolean saved = doSave();
036        if (saved) {
037            addToFileOpenHistory();
038        }
039    }
040
041    public boolean doSave() {
042        if (Main.isDisplayingMapView()) {
043            Layer layer = Main.map.mapView.getActiveLayer();
044            if (layer != null && layer.isSavable()) {
045                return doSave(layer);
046            }
047        }
048        return false;
049    }
050
051    public boolean doSave(Layer layer) {
052        if(!layer.checkSaveConditions())
053            return false;
054        file = getFile(layer);
055        return doInternalSave(layer, file);
056    }
057
058    public static boolean doSave(Layer layer, File file) {
059        if(!layer.checkSaveConditions())
060            return false;
061        return doInternalSave(layer, file);
062    }
063
064    private static boolean doInternalSave(Layer layer, File file) {
065        if (file == null)
066            return false;
067
068        try {
069            boolean exported = false;
070            for (FileExporter exporter : ExtensionFileFilter.exporters) {
071                if (exporter.acceptFile(file, layer)) {
072                    exporter.exportData(file, layer);
073                    exported = true;
074                    break;
075                }
076            }
077            if (!exported) {
078                JOptionPane.showMessageDialog(Main.parent, tr("No Exporter found! Nothing saved."), tr("Warning"),
079                        JOptionPane.WARNING_MESSAGE);
080                return false;
081            }
082            layer.setName(file.getName());
083            layer.setAssociatedFile(file);
084            if (layer instanceof OsmDataLayer) {
085                ((OsmDataLayer) layer).onPostSaveToFile();
086            }
087            Main.parent.repaint();
088        } catch (IOException e) {
089            e.printStackTrace();
090            return false;
091        }
092        return true;
093    }
094
095    protected abstract File getFile(Layer layer);
096
097    /**
098     * Refreshes the enabled state
099     *
100     */
101    @Override
102    protected void updateEnabledState() {
103        if (Main.applet) {
104            setEnabled(false);
105            return;
106        }
107        boolean check = Main.isDisplayingMapView()
108        && Main.map.mapView.getActiveLayer() != null;
109        if(!check) {
110            setEnabled(false);
111            return;
112        }
113        Layer layer = Main.map.mapView.getActiveLayer();
114        setEnabled(layer != null && layer.isSavable());
115    }
116
117    /**
118     * Creates a new "Save" dialog for a single {@link ExtensionFileFilter} and makes it visible.<br/>
119     * When the user has chosen a file, checks the file extension, and confirms overwrite if needed.
120     *
121     * @param title The dialog title
122     * @param filter The dialog file filter
123     * @return The output {@code File}
124     * @since 5456
125     * @see DiskAccessAction#createAndOpenFileChooser(boolean, boolean, String, FileFilter, int, String)
126     */
127    public static File createAndOpenSaveFileChooser(String title, ExtensionFileFilter filter) {
128        JFileChooser fc = createAndOpenFileChooser(false, false, title, filter, JFileChooser.FILES_ONLY, null);
129        return checkFileAndConfirmOverWrite(fc, filter.getDefaultExtension());
130    }
131
132    /**
133     * Creates a new "Save" dialog for a given file extension and makes it visible.<br/>
134     * When the user has chosen a file, checks the file extension, and confirms overwrite if needed.
135     *
136     * @param title The dialog title
137     * @param extension The file extension
138     * @return The output {@code File}
139     * @see DiskAccessAction#createAndOpenFileChooser(boolean, boolean, String, String)
140     */
141    public static File createAndOpenSaveFileChooser(String title, String extension) {
142        JFileChooser fc = createAndOpenFileChooser(false, false, title, extension);
143        return checkFileAndConfirmOverWrite(fc, extension);
144    }
145
146    private static File checkFileAndConfirmOverWrite(JFileChooser fc, String extension) {
147        if (fc == null) return null;
148        File file = fc.getSelectedFile();
149
150        FileFilter ff = fc.getFileFilter();
151        if (!ff.accept(file)) {
152            // Extension of another filefilter given ?
153            for (FileFilter cff : fc.getChoosableFileFilters()) {
154                if (cff.accept(file)) {
155                    fc.setFileFilter(cff);
156                    return file;
157                }
158            }
159            // No filefilter accepts current filename, add default extension
160            String fn = file.getPath();
161            if (ff instanceof ExtensionFileFilter) {
162                fn += "." + ((ExtensionFileFilter)ff).getDefaultExtension();
163            } else if (extension != null) {
164                fn += "." + extension;
165            }
166            file = new File(fn);
167            if (!confirmOverwrite(file))
168                return null;
169        }
170        return file;
171    }
172
173    public static boolean confirmOverwrite(File file) {
174        if (file == null || (file.exists())) {
175            ExtendedDialog dialog = new ExtendedDialog(
176                    Main.parent,
177                    tr("Overwrite"),
178                    new String[] {tr("Overwrite"), tr("Cancel")}
179            );
180            dialog.setContent(tr("File exists. Overwrite?"));
181            dialog.setButtonIcons(new String[] {"save_as.png", "cancel.png"});
182            dialog.showDialog();
183            return (dialog.getValue() == 1);
184        }
185        return true;
186    }
187
188    protected void addToFileOpenHistory() {
189        String filepath;
190        try {
191            filepath = file.getCanonicalPath();
192        } catch (IOException ign) {
193            return;
194        }
195
196        int maxsize = Math.max(0, Main.pref.getInteger("file-open.history.max-size", 15));
197        Collection<String> oldHistory = Main.pref.getCollection("file-open.history");
198        List<String> history = new LinkedList<String>(oldHistory);
199        history.remove(filepath);
200        history.add(0, filepath);
201        Main.pref.putCollectionBounded("file-open.history", maxsize, history);
202    }
203}