source: josm/trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java@ 13115

Last change on this file since 13115 was 13115, checked in by Don-vip, 6 years ago

fix #12086 - fix EDT violation when no file exporter is found + choose note exporter by default when saving a note layer

  • Property svn:eol-style set to native
File size: 8.8 KB
RevLine 
[8378]1// License: GPL. For details, see LICENSE file.
[290]2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.io.File;
8import java.io.IOException;
[4371]9import java.util.Collection;
10import java.util.LinkedList;
11import java.util.List;
[5014]12
[1949]13import javax.swing.JFileChooser;
[290]14import javax.swing.JOptionPane;
[1949]15import javax.swing.filechooser.FileFilter;
[290]16
17import org.openstreetmap.josm.Main;
[12891]18import org.openstreetmap.josm.data.PreferencesUtils;
[1397]19import org.openstreetmap.josm.gui.ExtendedDialog;
[12671]20import org.openstreetmap.josm.gui.io.importexport.FileExporter;
[1523]21import org.openstreetmap.josm.gui.layer.Layer;
[290]22import org.openstreetmap.josm.gui.layer.OsmDataLayer;
[13115]23import org.openstreetmap.josm.gui.util.GuiHelper;
[7578]24import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
[12846]25import org.openstreetmap.josm.spi.preferences.Config;
[12620]26import org.openstreetmap.josm.tools.Logging;
[1084]27import org.openstreetmap.josm.tools.Shortcut;
[290]28
[9676]29/**
30 * Abstract superclass of save actions.
31 * @since 290
32 */
[1820]33public abstract class SaveActionBase extends DiskAccessAction {
[290]34
[9676]35 /**
36 * Constructs a new {@code SaveActionBase}.
37 * @param name The action's text as displayed on the menu (if it is added to a menu)
38 * @param iconName The filename of the icon to use
39 * @param tooltip A longer description of the action that will be displayed in the tooltip
40 * @param shortcut A ready-created shortcut object or {@code null} if you don't want a shortcut
41 */
[1808]42 public SaveActionBase(String name, String iconName, String tooltip, Shortcut shortcut) {
[1169]43 super(name, iconName, tooltip, shortcut);
44 }
[1023]45
[4371]46 @Override
[1169]47 public void actionPerformed(ActionEvent e) {
[5014]48 if (!isEnabled())
[1808]49 return;
[9413]50 doSave();
[1373]51 }
52
[9676]53 /**
54 * Saves the active layer.
55 * @return {@code true} if the save operation succeeds
56 */
[1808]57 public boolean doSave() {
[12636]58 Layer layer = getLayerManager().getActiveLayer();
[10318]59 if (layer != null && layer.isSavable()) {
60 return doSave(layer);
[1750]61 }
[5459]62 return false;
[1808]63 }
[290]64
[9676]65 /**
66 * Saves the given layer.
67 * @param layer layer to save
68 * @return {@code true} if the save operation succeeds
69 */
[1808]70 public boolean doSave(Layer layer) {
[8510]71 if (!layer.checkSaveConditions())
[1808]72 return false;
[10007]73 return doInternalSave(layer, getFile(layer));
[4114]74 }
75
[7204]76 /**
77 * Saves a layer to a given file.
78 * @param layer The layer to save
79 * @param file The destination file
80 * @param checkSaveConditions if {@code true}, checks preconditions before saving. Set it to {@code false} to skip it
81 * if preconditions have already been checked (as this check can prompt UI dialog in EDT it may be best in some cases
82 * to do it earlier).
83 * @return {@code true} if the layer has been successfully saved, {@code false} otherwise
84 * @since 7204
85 */
86 public static boolean doSave(Layer layer, File file, boolean checkSaveConditions) {
87 if (checkSaveConditions && !layer.checkSaveConditions())
[1808]88 return false;
[4114]89 return doInternalSave(layer, file);
90 }
[290]91
[5014]92 private static boolean doInternalSave(Layer layer, File file) {
[1169]93 if (file == null)
[1373]94 return false;
[290]95
[1949]96 try {
97 boolean exported = false;
[6815]98 boolean canceled = false;
[10407]99 for (FileExporter exporter : ExtensionFileFilter.getExporters()) {
[1949]100 if (exporter.acceptFile(file, layer)) {
101 exporter.exportData(file, layer);
102 exported = true;
[6815]103 canceled = exporter.isCanceled();
[4463]104 break;
[1949]105 }
106 }
107 if (!exported) {
[13115]108 GuiHelper.runInEDTAndWait(() ->
109 JOptionPane.showMessageDialog(Main.parent, tr("No Exporter found! Nothing saved."), tr("Warning"),
110 JOptionPane.WARNING_MESSAGE));
[1949]111 return false;
[6815]112 } else if (canceled) {
113 return false;
[1949]114 }
[9510]115 if (!layer.isRenamed()) {
116 layer.setName(file.getName());
117 }
[1949]118 layer.setAssociatedFile(file);
[2025]119 if (layer instanceof OsmDataLayer) {
120 ((OsmDataLayer) layer).onPostSaveToFile();
121 }
[1949]122 Main.parent.repaint();
123 } catch (IOException e) {
[12620]124 Logging.error(e);
[1949]125 return false;
126 }
[9303]127 addToFileOpenHistory(file);
[1373]128 return true;
[1169]129 }
[319]130
[1169]131 protected abstract File getFile(Layer layer);
[290]132
[1169]133 /**
[1808]134 * Refreshes the enabled state
[1949]135 *
[1808]136 */
[1820]137 @Override
138 protected void updateEnabledState() {
[12636]139 Layer activeLayer = getLayerManager().getActiveLayer();
[10318]140 setEnabled(activeLayer != null && activeLayer.isSavable());
[1808]141 }
[1949]142
[5456]143 /**
[6830]144 * Creates a new "Save" dialog for a single {@link ExtensionFileFilter} and makes it visible.<br>
[5456]145 * When the user has chosen a file, checks the file extension, and confirms overwrite if needed.
[6069]146 *
[5456]147 * @param title The dialog title
148 * @param filter The dialog file filter
149 * @return The output {@code File}
[8419]150 * @see DiskAccessAction#createAndOpenFileChooser(boolean, boolean, String, FileFilter, int, String)
[5456]151 * @since 5456
152 */
153 public static File createAndOpenSaveFileChooser(String title, ExtensionFileFilter filter) {
[7578]154 AbstractFileChooser fc = createAndOpenFileChooser(false, false, title, filter, JFileChooser.FILES_ONLY, null);
[5456]155 return checkFileAndConfirmOverWrite(fc, filter.getDefaultExtension());
156 }
157
158 /**
[6830]159 * Creates a new "Save" dialog for a given file extension and makes it visible.<br>
[5456]160 * When the user has chosen a file, checks the file extension, and confirms overwrite if needed.
[6069]161 *
[5456]162 * @param title The dialog title
163 * @param extension The file extension
164 * @return The output {@code File}
165 * @see DiskAccessAction#createAndOpenFileChooser(boolean, boolean, String, String)
166 */
[1949]167 public static File createAndOpenSaveFileChooser(String title, String extension) {
[7578]168 AbstractFileChooser fc = createAndOpenFileChooser(false, false, title, extension);
[5456]169 return checkFileAndConfirmOverWrite(fc, extension);
170 }
[6069]171
[9670]172 /**
173 * Checks if selected filename has the given extension. If not, adds the extension and asks for overwrite if filename exists.
174 *
175 * @param fc FileChooser where file was already selected
[9676]176 * @param extension file extension
[9670]177 * @return the {@code File} or {@code null} if the user cancelled the dialog.
178 */
179 public static File checkFileAndConfirmOverWrite(AbstractFileChooser fc, String extension) {
[9676]180 if (fc == null)
181 return null;
[1949]182 File file = fc.getSelectedFile();
[6069]183
[6011]184 FileFilter ff = fc.getFileFilter();
185 if (!ff.accept(file)) {
186 // Extension of another filefilter given ?
187 for (FileFilter cff : fc.getChoosableFileFilters()) {
188 if (cff.accept(file)) {
189 fc.setFileFilter(cff);
190 return file;
191 }
192 }
193 // No filefilter accepts current filename, add default extension
194 String fn = file.getPath();
[9721]195 if (extension != null && ff.accept(new File(fn + '.' + extension))) {
[9466]196 fn += '.' + extension;
197 } else if (ff instanceof ExtensionFileFilter) {
[8846]198 fn += '.' + ((ExtensionFileFilter) ff).getDefaultExtension();
[1949]199 }
[3570]200 file = new File(fn);
[9687]201 if (!fc.getSelectedFile().exists() && !confirmOverwrite(file))
[5457]202 return null;
[1949]203 }
[3863]204 return file;
205 }
206
[9676]207 /**
208 * Asks user to confirm overwiting a file.
209 * @param file file to overwrite
210 * @return {@code true} if the file can be written
211 */
[4911]212 public static boolean confirmOverwrite(File file) {
[9676]213 if (file == null || file.exists()) {
[12279]214 return new ExtendedDialog(
[2070]215 Main.parent,
216 tr("Overwrite"),
[12279]217 tr("Overwrite"), tr("Cancel"))
218 .setContent(tr("File exists. Overwrite?"))
219 .setButtonIcons("save_as", "cancel")
220 .showDialog()
221 .getValue() == 1;
[2070]222 }
[3863]223 return true;
[1949]224 }
[4371]225
[8802]226 static void addToFileOpenHistory(File file) {
227 final String filepath;
[4371]228 try {
229 filepath = file.getCanonicalPath();
230 } catch (IOException ign) {
[12620]231 Logging.warn(ign);
[4371]232 return;
233 }
234
[12846]235 int maxsize = Math.max(0, Config.getPref().getInt("file-open.history.max-size", 15));
236 Collection<String> oldHistory = Config.getPref().getList("file-open.history");
[7005]237 List<String> history = new LinkedList<>(oldHistory);
[4374]238 history.remove(filepath);
[4371]239 history.add(0, filepath);
[12894]240 PreferencesUtils.putListBounded(Config.getPref(), "file-open.history", maxsize, history);
[4371]241 }
[290]242}
Note: See TracBrowser for help on using the repository browser.