001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.widgets; 003 004import java.awt.Component; 005import java.io.File; 006import java.util.Collection; 007import java.util.Collections; 008 009import javax.swing.JFileChooser; 010import javax.swing.filechooser.FileFilter; 011 012import org.openstreetmap.josm.Main; 013import org.openstreetmap.josm.actions.DiskAccessAction; 014import org.openstreetmap.josm.actions.ExtensionFileFilter; 015import org.openstreetmap.josm.actions.SaveActionBase; 016 017/** 018 * A chained utility class used to create and open {@link JFileChooser} dialogs.<br> 019 * Use only this class if you need to control specifically your JFileChooser dialog.<br> 020 * <p> 021 * A simpler usage is to call the {@link DiskAccessAction#createAndOpenFileChooser} methods. 022 * 023 * @since 5438 024 */ 025public class JFileChooserManager { 026 private final boolean open; 027 private final String lastDirProperty; 028 private final String curDir; 029 030 private JFileChooser fc; 031 032 /** 033 * Creates a new {@code JFileChooserManager}. 034 * @param open If true, "Open File" dialogs will be created. If false, "Save File" dialogs will be created. 035 * @see #createFileChooser 036 */ 037 public JFileChooserManager(boolean open) { 038 this(open, null); 039 } 040 041 /** 042 * Creates a new {@code JFileChooserManager}. 043 * @param open If true, "Open File" dialogs will be created. If false, "Save File" dialogs will be created. 044 * @param lastDirProperty The name of the property used to get the last directory. This directory is used to initialize the JFileChooser. 045 * Then, if the user effectively chooses a file or a directory, this property will be updated to the directory path. 046 * @see #createFileChooser 047 */ 048 public JFileChooserManager(boolean open, String lastDirProperty) { 049 this(open, lastDirProperty, null); 050 } 051 052 /** 053 * Creates a new {@code JFileChooserManager}. 054 * @param open If true, "Open File" dialogs will be created. If false, "Save File" dialogs will be created. 055 * @param lastDirProperty The name of the property used to get the last directory. This directory is used to initialize the JFileChooser. 056 * Then, if the user effectively chooses a file or a directory, this property will be updated to the directory path. 057 * @param defaultDir The default directory used to initialize the JFileChooser if the {@code lastDirProperty} property value is missing. 058 * @see #createFileChooser 059 */ 060 public JFileChooserManager(boolean open, String lastDirProperty, String defaultDir) { 061 this.open = open; 062 this.lastDirProperty = lastDirProperty == null || lastDirProperty.isEmpty() ? "lastDirectory" : lastDirProperty; 063 this.curDir = Main.pref.get(this.lastDirProperty).isEmpty() ? 064 (defaultDir == null || defaultDir.isEmpty() ? "." : defaultDir) 065 : Main.pref.get(this.lastDirProperty); 066 } 067 068 /** 069 * Replies the {@code JFileChooser} that has been previously created. 070 * @return The {@code JFileChooser} that has been previously created, or {@code null} if it has not been created yet. 071 * @see #createFileChooser 072 */ 073 public final JFileChooser getFileChooser() { 074 return fc; 075 } 076 077 /** 078 * Replies the initial directory used to construct the {@code JFileChooser}. 079 * @return The initial directory used to construct the {@code JFileChooser}. 080 */ 081 public final String getInitialDirectory() { 082 return curDir; 083 } 084 085 /** 086 * Creates a new {@link JFileChooser} with default settings. All files will be accepted. 087 * @return this 088 */ 089 public final JFileChooserManager createFileChooser() { 090 return doCreateFileChooser(false, null, null, null, null, JFileChooser.FILES_ONLY, false); 091 } 092 093 /** 094 * Creates a new {@link JFileChooser} with given settings for a single {@code FileFilter}. 095 * 096 * @param multiple If true, makes the dialog allow multiple file selections 097 * @param title The string that goes in the dialog window's title bar 098 * @param filter The only file filter that will be proposed by the dialog 099 * @param selectionMode The selection mode that allows the user to:<br><ul> 100 * <li>just select files ({@code JFileChooser.FILES_ONLY})</li> 101 * <li>just select directories ({@code JFileChooser.DIRECTORIES_ONLY})</li> 102 * <li>select both files and directories ({@code JFileChooser.FILES_AND_DIRECTORIES})</li></ul> 103 * @return this 104 * @see DiskAccessAction#createAndOpenFileChooser(boolean, boolean, String, FileFilter, int, String) 105 */ 106 public final JFileChooserManager createFileChooser(boolean multiple, String title, FileFilter filter, int selectionMode) { 107 doCreateFileChooser(multiple, title, Collections.singleton(filter), filter, null, selectionMode, false); 108 getFileChooser().setAcceptAllFileFilterUsed(false); 109 return this; 110 } 111 112 /** 113 * Creates a new {@link JFileChooser} with given settings for a collection of {@code FileFilter}s. 114 * 115 * @param multiple If true, makes the dialog allow multiple file selections 116 * @param title The string that goes in the dialog window's title bar 117 * @param filters The file filters that will be proposed by the dialog 118 * @param defaultFilter The file filter that will be selected by default 119 * @param selectionMode The selection mode that allows the user to:<br><ul> 120 * <li>just select files ({@code JFileChooser.FILES_ONLY})</li> 121 * <li>just select directories ({@code JFileChooser.DIRECTORIES_ONLY})</li> 122 * <li>select both files and directories ({@code JFileChooser.FILES_AND_DIRECTORIES})</li></ul> 123 * @return this 124 * @see DiskAccessAction#createAndOpenFileChooser(boolean, boolean, String, Collection, FileFilter, int, String) 125 */ 126 public final JFileChooserManager createFileChooser(boolean multiple, String title, Collection<? extends FileFilter> filters, FileFilter defaultFilter, int selectionMode) { 127 return doCreateFileChooser(multiple, title, filters, defaultFilter, null, selectionMode, false); 128 } 129 130 /** 131 * Creates a new {@link JFileChooser} with given settings for a file extension. 132 * 133 * @param multiple If true, makes the dialog allow multiple file selections 134 * @param title The string that goes in the dialog window's title bar 135 * @param extension The file extension that will be selected as the default file filter 136 * @param allTypes If true, all the files types known by JOSM will be proposed in the "file type" combobox. 137 * If false, only the file filters that include {@code extension} will be proposed 138 * @param selectionMode The selection mode that allows the user to:<br><ul> 139 * <li>just select files ({@code JFileChooser.FILES_ONLY})</li> 140 * <li>just select directories ({@code JFileChooser.DIRECTORIES_ONLY})</li> 141 * <li>select both files and directories ({@code JFileChooser.FILES_AND_DIRECTORIES})</li></ul> 142 * @return this 143 * @see DiskAccessAction#createAndOpenFileChooser(boolean, boolean, String, FileFilter, int, String) 144 */ 145 public final JFileChooserManager createFileChooser(boolean multiple, String title, String extension, boolean allTypes, int selectionMode) { 146 return doCreateFileChooser(multiple, title, null, null, extension, selectionMode, allTypes); 147 } 148 149 private final JFileChooserManager doCreateFileChooser(boolean multiple, String title, Collection<? extends FileFilter> filters, FileFilter defaultFilter, String extension, int selectionMode, boolean allTypes) { 150 fc = new JFileChooser(new File(curDir)); 151 if (title != null) { 152 fc.setDialogTitle(title); 153 } 154 155 fc.setFileSelectionMode(selectionMode); 156 fc.setMultiSelectionEnabled(multiple); 157 fc.setAcceptAllFileFilterUsed(false); 158 159 if (filters != null) { 160 for (FileFilter filter : filters) { 161 fc.addChoosableFileFilter(filter); 162 } 163 if (defaultFilter != null) { 164 fc.setFileFilter(defaultFilter); 165 } 166 } else if (open) { 167 ExtensionFileFilter.applyChoosableImportFileFilters(fc, extension, allTypes); 168 } else { 169 ExtensionFileFilter.applyChoosableExportFileFilters(fc, extension, allTypes); 170 } 171 return this; 172 } 173 174 /** 175 * Opens the {@code JFileChooser} that has been created. Nothing happens if it has not been created yet. 176 * @return the {@code JFileChooser} if the user effectively choses a file or directory. {@code null} if the user cancelled the dialog. 177 */ 178 public final JFileChooser openFileChooser() { 179 return openFileChooser(null); 180 } 181 182 /** 183 * Opens the {@code JFileChooser} that has been created and waits for the user to choose a file/directory, or cancel the dialog.<br> 184 * Nothing happens if the dialog has not been created yet.<br> 185 * When the user choses a file or directory, the {@code lastDirProperty} is updated to the chosen directory path. 186 * 187 * @param parent The Component used as the parent of the JFileChooser. If null, uses {@code Main.parent}. 188 * @return the {@code JFileChooser} if the user effectively choses a file or directory. {@code null} if the user cancelled the dialog. 189 */ 190 public JFileChooser openFileChooser(Component parent) { 191 if (fc != null) { 192 if (parent == null) { 193 parent = Main.parent; 194 } 195 196 int answer = open ? fc.showOpenDialog(parent) : fc.showSaveDialog(parent); 197 if (answer != JFileChooser.APPROVE_OPTION) { 198 return null; 199 } 200 201 if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir)) { 202 Main.pref.put(lastDirProperty, fc.getCurrentDirectory().getAbsolutePath()); 203 } 204 205 if (!open) { 206 File file = fc.getSelectedFile(); 207 if (!SaveActionBase.confirmOverwrite(file)) { 208 return null; 209 } 210 } 211 } 212 return fc; 213 } 214}