001: /*
002: * Copyright 2005 Paul Hinds
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.tp23.gui.widget;
017:
018: import java.beans.PropertyChangeEvent;
019: import java.beans.PropertyChangeListener;
020: import java.io.File;
021: import java.lang.reflect.InvocationHandler;
022: import java.lang.reflect.Method;
023: import java.lang.reflect.Proxy;
024: import java.net.URI;
025:
026: import javax.swing.JFileChooser;
027: import javax.swing.filechooser.FileFilter;
028: import javax.swing.filechooser.FileSystemView;
029: import javax.swing.filechooser.FileView;
030: import javax.swing.plaf.FileChooserUI;
031: import javax.swing.plaf.basic.BasicDirectoryModel;
032: import javax.swing.plaf.basic.BasicFileChooserUI;
033:
034: /**
035: * A file chooser that makes a better default if the default directory is not available.
036: * Basically the default looks for the suggested directory and if it does not find it moves back
037: * up the directories. If still nothing is found (for example it may get to My Computer
038: * which you can not add file to) the home directory is selected, as in the default file chooser.
039: *
040: * To use this class call new DefaultingDirectoryChooser(boolean) and immediately call
041: * setDefaultDirectory(File) to determine the default. If you don't it behaves like the default JFileChooser
042: * Also if you call setCurrentDirectory(File) it will behave like JFileChooser
043: *
044: * This chooser has two modes CREATE_MODE and EXISTING_MODE. <br/><br/>
045: *
046: * In existing mode it is assumed the chooser is being used to identify an existing directory
047: * the default directory should exist if not the directory shown will be the next existing parent.<br/><br/>
048: *
049: * In create mode it is
050: * assumed that the last directory in the default directory is to be created and may not
051: * exist yet. Therefor the default directory displayed is the parent (with suitable defaulting)
052: * and the text box will contain the name of the last directory.
053: * e.g. if default is C:\Program Files\MyApp C:\Program Files will be displayed and
054: * MyApp will be shown in the text box (even if it does not exist yet) If C:\Program Files does not exist
055: * C:\ will be shown as with existing mode.
056: * Choosing select can lead to the creation of the file. It will not be automatically
057: * created that is the responsibility of the client code.<br/>
058: *
059: * This class uses FileSystemView and will only display folders that return true to fsv.isFileSystem()
060: *
061: * This class also uses a bit of a hack by in CREATE_MODE it sets the selectables to files and directories
062: * although only shows the directories this way a non-existing Directory can be chosen. This is the only way I can
063: * get it to work on my Java version but I would not be supprised if it did not work on
064: * other versions of the Java API. It also means that setFileSelectionMode() should
065: * never be called by clients as it will break this hack.
066: *
067: * N.B. createMode is incompatible with multiselect mode
068: * @author Paul Hinds
069: * @version 1.0
070: */
071: public class DefaultingDirectoryChooser extends JFileChooser implements
072: DirectoryChooser {
073:
074: public static final boolean CREATE_MODE = true;
075: public static final boolean EXISTING_MODE = false;
076:
077: /**
078: * Determines that the default includes a directory name that is requried
079: * e.g. C:\Program Files\MyApp where even if Program Files does not exist
080: * MyApp should be part of the default even if it does not exist.
081: */
082: private boolean createMode = false;
083: private File selectedFile = null;
084: private String desiredName = null;
085:
086: public DefaultingDirectoryChooser(boolean createMode) {
087: this .createMode = createMode;
088: if (createMode) {
089: setMultiSelectionEnabled(false);
090: setFileSelectionMode(FILES_AND_DIRECTORIES);
091: removeChoosableFileFilter(getAcceptAllFileFilter());
092: addChoosableFileFilter(new FileFilter() {
093: public boolean accept(File f) {
094: if (f.exists() && f.isDirectory()) {
095: return true;
096: }
097: if (!f.exists()
098: && getFileSystemView()
099: .getParentDirectory(f)
100: .isDirectory()) {
101: return true;
102: }
103: return false;
104: }
105:
106: public String getDescription() {
107: return "Directories";
108: }
109: });
110: initDefaultNameListener();
111: } else {
112: setFileSelectionMode(DIRECTORIES_ONLY);
113: }
114: }
115:
116: public void initDefaultNameListener() {
117: addPropertyChangeListener(
118: JFileChooser.DIRECTORY_CHANGED_PROPERTY,
119: new PropertyChangeListener() {
120: public void propertyChange(PropertyChangeEvent evt) {
121: File directory = (File) evt.getNewValue();
122: File desired = new File(directory, desiredName);
123: DefaultingDirectoryChooser.this
124: .setCurrentDirectory(directory);
125: if (directory != null) {
126: setSelectedFile(desired);
127: }
128: }
129: });
130: }
131:
132: /**
133: * Sets the default directory.
134: * @param dir the current directory to point to
135: * @todo Implement this javax.swing.JFileChooser method
136: */
137: public void setDefaultDirectory(File dir) {
138: if (dir == null) {
139: return;// called by the default consructor
140: }
141: if (createMode) {
142: if (desiredName == null) {
143: desiredName = dir.getName();
144: }
145: File selected = determineCreateDir(dir);
146: super .setCurrentDirectory(selected.getParentFile());
147: setSelectedFile(selected);
148: } else {
149: super .setCurrentDirectory(determineExistingDir(dir));
150: }
151: }
152:
153: private File determineExistingDir(File defaultDir) {
154: if (defaultDir.exists())
155: return defaultDir;
156: FileSystemView fsv = this .getFileSystemView();
157: File validParent = getFSVParent(fsv, defaultDir);
158: if (validParent != null) {
159: return validParent;
160: } else {
161: return fsv.getHomeDirectory();
162: }
163: }
164:
165: /**
166: * Select a base path for the default even if it does not exist. The order of preference is as follows
167: * if the directory exists use it
168: * if the parent does not exist try finding the next parent
169: * if the next parent is a root directory e.g. / or C:\ use the users home directory
170: * @param defaultDir File
171: * @return File
172: */
173: private File determineCreateDir(File defaultDir) {
174: if (defaultDir.exists()) {
175: return defaultDir;
176: }
177: FileSystemView fsv = this .getFileSystemView();
178: String dirName = defaultDir.getName();
179: File validParent = getFSVParent(fsv, defaultDir);
180: if (validParent != null) {
181: return new File(validParent, dirName);
182: } else {
183: return new File(fsv.getHomeDirectory(), dirName);
184: }
185: }
186:
187: /**
188: * Step back up trying to find a parent if none if found return null.
189: * null can be found if the path starts e:\ and e: does not exist
190: * @param fsv FileSystemView
191: * @param dir File
192: * @return File
193: */
194: private File getFSVParent(FileSystemView fsv, File dir) {
195: File parent = dir.getParentFile();
196: if (fsv.isRoot(parent) && !parent.exists()) {
197: return null;
198: }
199: if (!parent.exists() || !fsv.isFileSystem(parent)) {
200: parent = getFSVParent(fsv, parent);
201: }
202: return parent;
203: }
204:
205: public static void main(String[] args) {
206: DefaultingDirectoryChooser chooser = new DefaultingDirectoryChooser(
207: CREATE_MODE);
208: chooser
209: .setDefaultDirectory(new File("/usr/local/dibble/MyApp"));
210:
211: chooser.showDialog(null, "Select");
212: System.out.println("Selected:"
213: + chooser.getSelectedFile().getAbsolutePath());
214: System.exit(0);
215: }
216:
217: }
|