View Javadoc
1   package fr.ifremer.quadrige3.ui.swing.action;
2   
3   /*-
4    * #%L
5    * Quadrige3 Core :: UI Swing Common
6    * %%
7    * Copyright (C) 2017 - 2020 Ifremer
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Affero General Public License as published by
11   * the Free Software Foundation, either version 3 of the License, or
12   * (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU Affero General Public License
20   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21   * #L%
22   */
23  
24  import com.google.common.collect.ImmutableList;
25  import org.apache.commons.collections4.CollectionUtils;
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  import javax.swing.Action;
31  import javax.swing.JFileChooser;
32  import javax.swing.filechooser.FileFilter;
33  import java.awt.Component;
34  import java.io.File;
35  import java.util.ArrayList;
36  import java.util.Arrays;
37  import java.util.List;
38  import java.util.stream.Collectors;
39  
40  /**
41   * Extended version of org.nuiton.jaxx.runtime.JaxxFileChooser which add load files
42   *
43   * @author peck7 on 13/02/2020.
44   */
45  public class FileChooser {
46  
47      /**
48       * To choose a file (in load mode).
49       * <p>
50       * Customize it and finish by invoking the method {@link ToLoadFile#choose()} to show file chooser.
51       */
52      public static ToLoadFile forLoadingFile() {
53          return new FileChooserBuilderImpl(ChooseMode.LOAD_FILE);
54      }
55  
56      /**
57       * To choose files (in load mode).
58       * <p>
59       * Customize it and finish by invoking the method {@link ToLoadFile#choose()} to show file chooser.
60       */
61      public static ToLoadFiles forLoadingFiles() {
62          return new FileChooserBuilderImpl(ChooseMode.LOAD_FILES);
63      }
64  
65      /**
66       * To choose a directory (in load mode).
67       * <p>
68       * Customize it and finish by invoking the method {@link ToLoadDirectory#choose()} to show file chooser.
69       */
70      public static ToLoadDirectory forLoadingDirectory() {
71          return new FileChooserBuilderImpl(ChooseMode.LOAD_DIRECTORY);
72      }
73  
74      /**
75       * To choose a file (in save mode).
76       * <p>
77       * Customize it and finish by invoking the method {@link ToSave#choose()} to show file chooser.
78       */
79      public static ToSave forSaving() {
80          return new FileChooserBuilderImpl(ChooseMode.SAVE);
81      }
82  
83      public static void setCurrentDirectory(File dir) {
84          currentDirectory = dir;
85      }
86  
87      public static File getCurrentDirectory() {
88          return currentDirectory;
89      }
90  
91      public static boolean isCurrentDirectoryDefault() {
92          return currentDirectory.equals(DEFAULT_CURRENT_DIRECTORY_FILE);
93      }
94  
95      /**
96       * Logger.
97       */
98      private static final Log log = LogFactory.getLog(FileChooser.class);
99  
100     public static final File DEFAULT_CURRENT_DIRECTORY_FILE = new File(".");
101 
102     protected static File currentDirectory = DEFAULT_CURRENT_DIRECTORY_FILE;
103 
104     public interface FileResult {
105 
106         File getFile();
107 
108         List<File> getFiles();
109 
110     }
111 
112     private interface ChooseResult {
113 
114         FileResult choose();
115 
116     }
117 
118     public interface ToLoadFile extends ChooseResult {
119 
120         ToLoadFile setTitle(String title);
121 
122         ToLoadFile setApprovalText(String approvalText);
123 
124         ToLoadFile setParent(Component parent);
125 
126         ToLoadFile setPatternOrDescriptionFilters(List<String> patternOrDescriptionFilters);
127 
128         ToLoadFile setPatternOrDescriptionFilters(String pattern, String description, String... patternOrDescriptionFilters);
129 
130         ToLoadFile setFileFilters(FileFilter fileFilter, FileFilter... fileFilters);
131 
132         ToLoadFile setFileFilters(List<FileFilter> fileFilters);
133 
134         ToLoadFile setShowFiles(boolean showFiles);
135 
136         ToLoadFile setShowDirectories(boolean showDirectories);
137 
138         ToLoadFile setFileHidingEnabled(boolean useFileHiding);
139 
140         ToLoadFile setUseAcceptAllFileFilter(boolean useAcceptAllFileFilter);
141 
142         ToLoadFile setStartDirectory(File startDirectory);
143 
144         ToLoadFile setKeepCurrentDirectory(boolean keepCurrentDirectory);
145 
146     }
147 
148     public interface ToLoadFiles extends ChooseResult {
149 
150         ToLoadFile setTitle(String title);
151 
152         ToLoadFile setApprovalText(String approvalText);
153 
154         ToLoadFile setParent(Component parent);
155 
156         ToLoadFile setPatternOrDescriptionFilters(List<String> patternOrDescriptionFilters);
157 
158         ToLoadFile setPatternOrDescriptionFilters(String pattern, String description, String... patternOrDescriptionFilters);
159 
160         ToLoadFile setFileFilters(FileFilter fileFilter, FileFilter... fileFilters);
161 
162         ToLoadFile setFileFilters(List<FileFilter> fileFilters);
163 
164         ToLoadFile setShowFiles(boolean showFiles);
165 
166         ToLoadFile setShowDirectories(boolean showDirectories);
167 
168         ToLoadFile setFileHidingEnabled(boolean useFileHiding);
169 
170         ToLoadFile setUseAcceptAllFileFilter(boolean useAcceptAllFileFilter);
171 
172         ToLoadFile setStartDirectory(File startDirectory);
173 
174         ToLoadFile setKeepCurrentDirectory(boolean keepCurrentDirectory);
175 
176     }
177 
178     public interface ToLoadDirectory extends ChooseResult {
179 
180         ToLoadDirectory setTitle(String title);
181 
182         ToLoadDirectory setApprovalText(String approvalText);
183 
184         ToLoadDirectory setParent(Component parent);
185 
186         ToLoadDirectory setStartDirectory(File startDirectory);
187 
188         ToLoadDirectory setKeepCurrentDirectory(boolean keepCurrentDirectory);
189 
190     }
191 
192     public interface ToSave extends ChooseResult {
193 
194         ToSave setTitle(String title);
195 
196         ToSave setApprovalText(String approvalText);
197 
198         ToSave setParent(Component parent);
199 
200         ToSave setPatternOrDescriptionFilters(List<String> patternOrDescriptionFilters);
201 
202         ToSave setPatternOrDescriptionFilters(String pattern, String description, String... patternOrDescriptionFilters);
203 
204         ToSave setFileFilters(FileFilter fileFilter, FileFilter... fileFilters);
205 
206         ToSave setFileFilters(List<FileFilter> fileFilters);
207 
208         ToSave setFileHidingEnabled(boolean useFileHiding);
209 
210         ToSave setUseAcceptAllFileFilter(boolean useAcceptAllFileFilter);
211 
212         ToSave setFilename(String filename);
213 
214         ToSave setStartDirectory(File startDirectory);
215 
216         ToSave setKeepCurrentDirectory(boolean keepCurrentDirectory);
217 
218     }
219 
220     private enum ChooseMode {
221         LOAD_FILE,
222         LOAD_FILES,
223         LOAD_DIRECTORY,
224         SAVE
225     }
226 
227     static class EmptyResult implements FileResult {
228 
229         @Override
230         public File getFile() {
231             return null;
232         }
233 
234         @Override
235         public List<File> getFiles() {
236             return null;
237         }
238     }
239 
240     static class FileResultImpl implements FileResult {
241 
242         private List<File> result;
243 
244         public FileResultImpl(List<File> result) {
245             this.result = result;
246         }
247 
248         public FileResultImpl(File[] files) {
249             this.result = new ArrayList<>(Arrays.asList(files));
250         }
251 
252         public FileResultImpl(File file) {
253             this.result = ImmutableList.of(file);
254         }
255 
256         @Override
257         public File getFile() {
258             return CollectionUtils.size(result) > 0 ? result.get(0) : null;
259         }
260 
261         @Override
262         public List<File> getFiles() {
263             return result;
264         }
265     }
266 
267     static class FileChooserBuilderImpl implements ToLoadFile, ToLoadFiles, ToLoadDirectory, ToSave {
268 
269         private final ChooseMode chooseMode;
270 
271         protected String title;
272 
273         protected File startDirectory;
274 
275         protected String approvalText;
276 
277         protected Component parent;
278 
279         protected List<FileFilter> fileFilters;
280 
281         protected boolean showFiles;
282 
283         protected boolean showDirectories;
284 
285         protected boolean fileHidingEnabled;
286 
287         protected boolean useAcceptAllFileFilter;
288 
289         protected boolean keepCurrentDirectory = true;
290 
291         protected String filename;
292 
293         FileChooserBuilderImpl(ChooseMode chooseMode) {
294             this.chooseMode = chooseMode;
295         }
296 
297         @Override
298         public FileChooserBuilderImpl setTitle(String title) {
299             this.title = title;
300             return this;
301         }
302 
303         @Override
304         public FileChooserBuilderImpl setApprovalText(String approvalText) {
305             this.approvalText = approvalText;
306             return this;
307         }
308 
309         @Override
310         public FileChooserBuilderImpl setStartDirectory(File startDirectory) {
311             this.startDirectory = startDirectory;
312             return this;
313         }
314 
315         @Override
316         public FileChooserBuilderImpl setParent(Component parent) {
317             this.parent = parent;
318             return this;
319         }
320 
321         @Override
322         public FileChooserBuilderImpl setPatternOrDescriptionFilters(String pattern, String description, String... patternOrDescriptionFilters) {
323             List<String> r = new ArrayList<>();
324             r.add(pattern);
325             r.add(description);
326             r.addAll(Arrays.asList(patternOrDescriptionFilters));
327             setPatternOrDescriptionFilters(r);
328             return this;
329         }
330 
331         @Override
332         public FileChooserBuilderImpl setPatternOrDescriptionFilters(List<String> patternOrDescriptionFilters) {
333 
334             List<FileFilter> r = new ArrayList<>();
335             if (patternOrDescriptionFilters.size() % 2 != 0) {
336                 throw new IllegalArgumentException(
337                     "Arguments must be (pattern, description) couple");
338             }
339             for (int i = 0, nbFilters = patternOrDescriptionFilters.size() / 2; i < nbFilters; i++) {
340                 String pattern = patternOrDescriptionFilters.get(i * 2);
341                 String description = patternOrDescriptionFilters.get(i * 2 + 1);
342                 r.add(new FileChooserBuilderImpl.PatternChooserFilter(pattern, description));
343             }
344 
345             return setFileFilters(r);
346 
347         }
348 
349         @Override
350         public FileChooserBuilderImpl setFileFilters(FileFilter fileFilter, FileFilter... fileFilters) {
351             List<FileFilter> r = new ArrayList<>();
352             r.add(fileFilter);
353             r.addAll(Arrays.asList(fileFilters));
354             return setFileFilters(r);
355         }
356 
357         @Override
358         public FileChooserBuilderImpl setFileFilters(List<FileFilter> fileFilters) {
359             this.fileFilters = fileFilters;
360             return this;
361         }
362 
363         @Override
364         public FileChooserBuilderImpl setShowFiles(boolean showFiles) {
365             this.showFiles = showFiles;
366             return this;
367         }
368 
369         @Override
370         public FileChooserBuilderImpl setShowDirectories(boolean showDirectories) {
371             this.showDirectories = showDirectories;
372             return this;
373         }
374 
375         @Override
376         public FileChooserBuilderImpl setFileHidingEnabled(boolean fileHidingEnabled) {
377             this.fileHidingEnabled = fileHidingEnabled;
378             return this;
379         }
380 
381         @Override
382         public FileChooserBuilderImpl setUseAcceptAllFileFilter(boolean useAcceptAllFileFilter) {
383             this.useAcceptAllFileFilter = useAcceptAllFileFilter;
384             return this;
385         }
386 
387         @Override
388         public FileChooserBuilderImpl setFilename(String filename) {
389             this.filename = filename;
390             return this;
391         }
392 
393         @Override
394         public FileChooserBuilderImpl setKeepCurrentDirectory(boolean keepCurrentDirectory) {
395             this.keepCurrentDirectory = keepCurrentDirectory;
396             return this;
397         }
398 
399         @Override
400         public FileResult choose() {
401 
402             FileResult result = null;
403             switch (chooseMode) {
404                 case LOAD_FILE:
405                     result = chooseToLoadFile();
406                     break;
407                 case LOAD_FILES:
408                     result = chooseToLoadFiles();
409                     break;
410                 case LOAD_DIRECTORY:
411                     result = chooseToLoadDirectory();
412                     break;
413                 case SAVE:
414                     result = chooseToSave();
415                     break;
416             }
417 
418             return result;
419 
420         }
421 
422         protected FileResult chooseToLoadFile() {
423 
424             try {
425 
426                 File directory;
427 
428                 if (startDirectory == null) {
429                     directory = getCurrentDirectory();
430                 } else {
431                     directory = startDirectory;
432                 }
433 
434                 JFileChooser chooser = new JFileChooser(directory);
435 
436                 chooser.setDialogType(JFileChooser.CUSTOM_DIALOG);
437                 if (CollectionUtils.isNotEmpty(fileFilters)) {
438                     for (FileFilter filter : fileFilters) {
439                         chooser.addChoosableFileFilter(filter);
440                     }
441                     chooser.setFileFilter(fileFilters.get(0));
442                 }
443 
444                 chooser.setAcceptAllFileFilterUsed(useAcceptAllFileFilter);
445                 chooser.setFileHidingEnabled(fileHidingEnabled);
446 
447                 if (showFiles) {
448                     if (showDirectories) {
449                         chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
450                     } else {
451                         chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
452                     }
453                 } else if (showDirectories) {
454                     chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
455                 }
456 
457                 chooser.setDialogTitle(title);
458                 int returnVal = chooser.showDialog(parent, approvalText);
459                 if (returnVal == JFileChooser.APPROVE_OPTION) {
460                     File theFile = chooser.getSelectedFile();
461                     if (theFile != null) {
462                         if (keepCurrentDirectory) {
463                             setCurrentDirectory(theFile.getParentFile());
464                         }
465                         return new FileResultImpl(theFile.getAbsoluteFile());
466                     }
467                 }
468 
469             } catch (Exception eee) {
470                 if (log.isWarnEnabled())
471                     log.warn("Could not choose file to load", eee);
472             }
473 
474             return new EmptyResult();
475 
476         }
477 
478         protected FileResult chooseToLoadFiles() {
479 
480             try {
481 
482                 File directory;
483 
484                 if (startDirectory == null) {
485                     directory = getCurrentDirectory();
486                 } else {
487                     directory = startDirectory;
488                 }
489 
490                 JFileChooser chooser = new JFileChooser(directory);
491 
492                 chooser.setDialogType(JFileChooser.CUSTOM_DIALOG);
493                 if (CollectionUtils.isNotEmpty(fileFilters)) {
494                     for (FileFilter filter : fileFilters) {
495                         chooser.addChoosableFileFilter(filter);
496                     }
497                     chooser.setFileFilter(fileFilters.get(0));
498                 }
499 
500                 chooser.setMultiSelectionEnabled(true);
501                 // force details view
502                 Action details = chooser.getActionMap().get("viewTypeDetails");
503                 details.actionPerformed(null);
504 
505                 chooser.setAcceptAllFileFilterUsed(useAcceptAllFileFilter);
506                 chooser.setFileHidingEnabled(fileHidingEnabled);
507 
508                 if (showFiles) {
509                     if (showDirectories) {
510                         chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
511                     } else {
512                         chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
513                     }
514                 } else if (showDirectories) {
515                     chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
516                 }
517 
518                 chooser.setDialogTitle(title);
519                 int returnVal = chooser.showDialog(parent, approvalText);
520                 if (returnVal == JFileChooser.APPROVE_OPTION) {
521                     File[] theFiles = chooser.getSelectedFiles();
522                     if (theFiles != null) {
523                         if (keepCurrentDirectory && theFiles.length > 0) {
524                             setCurrentDirectory(theFiles[0].getParentFile());
525                         }
526                         return new FileResultImpl(Arrays.stream(theFiles).map(File::getAbsoluteFile).collect(Collectors.toList()));
527                     }
528                 }
529 
530             } catch (Exception eee) {
531                 if (log.isWarnEnabled())
532                     log.warn("Could not choose files to load", eee);
533             }
534 
535             return new EmptyResult();
536 
537         }
538 
539         protected FileResult chooseToLoadDirectory() {
540 
541             try {
542 
543                 File directory;
544 
545                 if (startDirectory == null) {
546                     directory = getCurrentDirectory();
547                 } else {
548                     directory = startDirectory;
549                 }
550 
551                 JFileChooser chooser = new JFileChooser(directory);
552 
553                 chooser.setDialogType(JFileChooser.CUSTOM_DIALOG);
554                 chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
555                 chooser.setDialogTitle(title);
556                 int returnVal = chooser.showDialog(parent, approvalText);
557                 if (returnVal == JFileChooser.APPROVE_OPTION) {
558                     File theFile = chooser.getSelectedFile();
559                     if (theFile != null) {
560                         if (theFile.isDirectory()) {
561                             if (keepCurrentDirectory) {
562                                 setCurrentDirectory(theFile);
563                             }
564                             return new FileResultImpl(theFile);
565                         }
566                     }
567                 }
568 
569             } catch (Exception eee) {
570                 if (log.isWarnEnabled())
571                     log.warn("Could not choose directory to load", eee);
572             }
573 
574             return new EmptyResult();
575 
576         }
577 
578         protected FileResult chooseToSave() {
579 
580             try {
581                 File directory;
582 
583                 if (startDirectory == null) {
584                     directory = getCurrentDirectory();
585                 } else {
586                     directory = startDirectory;
587                 }
588 
589                 File selectedFile = new File(directory, filename);
590                 JFileChooser chooser = new JFileChooser(selectedFile);
591 
592                 chooser.setDialogType(JFileChooser.SAVE_DIALOG);
593                 chooser.setSelectedFile(selectedFile);
594 
595                 if (CollectionUtils.isNotEmpty(fileFilters)) {
596                     for (FileFilter filter : fileFilters) {
597                         chooser.addChoosableFileFilter(filter);
598                     }
599                     chooser.setFileFilter(fileFilters.get(0));
600                 }
601                 chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
602                 chooser.setDialogTitle(title);
603 
604                 int returnVal = chooser.showDialog(parent, approvalText);
605                 if (returnVal == JFileChooser.APPROVE_OPTION) {
606                     File result = chooser.getSelectedFile();
607                     if (result != null) {
608                         if (keepCurrentDirectory) {
609                             setCurrentDirectory(result.getParentFile());
610                         }
611                         result = result.getAbsoluteFile();
612                         return new FileResultImpl(result);
613                     }
614                 }
615 
616             } catch (Exception eee) {
617                 if (log.isWarnEnabled())
618                     log.warn("Could not choose file to save", eee);
619             }
620 
621             return new EmptyResult();
622         }
623 
624         public static class PatternChooserFilter extends FileFilter {
625             protected String pattern;
626 
627             protected String description;
628 
629             public PatternChooserFilter(String pattern, String description) {
630                 this.pattern = StringUtils.lowerCase(pattern);
631                 this.description = description;
632             }
633 
634             @Override
635             public boolean accept(File f) {
636                 return f.isDirectory() || f.getAbsolutePath().toLowerCase().matches(pattern);
637             }
638 
639             @Override
640             public String getDescription() {
641                 return description;
642             }
643 
644         }
645 
646     }
647 
648     protected FileChooser() {
649         // no instance please
650     }
651 
652 }