source: josm/trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java@ 7578

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

fix #10024 - Add an option in Preferences/Look-and-Feel to use native file-choosing dialogs.
They look nicer but they do not support file filters, so we cannot use them (yet) as default.
Based on patch by Lesath and code review by simon04.
The native dialogs are not used if selection mode is not supported ("files and directories" on all platforms, "directories" on systems other than OS X)

  • Property svn:eol-style set to native
File size: 13.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.event.ActionEvent;
9import java.awt.event.KeyEvent;
10import java.io.BufferedReader;
11import java.io.File;
12import java.io.IOException;
13import java.nio.charset.StandardCharsets;
14import java.nio.file.Files;
15import java.util.ArrayList;
16import java.util.Arrays;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.HashSet;
20import java.util.LinkedHashSet;
21import java.util.LinkedList;
22import java.util.List;
23import java.util.Set;
24import java.util.regex.Matcher;
25import java.util.regex.Pattern;
26
27import javax.swing.JOptionPane;
28import javax.swing.SwingUtilities;
29import javax.swing.filechooser.FileFilter;
30
31import org.openstreetmap.josm.Main;
32import org.openstreetmap.josm.gui.HelpAwareOptionPane;
33import org.openstreetmap.josm.gui.PleaseWaitRunnable;
34import org.openstreetmap.josm.gui.help.HelpUtil;
35import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
36import org.openstreetmap.josm.io.AllFormatsImporter;
37import org.openstreetmap.josm.io.FileImporter;
38import org.openstreetmap.josm.io.OsmTransferException;
39import org.openstreetmap.josm.tools.MultiMap;
40import org.openstreetmap.josm.tools.Shortcut;
41import org.xml.sax.SAXException;
42
43/**
44 * Open a file chooser dialog and select a file to import.
45 *
46 * @author imi
47 * @since 1146
48 */
49public class OpenFileAction extends DiskAccessAction {
50
51 /**
52 * The {@link ExtensionFileFilter} matching .url files
53 */
54 public static final ExtensionFileFilter urlFileFilter = new ExtensionFileFilter("url", "url", tr("URL Files") + " (*.url)");
55
56 /**
57 * Create an open action. The name is "Open a file".
58 */
59 public OpenFileAction() {
60 super(tr("Open..."), "open", tr("Open a file."),
61 Shortcut.registerShortcut("system:open", tr("File: {0}", tr("Open...")), KeyEvent.VK_O, Shortcut.CTRL));
62 putValue("help", ht("/Action/Open"));
63 }
64
65 @Override
66 public void actionPerformed(ActionEvent e) {
67 AbstractFileChooser fc = createAndOpenFileChooser(true, true, null);
68 if (fc == null)
69 return;
70 File[] files = fc.getSelectedFiles();
71 OpenFileTask task = new OpenFileTask(Arrays.asList(files), fc.getFileFilter());
72 task.setRecordHistory(true);
73 Main.worker.submit(task);
74 }
75
76 @Override
77 protected void updateEnabledState() {
78 setEnabled(true);
79 }
80
81 /**
82 * Open a list of files. The complete list will be passed to batch importers.
83 * @param fileList A list of files
84 */
85 public static void openFiles(List<File> fileList) {
86 openFiles(fileList, false);
87 }
88
89 public static void openFiles(List<File> fileList, boolean recordHistory) {
90 OpenFileTask task = new OpenFileTask(fileList, null);
91 task.setRecordHistory(recordHistory);
92 Main.worker.submit(task);
93 }
94
95 public static class OpenFileTask extends PleaseWaitRunnable {
96 private final List<File> files;
97 private final List<File> successfullyOpenedFiles = new ArrayList<>();
98 private final Set<String> fileHistory = new LinkedHashSet<>();
99 private final Set<String> failedAll = new HashSet<>();
100 private final FileFilter fileFilter;
101 private boolean canceled;
102 private boolean recordHistory = false;
103
104 public OpenFileTask(List<File> files, FileFilter fileFilter, String title) {
105 super(title, false /* don't ignore exception */);
106 this.files = new ArrayList<>(files);
107 this.fileFilter = fileFilter;
108 }
109
110 public OpenFileTask(List<File> files, FileFilter fileFilter) {
111 this(files, fileFilter, tr("Opening files"));
112 }
113
114 /**
115 * save filename in history (for list of recently opened files)
116 * default: false
117 */
118 public void setRecordHistory(boolean recordHistory) {
119 this.recordHistory = recordHistory;
120 }
121
122 public boolean isRecordHistory() {
123 return recordHistory;
124 }
125
126 @Override
127 protected void cancel() {
128 this.canceled = true;
129 }
130
131 @Override
132 protected void finish() {
133 // do nothing
134 }
135
136 protected void alertFilesNotMatchingWithImporter(Collection<File> files, FileImporter importer) {
137 final StringBuilder msg = new StringBuilder();
138 msg.append("<html>");
139 msg.append(
140 trn(
141 "Cannot open {0} file with the file importer ''{1}''.",
142 "Cannot open {0} files with the file importer ''{1}''.",
143 files.size(),
144 files.size(),
145 importer.filter.getDescription()
146 )
147 ).append("<br>");
148 msg.append("<ul>");
149 for (File f: files) {
150 msg.append("<li>").append(f.getAbsolutePath()).append("</li>");
151 }
152 msg.append("</ul>");
153
154 HelpAwareOptionPane.showMessageDialogInEDT(
155 Main.parent,
156 msg.toString(),
157 tr("Warning"),
158 JOptionPane.WARNING_MESSAGE,
159 HelpUtil.ht("/Action/Open#ImporterCantImportFiles")
160 );
161 }
162
163 protected void alertFilesWithUnknownImporter(Collection<File> files) {
164 final StringBuilder msg = new StringBuilder();
165 msg.append("<html>");
166 msg.append(
167 trn(
168 "Cannot open {0} file because file does not exist or no suitable file importer is available.",
169 "Cannot open {0} files because files do not exist or no suitable file importer is available.",
170 files.size(),
171 files.size()
172 )
173 ).append("<br>");
174 msg.append("<ul>");
175 for (File f: files) {
176 msg.append("<li>");
177 msg.append(f.getAbsolutePath());
178 msg.append(" (<i>");
179 msg.append(f.exists() ? tr("no importer") : tr("does not exist"));
180 msg.append("</i>)</li>");
181 }
182 msg.append("</ul>");
183
184 HelpAwareOptionPane.showMessageDialogInEDT(
185 Main.parent,
186 msg.toString(),
187 tr("Warning"),
188 JOptionPane.WARNING_MESSAGE,
189 HelpUtil.ht("/Action/Open#MissingImporterForFiles")
190 );
191 }
192
193 @Override
194 protected void realRun() throws SAXException, IOException, OsmTransferException {
195 if (files == null || files.isEmpty()) return;
196
197 /**
198 * Find the importer with the chosen file filter
199 */
200 FileImporter chosenImporter = null;
201 for (FileImporter importer : ExtensionFileFilter.importers) {
202 if (fileFilter == importer.filter) {
203 chosenImporter = importer;
204 }
205 }
206 /**
207 * If the filter hasn't been changed in the dialog, chosenImporter is null now.
208 * When the filter has been set explicitly to AllFormatsImporter, treat this the same.
209 */
210 if (chosenImporter instanceof AllFormatsImporter) {
211 chosenImporter = null;
212 }
213 getProgressMonitor().setTicksCount(files.size());
214
215 if (chosenImporter != null) {
216 // The importer was explicitly chosen, so use it.
217 List<File> filesNotMatchingWithImporter = new LinkedList<>();
218 List<File> filesMatchingWithImporter = new LinkedList<>();
219 for (final File f : files) {
220 if (!chosenImporter.acceptFile(f)) {
221 if (f.isDirectory()) {
222 SwingUtilities.invokeLater(new Runnable() {
223 @Override
224 public void run() {
225 JOptionPane.showMessageDialog(Main.parent, tr(
226 "<html>Cannot open directory ''{0}''.<br>Please select a file.</html>",
227 f.getAbsolutePath()), tr("Open file"), JOptionPane.ERROR_MESSAGE);
228 }
229 });
230 // TODO when changing to Java 6: Don't cancel the
231 // task here but use different modality. (Currently 2 dialogs
232 // would block each other.)
233 return;
234 } else {
235 filesNotMatchingWithImporter.add(f);
236 }
237 } else {
238 filesMatchingWithImporter.add(f);
239 }
240 }
241
242 if (!filesNotMatchingWithImporter.isEmpty()) {
243 alertFilesNotMatchingWithImporter(filesNotMatchingWithImporter, chosenImporter);
244 }
245 if (!filesMatchingWithImporter.isEmpty()) {
246 importData(chosenImporter, filesMatchingWithImporter);
247 }
248 } else {
249 // find appropriate importer
250 MultiMap<FileImporter, File> importerMap = new MultiMap<>();
251 List<File> filesWithUnknownImporter = new LinkedList<>();
252 List<File> urlFiles = new LinkedList<>();
253 FILES: for (File f : files) {
254 for (FileImporter importer : ExtensionFileFilter.importers) {
255 if (importer.acceptFile(f)) {
256 importerMap.put(importer, f);
257 continue FILES;
258 }
259 }
260 if (urlFileFilter.accept(f)) {
261 urlFiles.add(f);
262 } else {
263 filesWithUnknownImporter.add(f);
264 }
265 }
266 if (!filesWithUnknownImporter.isEmpty()) {
267 alertFilesWithUnknownImporter(filesWithUnknownImporter);
268 }
269 List<FileImporter> importers = new ArrayList<>(importerMap.keySet());
270 Collections.sort(importers);
271 Collections.reverse(importers);
272
273 for (FileImporter importer : importers) {
274 importData(importer, new ArrayList<>(importerMap.get(importer)));
275 }
276
277 for (File urlFile: urlFiles) {
278 try (BufferedReader reader = Files.newBufferedReader(urlFile.toPath(), StandardCharsets.UTF_8)) {
279 String line;
280 while ((line = reader.readLine()) != null) {
281 Matcher m = Pattern.compile(".*(https?://.*)").matcher(line);
282 if (m.matches()) {
283 String url = m.group(1);
284 Main.main.menu.openLocation.openUrl(false, url);
285 }
286 }
287 } catch (Exception e) {
288 Main.error(e);
289 }
290 }
291 }
292
293 if (recordHistory) {
294 Collection<String> oldFileHistory = Main.pref.getCollection("file-open.history");
295 fileHistory.addAll(oldFileHistory);
296 // remove the files which failed to load from the list
297 fileHistory.removeAll(failedAll);
298 int maxsize = Math.max(0, Main.pref.getInteger("file-open.history.max-size", 15));
299 Main.pref.putCollectionBounded("file-open.history", maxsize, fileHistory);
300 }
301 }
302
303 public void importData(FileImporter importer, List<File> files) {
304 if (importer.isBatchImporter()) {
305 if (canceled) return;
306 String msg = trn("Opening {0} file...", "Opening {0} files...", files.size(), files.size());
307 getProgressMonitor().setCustomText(msg);
308 getProgressMonitor().indeterminateSubTask(msg);
309 if (importer.importDataHandleExceptions(files, getProgressMonitor().createSubTaskMonitor(files.size(), false))) {
310 successfullyOpenedFiles.addAll(files);
311 }
312 } else {
313 for (File f : files) {
314 if (canceled) return;
315 getProgressMonitor().indeterminateSubTask(tr("Opening file ''{0}'' ...", f.getAbsolutePath()));
316 if (importer.importDataHandleExceptions(f, getProgressMonitor().createSubTaskMonitor(1, false))) {
317 successfullyOpenedFiles.add(f);
318 }
319 }
320 }
321 if (recordHistory && !importer.isBatchImporter()) {
322 for (File f : files) {
323 try {
324 if (successfullyOpenedFiles.contains(f)) {
325 fileHistory.add(f.getCanonicalPath());
326 } else {
327 failedAll.add(f.getCanonicalPath());
328 }
329 } catch (IOException e) {
330 Main.warn(e);
331 }
332 }
333 }
334 }
335
336 /**
337 * Replies the list of files that have been successfully opened.
338 * @return The list of files that have been successfully opened.
339 */
340 public List<File> getSuccessfullyOpenedFiles() {
341 return successfullyOpenedFiles;
342 }
343 }
344}
Note: See TracBrowser for help on using the repository browser.