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