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

Last change on this file since 14950 was 14950, checked in by GerdP, 5 years ago

fix #17238 Don't know why I forgot to commit this patch.

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