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