/* * Copyright 2005 Paul Hinds * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.tp23.gui.widget; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import javax.swing.JFileChooser; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileSystemView; /** * A file chooser that makes a beter default if the default directory is not available. * Basically the default looks for the suggested directory and if it does not find it moves back * up the directories. If still nothing is found (for example it may get to My Computer * which you can not add file to) the home directory is selected, as in the default file chooser. * * To use this class call new DefaultingDirectoryChooser(boolean) and immediately call * setDefaultDirectory(File) to determine the default. If you don't it behaves like the default JFileChooser * Also if you call setCurrentDirectory(File) it will behave like JFileChooser * * This chooser has two modes CREATE_MODE and EXISTING_MODE.

* * In existing mode it is assumed the chooser is being used to identify an existing directory * the default directory should exist if not the directory shown will be the next existing parent.

* * In create mode it is * assumed that the last directory in the default directory is to be created and may not * exist yet. Therefor the default directory displayed is the parent (with suitable defaulting) * and the text box will contain the name of the last directory. * e.g. if default is C:\Program Files\MyApp C:\Program Files will be displayed and * MyApp will be shown in the text box (even if it does not exist yet) If C:\Program Files does not exist * C:\ will be shown as with existing mode. * Choosing select can lead to the creation of the file. It will not be automatically * created that is the responsibility of the client code.
* * This class uses FileSystemView and will only display folders that return true to fsv.isFileSystem() * * This class also uses a bit of a hack by in CREATE_MODE it sets the selectables to files and directories * although only shows the directories this way a non-existing Directory can be chosen. This is the only way I can * get it to work on my Java version but I would not be supprised if it did not work on * other versions of the Java API. It also means that setFileSelectionMode() should * never be called by clients as it will break this hack. * @author Paul Hinds * @version 1.0 */ public class DefaultingDirectoryChooser extends JFileChooser{ public static final boolean CREATE_MODE = true; public static final boolean EXISTING_MODE = false; /** * Determines that the default includes a directory name that is requried * e.g. C:\Program Files\MyApp where even if Program Files does not exist * MyApp should be part of the default even if it does not exist. */ private boolean createMode = false; private File selectedFile = null; private String desiredName = null; public DefaultingDirectoryChooser(boolean createMode) { this.createMode=createMode; if(createMode){ setFileSelectionMode(FILES_AND_DIRECTORIES); removeChoosableFileFilter(getAcceptAllFileFilter()); addChoosableFileFilter(new FileFilter(){ public boolean accept(File f) { if(f.exists() && f.isDirectory())return true; if(!f.exists() && getFileSystemView().getParentDirectory(f).isDirectory())return true; return false; } public String getDescription() { return "Folders"; } }); addPropertyChangeListener(JFileChooser.DIRECTORY_CHANGED_PROPERTY, new PropertyChangeListener(){ public void propertyChange(PropertyChangeEvent evt) { File directory = (File)evt.getNewValue(); DefaultingDirectoryChooser.this.setCurrentDirectory(new File(directory, "newfolder")); setSelectedFile(new File(directory, desiredName)); } }); } else { setFileSelectionMode(DIRECTORIES_ONLY); } } /** * Sets the default directory. * @param dir the current directory to point to * @todo Implement this javax.swing.JFileChooser method */ public void setDefaultDirectory(File dir) { if(dir==null)return;// called by the default consructor if(createMode){ if(desiredName == null)desiredName = dir.getName(); File selected = determineCreateDir(dir); super.setCurrentDirectory(selected.getParentFile()); setSelectedFile(selected); }else{ super.setCurrentDirectory(determineExistingDir(dir)); } } private File determineExistingDir(File defaultDir){ if(defaultDir.exists())return defaultDir; FileSystemView fsv = this.getFileSystemView(); File validParent = getFSVParent(fsv,defaultDir); if(validParent!=null) { return validParent; } else { return fsv.getHomeDirectory(); } } /** * Select a base path for the default even if it does not exist. The order of preference is as follows * if the directory exists use it * if the parent does not exist try finding the next parent * if the next parent is a root directory e.g. / or C:\ use the users home directory * @param defaultDir File * @return File */ private File determineCreateDir(File defaultDir){ if(defaultDir.exists())return defaultDir; FileSystemView fsv = this.getFileSystemView(); String dirName = defaultDir.getName(); File validParent = getFSVParent(fsv,defaultDir); if(validParent!=null){ return new File(validParent, dirName); } else { return new File(fsv.getHomeDirectory(), dirName); } } /** * Step back up trying to find a parent if none if found return null. * null can be found if the path starts e:\ and e: does not exist * @param fsv FileSystemView * @param dir File * @return File */ private File getFSVParent(FileSystemView fsv,File dir){ File parent = dir.getParentFile(); if(fsv.isRoot(parent) && !parent.exists()){ return null; } if(!parent.exists() || !fsv.isFileSystem(parent)){ parent = getFSVParent(fsv,parent); } return parent; } public static void main(String[] args) { DefaultingDirectoryChooser chooser = new DefaultingDirectoryChooser(CREATE_MODE); chooser.setDefaultDirectory(new File("/usr/local/dibble/MyApp")); chooser.showDialog(null,"Select"); System.out.println("Selected:"+chooser.getSelectedFile().getAbsolutePath()); System.exit(0); } }