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

Last change on this file since 15652 was 15496, checked in by Don-vip, 5 years ago

fix #16796 - Rework of GPX track colors / layer preferences (patch by Bjoeni)

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