1 /*
2  * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package javax.swing;
27 
28 import java.awt.AWTEvent;
29 import java.awt.BorderLayout;
30 import java.awt.Component;
31 import java.awt.Container;
32 import java.awt.Dialog;
33 import java.awt.EventQueue;
34 import java.awt.Frame;
35 import java.awt.GraphicsEnvironment;
36 import java.awt.HeadlessException;
37 import java.awt.Toolkit;
38 import java.awt.Window;
39 import java.awt.event.ActionEvent;
40 import java.awt.event.ActionListener;
41 import java.awt.event.HierarchyEvent;
42 import java.awt.event.HierarchyListener;
43 import java.awt.event.InputEvent;
44 import java.awt.event.WindowAdapter;
45 import java.awt.event.WindowEvent;
46 import java.beans.BeanProperty;
47 import java.beans.JavaBean;
48 import java.beans.PropertyChangeEvent;
49 import java.beans.PropertyChangeListener;
50 import java.io.File;
51 import java.io.IOException;
52 import java.io.InvalidObjectException;
53 import java.io.ObjectInputStream;
54 import java.io.ObjectOutputStream;
55 import java.io.Serializable;
56 import java.lang.ref.WeakReference;
57 import java.util.Vector;
58 
59 import javax.accessibility.Accessible;
60 import javax.accessibility.AccessibleContext;
61 import javax.accessibility.AccessibleRole;
62 import javax.swing.event.EventListenerList;
63 import javax.swing.filechooser.FileFilter;
64 import javax.swing.filechooser.FileSystemView;
65 import javax.swing.filechooser.FileView;
66 import javax.swing.plaf.FileChooserUI;
67 
68 /**
69  * <code>JFileChooser</code> provides a simple mechanism for the user to
70  * choose a file.
71  * For information about using <code>JFileChooser</code>, see
72  * <a
73  href="https://docs.oracle.com/javase/tutorial/uiswing/components/filechooser.html">How to Use File Choosers</a>,
74  * a section in <em>The Java Tutorial</em>.
75  *
76  * <p>
77  *
78  * The following code pops up a file chooser for the user's home directory that
79  * sees only .jpg and .gif images:
80  * <pre>
81  *    JFileChooser chooser = new JFileChooser();
82  *    FileNameExtensionFilter filter = new FileNameExtensionFilter(
83  *        "JPG &amp; GIF Images", "jpg", "gif");
84  *    chooser.setFileFilter(filter);
85  *    int returnVal = chooser.showOpenDialog(parent);
86  *    if(returnVal == JFileChooser.APPROVE_OPTION) {
87  *       System.out.println("You chose to open this file: " +
88  *            chooser.getSelectedFile().getName());
89  *    }
90  * </pre>
91  * <p>
92  * <strong>Warning:</strong> Swing is not thread safe. For more
93  * information see <a
94  * href="package-summary.html#threading">Swing's Threading
95  * Policy</a>.
96  *
97  * @author Jeff Dinkins
98  * @since 1.2
99  */
100 @JavaBean(defaultProperty = "UI", description = "A component which allows for the interactive selection of a file.")
101 @SwingContainer(false)
102 @SuppressWarnings("serial") // Superclass is not serializable across versions
103 public class JFileChooser extends JComponent implements Accessible {
104 
105     /**
106      * @see #getUIClassID
107      * @see #readObject
108      */
109     private static final String uiClassID = "FileChooserUI";
110 
111     // ************************
112     // ***** Dialog Types *****
113     // ************************
114 
115     /**
116      * Type value indicating that the <code>JFileChooser</code> supports an
117      * "Open" file operation.
118      */
119     public static final int OPEN_DIALOG = 0;
120 
121     /**
122      * Type value indicating that the <code>JFileChooser</code> supports a
123      * "Save" file operation.
124      */
125     public static final int SAVE_DIALOG = 1;
126 
127     /**
128      * Type value indicating that the <code>JFileChooser</code> supports a
129      * developer-specified file operation.
130      */
131     public static final int CUSTOM_DIALOG = 2;
132 
133 
134     // ********************************
135     // ***** Dialog Return Values *****
136     // ********************************
137 
138     /**
139      * Return value if cancel is chosen.
140      */
141     public static final int CANCEL_OPTION = 1;
142 
143     /**
144      * Return value if approve (yes, ok) is chosen.
145      */
146     public static final int APPROVE_OPTION = 0;
147 
148     /**
149      * Return value if an error occurred.
150      */
151     public static final int ERROR_OPTION = -1;
152 
153 
154     // **********************************
155     // ***** JFileChooser properties *****
156     // **********************************
157 
158 
159     /** Instruction to display only files. */
160     public static final int FILES_ONLY = 0;
161 
162     /** Instruction to display only directories. */
163     public static final int DIRECTORIES_ONLY = 1;
164 
165     /** Instruction to display both files and directories. */
166     public static final int FILES_AND_DIRECTORIES = 2;
167 
168     /** Instruction to cancel the current selection. */
169     public static final String CANCEL_SELECTION = "CancelSelection";
170 
171     /**
172      * Instruction to approve the current selection
173      * (same as pressing yes or ok).
174      */
175     public static final String APPROVE_SELECTION = "ApproveSelection";
176 
177     /** Identifies change in the text on the approve (yes, ok) button. */
178     public static final String APPROVE_BUTTON_TEXT_CHANGED_PROPERTY = "ApproveButtonTextChangedProperty";
179 
180     /**
181      * Identifies change in the tooltip text for the approve (yes, ok)
182      * button.
183      */
184     public static final String APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY = "ApproveButtonToolTipTextChangedProperty";
185 
186     /** Identifies change in the mnemonic for the approve (yes, ok) button. */
187     public static final String APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY = "ApproveButtonMnemonicChangedProperty";
188 
189     /** Instruction to display the control buttons. */
190     public static final String CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY = "ControlButtonsAreShownChangedProperty";
191 
192     /** Identifies user's directory change. */
193     public static final String DIRECTORY_CHANGED_PROPERTY = "directoryChanged";
194 
195     /** Identifies change in user's single-file selection. */
196     public static final String SELECTED_FILE_CHANGED_PROPERTY = "SelectedFileChangedProperty";
197 
198     /** Identifies change in user's multiple-file selection. */
199     public static final String SELECTED_FILES_CHANGED_PROPERTY = "SelectedFilesChangedProperty";
200 
201     /** Enables multiple-file selections. */
202     public static final String MULTI_SELECTION_ENABLED_CHANGED_PROPERTY = "MultiSelectionEnabledChangedProperty";
203 
204     /**
205      * Says that a different object is being used to find available drives
206      * on the system.
207      */
208     public static final String FILE_SYSTEM_VIEW_CHANGED_PROPERTY = "FileSystemViewChanged";
209 
210     /**
211      * Says that a different object is being used to retrieve file
212      * information.
213      */
214     public static final String FILE_VIEW_CHANGED_PROPERTY = "fileViewChanged";
215 
216     /** Identifies a change in the display-hidden-files property. */
217     public static final String FILE_HIDING_CHANGED_PROPERTY = "FileHidingChanged";
218 
219     /** User changed the kind of files to display. */
220     public static final String FILE_FILTER_CHANGED_PROPERTY = "fileFilterChanged";
221 
222     /**
223      * Identifies a change in the kind of selection (single,
224      * multiple, etc.).
225      */
226     public static final String FILE_SELECTION_MODE_CHANGED_PROPERTY = "fileSelectionChanged";
227 
228     /**
229      * Says that a different accessory component is in use
230      * (for example, to preview files).
231      */
232     public static final String ACCESSORY_CHANGED_PROPERTY = "AccessoryChangedProperty";
233 
234     /**
235      * Identifies whether a the AcceptAllFileFilter is used or not.
236      */
237     public static final String ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY = "acceptAllFileFilterUsedChanged";
238 
239     /** Identifies a change in the dialog title. */
240     public static final String DIALOG_TITLE_CHANGED_PROPERTY = "DialogTitleChangedProperty";
241 
242     /**
243      * Identifies a change in the type of files displayed (files only,
244      * directories only, or both files and directories).
245      */
246     public static final String DIALOG_TYPE_CHANGED_PROPERTY = "DialogTypeChangedProperty";
247 
248     /**
249      * Identifies a change in the list of predefined file filters
250      * the user can choose from.
251      */
252     public static final String CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY = "ChoosableFileFilterChangedProperty";
253 
254     // ******************************
255     // ***** instance variables *****
256     // ******************************
257 
258     private String dialogTitle = null;
259     private String approveButtonText = null;
260     private String approveButtonToolTipText = null;
261     private int approveButtonMnemonic = 0;
262 
263     private Vector<FileFilter> filters = new Vector<FileFilter>(5);
264     private JDialog dialog = null;
265     private int dialogType = OPEN_DIALOG;
266     private int returnValue = ERROR_OPTION;
267     private JComponent accessory = null;
268 
269     private FileView fileView = null;
270 
271     private boolean controlsShown = true;
272 
273     private boolean useFileHiding = true;
274     private static final String SHOW_HIDDEN_PROP = "awt.file.showHiddenFiles";
275 
276     // Listens to changes in the native setting for showing hidden files.
277     // The Listener is removed and the native setting is ignored if
278     // setFileHidingEnabled() is ever called.
279     private transient PropertyChangeListener showFilesListener = null;
280 
281     private int fileSelectionMode = FILES_ONLY;
282 
283     private boolean multiSelectionEnabled = false;
284 
285     private boolean useAcceptAllFileFilter = true;
286 
287     private boolean dragEnabled = false;
288 
289     private FileFilter fileFilter = null;
290 
291     private FileSystemView fileSystemView = null;
292 
293     private File currentDirectory = null;
294     private File selectedFile = null;
295     private File[] selectedFiles;
296 
297     // *************************************
298     // ***** JFileChooser Constructors *****
299     // *************************************
300 
301     /**
302      * Constructs a <code>JFileChooser</code> pointing to the user's
303      * default directory. This default depends on the operating system.
304      * It is typically the "My Documents" folder on Windows, and the
305      * user's home directory on Unix.
306      */
JFileChooser()307     public JFileChooser() {
308         this((File) null, (FileSystemView) null);
309     }
310 
311     /**
312      * Constructs a <code>JFileChooser</code> using the given path.
313      * Passing in a <code>null</code>
314      * string causes the file chooser to point to the user's default directory.
315      * This default depends on the operating system. It is
316      * typically the "My Documents" folder on Windows, and the user's
317      * home directory on Unix.
318      *
319      * @param currentDirectoryPath  a <code>String</code> giving the path
320      *                          to a file or directory
321      */
JFileChooser(String currentDirectoryPath)322     public JFileChooser(String currentDirectoryPath) {
323         this(currentDirectoryPath, (FileSystemView) null);
324     }
325 
326     /**
327      * Constructs a <code>JFileChooser</code> using the given <code>File</code>
328      * as the path. Passing in a <code>null</code> file
329      * causes the file chooser to point to the user's default directory.
330      * This default depends on the operating system. It is
331      * typically the "My Documents" folder on Windows, and the user's
332      * home directory on Unix.
333      *
334      * @param currentDirectory  a <code>File</code> object specifying
335      *                          the path to a file or directory
336      */
JFileChooser(File currentDirectory)337     public JFileChooser(File currentDirectory) {
338         this(currentDirectory, (FileSystemView) null);
339     }
340 
341     /**
342      * Constructs a <code>JFileChooser</code> using the given
343      * <code>FileSystemView</code>.
344      *
345      * @param fsv a {@code FileSystemView}
346      */
JFileChooser(FileSystemView fsv)347     public JFileChooser(FileSystemView fsv) {
348         this((File) null, fsv);
349     }
350 
351 
352     /**
353      * Constructs a <code>JFileChooser</code> using the given current directory
354      * and <code>FileSystemView</code>.
355      *
356      * @param currentDirectory a {@code File} object specifying the path to a
357      *                         file or directory
358      * @param fsv a {@code FileSystemView}
359      */
JFileChooser(File currentDirectory, FileSystemView fsv)360     public JFileChooser(File currentDirectory, FileSystemView fsv) {
361         setup(fsv);
362         setCurrentDirectory(currentDirectory);
363     }
364 
365     /**
366      * Constructs a <code>JFileChooser</code> using the given current directory
367      * path and <code>FileSystemView</code>.
368      *
369      * @param currentDirectoryPath a {@code String} specifying the path to a file
370      *                             or directory
371      * @param fsv a {@code FileSystemView}
372      */
JFileChooser(String currentDirectoryPath, FileSystemView fsv)373     public JFileChooser(String currentDirectoryPath, FileSystemView fsv) {
374         setup(fsv);
375         if(currentDirectoryPath == null) {
376             setCurrentDirectory(null);
377         } else {
378             setCurrentDirectory(fileSystemView.createFileObject(currentDirectoryPath));
379         }
380     }
381 
382     /**
383      * Performs common constructor initialization and setup.
384      *
385      * @param view the {@code FileSystemView} used for setup
386      */
setup(FileSystemView view)387     protected void setup(FileSystemView view) {
388         installShowFilesListener();
389         installHierarchyListener();
390 
391         if(view == null) {
392             view = FileSystemView.getFileSystemView();
393         }
394         setFileSystemView(view);
395         updateUI();
396         if(isAcceptAllFileFilterUsed()) {
397             setFileFilter(getAcceptAllFileFilter());
398         }
399         enableEvents(AWTEvent.MOUSE_EVENT_MASK);
400     }
401 
installHierarchyListener()402     private void installHierarchyListener() {
403         addHierarchyListener(new FCHierarchyListener());
404     }
405 
installShowFilesListener()406     private void installShowFilesListener() {
407         // Track native setting for showing hidden files
408         Toolkit tk = Toolkit.getDefaultToolkit();
409         Object showHiddenProperty = tk.getDesktopProperty(SHOW_HIDDEN_PROP);
410         if (showHiddenProperty instanceof Boolean) {
411             useFileHiding = !((Boolean)showHiddenProperty).booleanValue();
412             showFilesListener = new WeakPCL(this);
413             tk.addPropertyChangeListener(SHOW_HIDDEN_PROP, showFilesListener);
414         }
415     }
416 
417     /**
418      * Sets the <code>dragEnabled</code> property,
419      * which must be <code>true</code> to enable
420      * automatic drag handling (the first part of drag and drop)
421      * on this component.
422      * The <code>transferHandler</code> property needs to be set
423      * to a non-<code>null</code> value for the drag to do
424      * anything.  The default value of the <code>dragEnabled</code>
425      * property
426      * is <code>false</code>.
427      *
428      * <p>
429      *
430      * When automatic drag handling is enabled,
431      * most look and feels begin a drag-and-drop operation
432      * whenever the user presses the mouse button over an item
433      * and then moves the mouse a few pixels.
434      * Setting this property to <code>true</code>
435      * can therefore have a subtle effect on
436      * how selections behave.
437      *
438      * <p>
439      *
440      * Some look and feels might not support automatic drag and drop;
441      * they will ignore this property.  You can work around such
442      * look and feels by modifying the component
443      * to directly call the <code>exportAsDrag</code> method of a
444      * <code>TransferHandler</code>.
445      *
446      * @param b the value to set the <code>dragEnabled</code> property to
447      * @exception HeadlessException if
448      *            <code>b</code> is <code>true</code> and
449      *            <code>GraphicsEnvironment.isHeadless()</code>
450      *            returns <code>true</code>
451      * @see java.awt.GraphicsEnvironment#isHeadless
452      * @see #getDragEnabled
453      * @see #setTransferHandler
454      * @see TransferHandler
455      * @since 1.4
456      */
457     @BeanProperty(bound = false, description
458             = "determines whether automatic drag handling is enabled")
setDragEnabled(boolean b)459     public void setDragEnabled(boolean b) {
460         checkDragEnabled(b);
461         dragEnabled = b;
462     }
463 
checkDragEnabled(boolean b)464     private static void checkDragEnabled(boolean b) {
465         if (b && GraphicsEnvironment.isHeadless()) {
466             throw new HeadlessException();
467         }
468     }
469 
470     /**
471      * Gets the value of the <code>dragEnabled</code> property.
472      *
473      * @return  the value of the <code>dragEnabled</code> property
474      * @see #setDragEnabled
475      * @since 1.4
476      */
getDragEnabled()477     public boolean getDragEnabled() {
478         return dragEnabled;
479     }
480 
481     // *****************************
482     // ****** File Operations ******
483     // *****************************
484 
485     /**
486      * Returns the selected file. This can be set either by the
487      * programmer via <code>setSelectedFile</code> or by a user action, such as
488      * either typing the filename into the UI or selecting the
489      * file from a list in the UI.
490      *
491      * @see #setSelectedFile
492      * @return the selected file
493      */
getSelectedFile()494     public File getSelectedFile() {
495         return selectedFile;
496     }
497 
498     /**
499      * Sets the selected file. If the file's parent directory is
500      * not the current directory, changes the current directory
501      * to be the file's parent directory.
502      *
503      * @see #getSelectedFile
504      *
505      * @param file the selected file
506      */
507     @BeanProperty(preferred = true)
setSelectedFile(File file)508     public void setSelectedFile(File file) {
509         File oldValue = selectedFile;
510         selectedFile = file;
511         if(selectedFile != null) {
512             if (file.isAbsolute() && !getFileSystemView().isParent(getCurrentDirectory(), selectedFile)) {
513                 setCurrentDirectory(selectedFile.getParentFile());
514             }
515             if (!isMultiSelectionEnabled() || selectedFiles == null || selectedFiles.length == 1) {
516                 ensureFileIsVisible(selectedFile);
517             }
518         }
519         firePropertyChange(SELECTED_FILE_CHANGED_PROPERTY, oldValue, selectedFile);
520     }
521 
522     /**
523      * Returns a list of selected files if the file chooser is
524      * set to allow multiple selection.
525      *
526      * @return an array of selected {@code File}s
527      */
getSelectedFiles()528     public File[] getSelectedFiles() {
529         if(selectedFiles == null) {
530             return new File[0];
531         } else {
532             return selectedFiles.clone();
533         }
534     }
535 
536     /**
537      * Sets the list of selected files if the file chooser is
538      * set to allow multiple selection.
539      *
540      * @param selectedFiles an array {@code File}s to be selected
541      */
542     @BeanProperty(description
543             = "The list of selected files if the chooser is in multiple selection mode.")
setSelectedFiles(File[] selectedFiles)544     public void setSelectedFiles(File[] selectedFiles) {
545         File[] oldValue = this.selectedFiles;
546         if (selectedFiles == null || selectedFiles.length == 0) {
547             selectedFiles = null;
548             this.selectedFiles = null;
549             setSelectedFile(null);
550         } else {
551             this.selectedFiles = selectedFiles.clone();
552             setSelectedFile(this.selectedFiles[0]);
553         }
554         firePropertyChange(SELECTED_FILES_CHANGED_PROPERTY, oldValue, selectedFiles);
555     }
556 
557     /**
558      * Returns the current directory.
559      *
560      * @return the current directory
561      * @see #setCurrentDirectory
562      */
getCurrentDirectory()563     public File getCurrentDirectory() {
564         return currentDirectory;
565     }
566 
567     /**
568      * Sets the current directory. Passing in <code>null</code> sets the
569      * file chooser to point to the user's default directory.
570      * This default depends on the operating system. It is
571      * typically the "My Documents" folder on Windows, and the user's
572      * home directory on Unix.
573      *
574      * If the file passed in as <code>currentDirectory</code> is not a
575      * directory, the parent of the file will be used as the currentDirectory.
576      * If the parent is not traversable, then it will walk up the parent tree
577      * until it finds a traversable directory, or hits the root of the
578      * file system.
579      *
580      * @param dir the current directory to point to
581      * @see #getCurrentDirectory
582      */
583     @BeanProperty(preferred = true, description
584             = "The directory that the JFileChooser is showing files of.")
setCurrentDirectory(File dir)585     public void setCurrentDirectory(File dir) {
586         File oldValue = currentDirectory;
587 
588         if (dir != null && !dir.exists()) {
589             dir = currentDirectory;
590         }
591         if (dir == null) {
592             dir = getFileSystemView().getDefaultDirectory();
593         }
594         if (currentDirectory != null) {
595             /* Verify the toString of object */
596             if (this.currentDirectory.equals(dir)) {
597                 return;
598             }
599         }
600 
601         File prev = null;
602         while (!isTraversable(dir) && prev != dir) {
603             prev = dir;
604             dir = getFileSystemView().getParentDirectory(dir);
605         }
606         currentDirectory = dir;
607 
608         firePropertyChange(DIRECTORY_CHANGED_PROPERTY, oldValue, currentDirectory);
609     }
610 
611     /**
612      * Changes the directory to be set to the parent of the
613      * current directory.
614      *
615      * @see #getCurrentDirectory
616      */
changeToParentDirectory()617     public void changeToParentDirectory() {
618         selectedFile = null;
619         File oldValue = getCurrentDirectory();
620         setCurrentDirectory(getFileSystemView().getParentDirectory(oldValue));
621     }
622 
623     /**
624      * Tells the UI to rescan its files list from the current directory.
625      */
rescanCurrentDirectory()626     public void rescanCurrentDirectory() {
627         getUI().rescanCurrentDirectory(this);
628     }
629 
630     /**
631      * Makes sure that the specified file is viewable, and
632      * not hidden.
633      *
634      * @param f  a File object
635      */
ensureFileIsVisible(File f)636     public void ensureFileIsVisible(File f) {
637         getUI().ensureFileIsVisible(this, f);
638     }
639 
640     // **************************************
641     // ***** JFileChooser Dialog methods *****
642     // **************************************
643 
644     /**
645      * Pops up an "Open File" file chooser dialog. Note that the
646      * text that appears in the approve button is determined by
647      * the L&amp;F.
648      *
649      * @param    parent  the parent component of the dialog,
650      *                  can be <code>null</code>;
651      *                  see <code>showDialog</code> for details
652      * @return   the return state of the file chooser on popdown:
653      * <ul>
654      * <li>JFileChooser.CANCEL_OPTION
655      * <li>JFileChooser.APPROVE_OPTION
656      * <li>JFileChooser.ERROR_OPTION if an error occurs or the
657      *                  dialog is dismissed
658      * </ul>
659      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
660      * returns true.
661      * @see java.awt.GraphicsEnvironment#isHeadless
662      * @see #showDialog
663      */
showOpenDialog(Component parent)664     public int showOpenDialog(Component parent) throws HeadlessException {
665         setDialogType(OPEN_DIALOG);
666         return showDialog(parent, null);
667     }
668 
669     /**
670      * Pops up a "Save File" file chooser dialog. Note that the
671      * text that appears in the approve button is determined by
672      * the L&amp;F.
673      *
674      * @param    parent  the parent component of the dialog,
675      *                  can be <code>null</code>;
676      *                  see <code>showDialog</code> for details
677      * @return   the return state of the file chooser on popdown:
678      * <ul>
679      * <li>JFileChooser.CANCEL_OPTION
680      * <li>JFileChooser.APPROVE_OPTION
681      * <li>JFileChooser.ERROR_OPTION if an error occurs or the
682      *                  dialog is dismissed
683      * </ul>
684      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
685      * returns true.
686      * @see java.awt.GraphicsEnvironment#isHeadless
687      * @see #showDialog
688      */
showSaveDialog(Component parent)689     public int showSaveDialog(Component parent) throws HeadlessException {
690         setDialogType(SAVE_DIALOG);
691         return showDialog(parent, null);
692     }
693 
694     /**
695      * Pops a custom file chooser dialog with a custom approve button.
696      * For example, the following code
697      * pops up a file chooser with a "Run Application" button
698      * (instead of the normal "Save" or "Open" button):
699      * <pre>
700      * filechooser.showDialog(parentFrame, "Run Application");
701      * </pre>
702      *
703      * Alternatively, the following code does the same thing:
704      * <pre>
705      *    JFileChooser chooser = new JFileChooser(null);
706      *    chooser.setApproveButtonText("Run Application");
707      *    chooser.showDialog(parentFrame, null);
708      * </pre>
709      *
710      * <!--PENDING(jeff) - the following method should be added to the api:
711      *      showDialog(Component parent);-->
712      * <!--PENDING(kwalrath) - should specify modality and what
713      *      "depends" means.-->
714      *
715      * <p>
716      *
717      * The <code>parent</code> argument determines two things:
718      * the frame on which the open dialog depends and
719      * the component whose position the look and feel
720      * should consider when placing the dialog.  If the parent
721      * is a <code>Frame</code> object (such as a <code>JFrame</code>)
722      * then the dialog depends on the frame and
723      * the look and feel positions the dialog
724      * relative to the frame (for example, centered over the frame).
725      * If the parent is a component, then the dialog
726      * depends on the frame containing the component,
727      * and is positioned relative to the component
728      * (for example, centered over the component).
729      * If the parent is <code>null</code>, then the dialog depends on
730      * no visible window, and it's placed in a
731      * look-and-feel-dependent position
732      * such as the center of the screen.
733      *
734      * @param   parent  the parent component of the dialog;
735      *                  can be <code>null</code>
736      * @param   approveButtonText the text of the <code>ApproveButton</code>
737      * @return  the return state of the file chooser on popdown:
738      * <ul>
739      * <li>JFileChooser.CANCEL_OPTION
740      * <li>JFileChooser.APPROVE_OPTION
741      * <li>JFileChooser.ERROR_OPTION if an error occurs or the
742      *                  dialog is dismissed
743      * </ul>
744      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
745      * returns true.
746      * @see java.awt.GraphicsEnvironment#isHeadless
747      */
748     @SuppressWarnings("deprecation")
showDialog(Component parent, String approveButtonText)749     public int showDialog(Component parent, String approveButtonText)
750         throws HeadlessException {
751         if (dialog != null) {
752             // Prevent to show second instance of dialog if the previous one still exists
753             return JFileChooser.ERROR_OPTION;
754         }
755 
756         if(approveButtonText != null) {
757             setApproveButtonText(approveButtonText);
758             setDialogType(CUSTOM_DIALOG);
759         }
760         dialog = createDialog(parent);
761         dialog.addWindowListener(new WindowAdapter() {
762             public void windowClosing(WindowEvent e) {
763                 returnValue = CANCEL_OPTION;
764             }
765         });
766         returnValue = ERROR_OPTION;
767         rescanCurrentDirectory();
768 
769         dialog.show();
770         firePropertyChange("JFileChooserDialogIsClosingProperty", dialog, null);
771 
772         // Remove all components from dialog. The MetalFileChooserUI.installUI() method (and other LAFs)
773         // registers AWT listener for dialogs and produces memory leaks. It happens when
774         // installUI invoked after the showDialog method.
775         dialog.getContentPane().removeAll();
776         dialog.dispose();
777         dialog = null;
778         return returnValue;
779     }
780 
781     /**
782      * Creates and returns a new <code>JDialog</code> wrapping
783      * <code>this</code> centered on the <code>parent</code>
784      * in the <code>parent</code>'s frame.
785      * This method can be overriden to further manipulate the dialog,
786      * to disable resizing, set the location, etc. Example:
787      * <pre>
788      *     class MyFileChooser extends JFileChooser {
789      *         protected JDialog createDialog(Component parent) throws HeadlessException {
790      *             JDialog dialog = super.createDialog(parent);
791      *             dialog.setLocation(300, 200);
792      *             dialog.setResizable(false);
793      *             return dialog;
794      *         }
795      *     }
796      * </pre>
797      *
798      * @param   parent  the parent component of the dialog;
799      *                  can be <code>null</code>
800      * @return a new <code>JDialog</code> containing this instance
801      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
802      * returns true.
803      * @see java.awt.GraphicsEnvironment#isHeadless
804      * @since 1.4
805      */
createDialog(Component parent)806     protected JDialog createDialog(Component parent) throws HeadlessException {
807         FileChooserUI ui = getUI();
808         String title = ui.getDialogTitle(this);
809         putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY,
810                           title);
811 
812         JDialog dialog;
813         Window window = JOptionPane.getWindowForComponent(parent);
814         if (window instanceof Frame) {
815             dialog = new JDialog((Frame)window, title, true);
816         } else {
817             dialog = new JDialog((Dialog)window, title, true);
818         }
819         dialog.setComponentOrientation(this.getComponentOrientation());
820 
821         Container contentPane = dialog.getContentPane();
822         contentPane.setLayout(new BorderLayout());
823         contentPane.add(this, BorderLayout.CENTER);
824 
825         if (JDialog.isDefaultLookAndFeelDecorated()) {
826             boolean supportsWindowDecorations =
827             UIManager.getLookAndFeel().getSupportsWindowDecorations();
828             if (supportsWindowDecorations) {
829                 dialog.getRootPane().setWindowDecorationStyle(JRootPane.FILE_CHOOSER_DIALOG);
830             }
831         }
832         dialog.pack();
833         dialog.setLocationRelativeTo(parent);
834 
835         return dialog;
836     }
837 
838     // **************************
839     // ***** Dialog Options *****
840     // **************************
841 
842     /**
843      * Returns the value of the <code>controlButtonsAreShown</code>
844      * property.
845      *
846      * @return   the value of the <code>controlButtonsAreShown</code>
847      *     property
848      *
849      * @see #setControlButtonsAreShown
850      * @since 1.3
851      */
getControlButtonsAreShown()852     public boolean getControlButtonsAreShown() {
853         return controlsShown;
854     }
855 
856 
857     /**
858      * Sets the property
859      * that indicates whether the <i>approve</i> and <i>cancel</i>
860      * buttons are shown in the file chooser.  This property
861      * is <code>true</code> by default.  Look and feels
862      * that always show these buttons will ignore the value
863      * of this property.
864      * This method fires a property-changed event,
865      * using the string value of
866      * <code>CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY</code>
867      * as the name of the property.
868      *
869      * @param b <code>false</code> if control buttons should not be
870      *    shown; otherwise, <code>true</code>
871      *
872      * @see #getControlButtonsAreShown
873      * @see #CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY
874      * @since 1.3
875      */
876     @BeanProperty(preferred = true, description
877             = "Sets whether the approve & cancel buttons are shown.")
setControlButtonsAreShown(boolean b)878     public void setControlButtonsAreShown(boolean b) {
879         if(controlsShown == b) {
880             return;
881         }
882         boolean oldValue = controlsShown;
883         controlsShown = b;
884         firePropertyChange(CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY, oldValue, controlsShown);
885     }
886 
887     /**
888      * Returns the type of this dialog.  The default is
889      * <code>JFileChooser.OPEN_DIALOG</code>.
890      *
891      * @return   the type of dialog to be displayed:
892      * <ul>
893      * <li>JFileChooser.OPEN_DIALOG
894      * <li>JFileChooser.SAVE_DIALOG
895      * <li>JFileChooser.CUSTOM_DIALOG
896      * </ul>
897      *
898      * @see #setDialogType
899      */
getDialogType()900     public int getDialogType() {
901         return dialogType;
902     }
903 
904     /**
905      * Sets the type of this dialog. Use <code>OPEN_DIALOG</code> when you
906      * want to bring up a file chooser that the user can use to open a file.
907      * Likewise, use <code>SAVE_DIALOG</code> for letting the user choose
908      * a file for saving.
909      * Use <code>CUSTOM_DIALOG</code> when you want to use the file
910      * chooser in a context other than "Open" or "Save".
911      * For instance, you might want to bring up a file chooser that allows
912      * the user to choose a file to execute. Note that you normally would not
913      * need to set the <code>JFileChooser</code> to use
914      * <code>CUSTOM_DIALOG</code>
915      * since a call to <code>setApproveButtonText</code> does this for you.
916      * The default dialog type is <code>JFileChooser.OPEN_DIALOG</code>.
917      *
918      * @param dialogType the type of dialog to be displayed:
919      * <ul>
920      * <li>JFileChooser.OPEN_DIALOG
921      * <li>JFileChooser.SAVE_DIALOG
922      * <li>JFileChooser.CUSTOM_DIALOG
923      * </ul>
924      *
925      * @exception IllegalArgumentException if <code>dialogType</code> is
926      *                          not legal
927      *
928      * @see #getDialogType
929      * @see #setApproveButtonText
930      */
931     // PENDING(jeff) - fire button text change property
932     @BeanProperty(preferred = true, enumerationValues = {
933             "JFileChooser.OPEN_DIALOG",
934             "JFileChooser.SAVE_DIALOG",
935             "JFileChooser.CUSTOM_DIALOG"}, description
936             = "The type (open, save, custom) of the JFileChooser.")
setDialogType(int dialogType)937     public void setDialogType(int dialogType) {
938         if(this.dialogType == dialogType) {
939             return;
940         }
941         checkDialogType(dialogType);
942         int oldValue = this.dialogType;
943         this.dialogType = dialogType;
944         if(dialogType == OPEN_DIALOG || dialogType == SAVE_DIALOG) {
945             setApproveButtonText(null);
946         }
947         firePropertyChange(DIALOG_TYPE_CHANGED_PROPERTY, oldValue, dialogType);
948     }
949 
checkDialogType(int dialogType)950     private static void checkDialogType(int dialogType) {
951         if (!(dialogType == OPEN_DIALOG || dialogType == SAVE_DIALOG
952                 || dialogType == CUSTOM_DIALOG)) {
953             throw new IllegalArgumentException(
954                     "Incorrect Dialog Type: " + dialogType);
955         }
956     }
957 
958     /**
959      * Sets the string that goes in the <code>JFileChooser</code> window's
960      * title bar.
961      *
962      * @param dialogTitle the new <code>String</code> for the title bar
963      *
964      * @see #getDialogTitle
965      *
966      */
967     @BeanProperty(preferred = true, description
968             = "The title of the JFileChooser dialog window.")
setDialogTitle(String dialogTitle)969     public void setDialogTitle(String dialogTitle) {
970         String oldValue = this.dialogTitle;
971         this.dialogTitle = dialogTitle;
972         if(dialog != null) {
973             dialog.setTitle(dialogTitle);
974         }
975         firePropertyChange(DIALOG_TITLE_CHANGED_PROPERTY, oldValue, dialogTitle);
976     }
977 
978     /**
979      * Gets the string that goes in the <code>JFileChooser</code>'s titlebar.
980      *
981      * @return the string from the {@code JFileChooser} window's title bar
982      * @see #setDialogTitle
983      */
getDialogTitle()984     public String getDialogTitle() {
985         return dialogTitle;
986     }
987 
988     // ************************************
989     // ***** JFileChooser View Options *****
990     // ************************************
991 
992 
993 
994     /**
995      * Sets the tooltip text used in the <code>ApproveButton</code>.
996      * If <code>null</code>, the UI object will determine the button's text.
997      *
998      * @param toolTipText the tooltip text for the approve button
999      * @see #setApproveButtonText
1000      * @see #setDialogType
1001      * @see #showDialog
1002      */
1003     @BeanProperty(preferred = true, description
1004             = "The tooltip text for the ApproveButton.")
setApproveButtonToolTipText(String toolTipText)1005     public void setApproveButtonToolTipText(String toolTipText) {
1006         if(approveButtonToolTipText == toolTipText) {
1007             return;
1008         }
1009         String oldValue = approveButtonToolTipText;
1010         approveButtonToolTipText = toolTipText;
1011         firePropertyChange(APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY, oldValue, approveButtonToolTipText);
1012     }
1013 
1014 
1015     /**
1016      * Returns the tooltip text used in the <code>ApproveButton</code>.
1017      * If <code>null</code>, the UI object will determine the button's text.
1018      *
1019      * @return the tooltip text used for the approve button
1020      *
1021      * @see #setApproveButtonText
1022      * @see #setDialogType
1023      * @see #showDialog
1024      */
getApproveButtonToolTipText()1025     public String getApproveButtonToolTipText() {
1026         return approveButtonToolTipText;
1027     }
1028 
1029     /**
1030      * Returns the approve button's mnemonic.
1031      * @return an integer value for the mnemonic key
1032      *
1033      * @see #setApproveButtonMnemonic
1034      */
getApproveButtonMnemonic()1035     public int getApproveButtonMnemonic() {
1036         return approveButtonMnemonic;
1037     }
1038 
1039     /**
1040      * Sets the approve button's mnemonic using a numeric keycode.
1041      *
1042      * @param mnemonic  an integer value for the mnemonic key
1043      *
1044      * @see #getApproveButtonMnemonic
1045      */
1046     @BeanProperty(preferred = true, description
1047             = "The mnemonic key accelerator for the ApproveButton.")
setApproveButtonMnemonic(int mnemonic)1048     public void setApproveButtonMnemonic(int mnemonic) {
1049         if(approveButtonMnemonic == mnemonic) {
1050            return;
1051         }
1052         int oldValue = approveButtonMnemonic;
1053         approveButtonMnemonic = mnemonic;
1054         firePropertyChange(APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY, oldValue, approveButtonMnemonic);
1055     }
1056 
1057     /**
1058      * Sets the approve button's mnemonic using a character.
1059      * @param mnemonic  a character value for the mnemonic key
1060      *
1061      * @see #getApproveButtonMnemonic
1062      */
setApproveButtonMnemonic(char mnemonic)1063     public void setApproveButtonMnemonic(char mnemonic) {
1064         int vk = (int) mnemonic;
1065         if(vk >= 'a' && vk <='z') {
1066             vk -= ('a' - 'A');
1067         }
1068         setApproveButtonMnemonic(vk);
1069     }
1070 
1071 
1072     /**
1073      * Sets the text used in the <code>ApproveButton</code> in the
1074      * <code>FileChooserUI</code>.
1075      *
1076      * @param approveButtonText the text used in the <code>ApproveButton</code>
1077      *
1078      * @see #getApproveButtonText
1079      * @see #setDialogType
1080      * @see #showDialog
1081      */
1082     // PENDING(jeff) - have ui set this on dialog type change
1083     @BeanProperty(preferred = true, description
1084             = "The text that goes in the ApproveButton.")
setApproveButtonText(String approveButtonText)1085     public void setApproveButtonText(String approveButtonText) {
1086         if(this.approveButtonText == approveButtonText) {
1087             return;
1088         }
1089         String oldValue = this.approveButtonText;
1090         this.approveButtonText = approveButtonText;
1091         firePropertyChange(APPROVE_BUTTON_TEXT_CHANGED_PROPERTY, oldValue, approveButtonText);
1092     }
1093 
1094     /**
1095      * Returns the text used in the <code>ApproveButton</code> in the
1096      * <code>FileChooserUI</code>.
1097      * If <code>null</code>, the UI object will determine the button's text.
1098      *
1099      * Typically, this would be "Open" or "Save".
1100      *
1101      * @return the text used in the <code>ApproveButton</code>
1102      *
1103      * @see #setApproveButtonText
1104      * @see #setDialogType
1105      * @see #showDialog
1106      */
getApproveButtonText()1107     public String getApproveButtonText() {
1108         return approveButtonText;
1109     }
1110 
1111     /**
1112      * Gets the list of user choosable file filters.
1113      *
1114      * @return a <code>FileFilter</code> array containing all the choosable
1115      *         file filters
1116      *
1117      * @see #addChoosableFileFilter
1118      * @see #removeChoosableFileFilter
1119      * @see #resetChoosableFileFilters
1120      */
1121     @BeanProperty(bound = false)
getChoosableFileFilters()1122     public FileFilter[] getChoosableFileFilters() {
1123         FileFilter[] filterArray = new FileFilter[filters.size()];
1124         filters.copyInto(filterArray);
1125         return filterArray;
1126     }
1127 
1128     /**
1129      * Adds a filter to the list of user choosable file filters.
1130      * For information on setting the file selection mode, see
1131      * {@link #setFileSelectionMode setFileSelectionMode}.
1132      *
1133      * @param filter the <code>FileFilter</code> to add to the choosable file
1134      *               filter list
1135      *
1136      * @see #getChoosableFileFilters
1137      * @see #removeChoosableFileFilter
1138      * @see #resetChoosableFileFilters
1139      * @see #setFileSelectionMode
1140      */
1141     @BeanProperty(preferred = true, description
1142             = "Adds a filter to the list of user choosable file filters.")
addChoosableFileFilter(FileFilter filter)1143     public void addChoosableFileFilter(FileFilter filter) {
1144         if(filter != null && !filters.contains(filter)) {
1145             FileFilter[] oldValue = getChoosableFileFilters();
1146             filters.addElement(filter);
1147             firePropertyChange(CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY, oldValue, getChoosableFileFilters());
1148             if (fileFilter == null && filters.size() == 1) {
1149                 setFileFilter(filter);
1150             }
1151         }
1152     }
1153 
1154     /**
1155      * Removes a filter from the list of user choosable file filters. Returns
1156      * true if the file filter was removed.
1157      *
1158      * @param f the file filter to be removed
1159      * @return true if the file filter was removed, false otherwise
1160      * @see #addChoosableFileFilter
1161      * @see #getChoosableFileFilters
1162      * @see #resetChoosableFileFilters
1163      */
removeChoosableFileFilter(FileFilter f)1164     public boolean removeChoosableFileFilter(FileFilter f) {
1165         int index = filters.indexOf(f);
1166         if (index >= 0) {
1167             if(getFileFilter() == f) {
1168                 FileFilter aaff = getAcceptAllFileFilter();
1169                 if (isAcceptAllFileFilterUsed() && (aaff != f)) {
1170                     // choose default filter if it is used
1171                     setFileFilter(aaff);
1172                 }
1173                 else if (index > 0) {
1174                     // choose the first filter, because it is not removed
1175                     setFileFilter(filters.get(0));
1176                 }
1177                 else if (filters.size() > 1) {
1178                     // choose the second filter, because the first one is removed
1179                     setFileFilter(filters.get(1));
1180                 }
1181                 else {
1182                     // no more filters
1183                     setFileFilter(null);
1184                 }
1185             }
1186             FileFilter[] oldValue = getChoosableFileFilters();
1187             filters.removeElement(f);
1188             firePropertyChange(CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY, oldValue, getChoosableFileFilters());
1189             return true;
1190         } else {
1191             return false;
1192         }
1193     }
1194 
1195     /**
1196      * Resets the choosable file filter list to its starting state. Normally,
1197      * this removes all added file filters while leaving the
1198      * <code>AcceptAll</code> file filter.
1199      *
1200      * @see #addChoosableFileFilter
1201      * @see #getChoosableFileFilters
1202      * @see #removeChoosableFileFilter
1203      */
resetChoosableFileFilters()1204     public void resetChoosableFileFilters() {
1205         FileFilter[] oldValue = getChoosableFileFilters();
1206         setFileFilter(null);
1207         filters.removeAllElements();
1208         if(isAcceptAllFileFilterUsed()) {
1209            addChoosableFileFilter(getAcceptAllFileFilter());
1210         }
1211         firePropertyChange(CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY, oldValue, getChoosableFileFilters());
1212     }
1213 
1214     /**
1215      * Returns the <code>AcceptAll</code> file filter.
1216      * For example, on Microsoft Windows this would be All Files (*.*).
1217      *
1218      * @return the {@code AcceptAll} file filter
1219      */
1220     @BeanProperty(bound = false)
getAcceptAllFileFilter()1221     public FileFilter getAcceptAllFileFilter() {
1222         FileFilter filter = null;
1223         if(getUI() != null) {
1224             filter = getUI().getAcceptAllFileFilter(this);
1225         }
1226         return filter;
1227     }
1228 
1229    /**
1230     * Returns whether the <code>AcceptAll FileFilter</code> is used.
1231     * @return true if the <code>AcceptAll FileFilter</code> is used
1232     * @see #setAcceptAllFileFilterUsed
1233     * @since 1.3
1234     */
isAcceptAllFileFilterUsed()1235     public boolean isAcceptAllFileFilterUsed() {
1236         return useAcceptAllFileFilter;
1237     }
1238 
1239    /**
1240     * Determines whether the <code>AcceptAll FileFilter</code> is used
1241     * as an available choice in the choosable filter list.
1242     * If false, the <code>AcceptAll</code> file filter is removed from
1243     * the list of available file filters.
1244     * If true, the <code>AcceptAll</code> file filter will become the
1245     * actively used file filter.
1246     *
1247     * @param b a {@code boolean} which determines whether the {@code AcceptAll}
1248     *          file filter is an available choice in the choosable filter list
1249     *
1250     * @see #isAcceptAllFileFilterUsed
1251     * @see #getAcceptAllFileFilter
1252     * @see #setFileFilter
1253     * @since 1.3
1254     */
1255     @BeanProperty(preferred = true, description
1256             = "Sets whether the AcceptAll FileFilter is used as an available choice in the choosable filter list.")
setAcceptAllFileFilterUsed(boolean b)1257     public void setAcceptAllFileFilterUsed(boolean b) {
1258         boolean oldValue = useAcceptAllFileFilter;
1259         useAcceptAllFileFilter = b;
1260         if(!b) {
1261             removeChoosableFileFilter(getAcceptAllFileFilter());
1262         } else {
1263             removeChoosableFileFilter(getAcceptAllFileFilter());
1264             addChoosableFileFilter(getAcceptAllFileFilter());
1265         }
1266         firePropertyChange(ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY, oldValue, useAcceptAllFileFilter);
1267     }
1268 
1269     /**
1270      * Returns the accessory component.
1271      *
1272      * @return this JFileChooser's accessory component, or null
1273      * @see #setAccessory
1274      */
getAccessory()1275     public JComponent getAccessory() {
1276         return accessory;
1277     }
1278 
1279     /**
1280      * Sets the accessory component. An accessory is often used to show a
1281      * preview image of the selected file; however, it can be used for anything
1282      * that the programmer wishes, such as extra custom file chooser controls.
1283      *
1284      * <p>
1285      * Note: if there was a previous accessory, you should unregister
1286      * any listeners that the accessory might have registered with the
1287      * file chooser.
1288      *
1289      * @param newAccessory the accessory component to be set
1290      */
1291     @BeanProperty(preferred = true, description
1292             = "Sets the accessory component on the JFileChooser.")
setAccessory(JComponent newAccessory)1293     public void setAccessory(JComponent newAccessory) {
1294         JComponent oldValue = accessory;
1295         accessory = newAccessory;
1296         firePropertyChange(ACCESSORY_CHANGED_PROPERTY, oldValue, accessory);
1297     }
1298 
1299     /**
1300      * Sets the <code>JFileChooser</code> to allow the user to just
1301      * select files, just select
1302      * directories, or select both files and directories.  The default is
1303      * <code>JFilesChooser.FILES_ONLY</code>.
1304      *
1305      * @param mode the type of files to be displayed:
1306      * <ul>
1307      * <li>JFileChooser.FILES_ONLY
1308      * <li>JFileChooser.DIRECTORIES_ONLY
1309      * <li>JFileChooser.FILES_AND_DIRECTORIES
1310      * </ul>
1311      *
1312      * @exception IllegalArgumentException  if <code>mode</code> is an
1313      *                          illegal file selection mode
1314      *
1315      * @see #getFileSelectionMode
1316      */
1317     @BeanProperty(preferred = true, enumerationValues = {
1318             "JFileChooser.FILES_ONLY",
1319             "JFileChooser.DIRECTORIES_ONLY",
1320             "JFileChooser.FILES_AND_DIRECTORIES"}, description
1321             = "Sets the types of files that the JFileChooser can choose.")
setFileSelectionMode(int mode)1322     public void setFileSelectionMode(int mode) {
1323         if(fileSelectionMode == mode) {
1324             return;
1325         }
1326 
1327         checkFileSelectionMode(mode);
1328            int oldValue = fileSelectionMode;
1329            fileSelectionMode = mode;
1330            firePropertyChange(FILE_SELECTION_MODE_CHANGED_PROPERTY, oldValue, fileSelectionMode);
1331     }
1332 
checkFileSelectionMode(int mode)1333     private static void checkFileSelectionMode(int mode) {
1334         if ((mode != FILES_ONLY) && (mode != DIRECTORIES_ONLY)
1335                 && (mode != FILES_AND_DIRECTORIES)) {
1336             throw new IllegalArgumentException(
1337                     "Incorrect Mode for file selection: " + mode);
1338         }
1339     }
1340 
1341     /**
1342      * Returns the current file-selection mode.  The default is
1343      * <code>JFilesChooser.FILES_ONLY</code>.
1344      *
1345      * @return the type of files to be displayed, one of the following:
1346      * <ul>
1347      * <li>JFileChooser.FILES_ONLY
1348      * <li>JFileChooser.DIRECTORIES_ONLY
1349      * <li>JFileChooser.FILES_AND_DIRECTORIES
1350      * </ul>
1351      * @see #setFileSelectionMode
1352      */
getFileSelectionMode()1353     public int getFileSelectionMode() {
1354         return fileSelectionMode;
1355     }
1356 
1357     /**
1358      * Convenience call that determines if files are selectable based on the
1359      * current file selection mode.
1360      *
1361      * @return true if files are selectable, false otherwise
1362      * @see #setFileSelectionMode
1363      * @see #getFileSelectionMode
1364      */
1365     @BeanProperty(bound = false)
isFileSelectionEnabled()1366     public boolean isFileSelectionEnabled() {
1367         return ((fileSelectionMode == FILES_ONLY) || (fileSelectionMode == FILES_AND_DIRECTORIES));
1368     }
1369 
1370     /**
1371      * Convenience call that determines if directories are selectable based
1372      * on the current file selection mode.
1373      *
1374      * @return true if directories are selectable, false otherwise
1375      * @see #setFileSelectionMode
1376      * @see #getFileSelectionMode
1377      */
1378     @BeanProperty(bound = false)
isDirectorySelectionEnabled()1379     public boolean isDirectorySelectionEnabled() {
1380         return ((fileSelectionMode == DIRECTORIES_ONLY) || (fileSelectionMode == FILES_AND_DIRECTORIES));
1381     }
1382 
1383     /**
1384      * Sets the file chooser to allow multiple file selections.
1385      *
1386      * @param b true if multiple files may be selected
1387      *
1388      * @see #isMultiSelectionEnabled
1389      */
1390     @BeanProperty(description
1391             = "Sets multiple file selection mode.")
setMultiSelectionEnabled(boolean b)1392     public void setMultiSelectionEnabled(boolean b) {
1393         if(multiSelectionEnabled == b) {
1394             return;
1395         }
1396         boolean oldValue = multiSelectionEnabled;
1397         multiSelectionEnabled = b;
1398         firePropertyChange(MULTI_SELECTION_ENABLED_CHANGED_PROPERTY, oldValue, multiSelectionEnabled);
1399     }
1400 
1401     /**
1402      * Returns true if multiple files can be selected.
1403      * @return true if multiple files can be selected
1404      * @see #setMultiSelectionEnabled
1405      */
isMultiSelectionEnabled()1406     public boolean isMultiSelectionEnabled() {
1407         return multiSelectionEnabled;
1408     }
1409 
1410 
1411     /**
1412      * Returns true if hidden files are not shown in the file chooser;
1413      * otherwise, returns false.
1414      *
1415      * @return the status of the file hiding property
1416      * @see #setFileHidingEnabled
1417      */
isFileHidingEnabled()1418     public boolean isFileHidingEnabled() {
1419         return useFileHiding;
1420     }
1421 
1422     /**
1423      * Sets file hiding on or off. If true, hidden files are not shown
1424      * in the file chooser. The job of determining which files are
1425      * shown is done by the <code>FileView</code>.
1426      *
1427      * @param b the boolean value that determines whether file hiding is
1428      *          turned on
1429      * @see #isFileHidingEnabled
1430      */
1431     @BeanProperty(preferred = true, description
1432             = "Sets file hiding on or off.")
setFileHidingEnabled(boolean b)1433     public void setFileHidingEnabled(boolean b) {
1434         // Dump showFilesListener since we'll ignore it from now on
1435         if (showFilesListener != null) {
1436             Toolkit.getDefaultToolkit().removePropertyChangeListener(SHOW_HIDDEN_PROP, showFilesListener);
1437             showFilesListener = null;
1438         }
1439         boolean oldValue = useFileHiding;
1440         useFileHiding = b;
1441         firePropertyChange(FILE_HIDING_CHANGED_PROPERTY, oldValue, useFileHiding);
1442     }
1443 
1444     /**
1445      * Sets the current file filter. The file filter is used by the
1446      * file chooser to filter out files from the user's view.
1447      *
1448      * @param filter the new current file filter to use
1449      * @see #getFileFilter
1450      */
1451     @BeanProperty(preferred = true, description
1452             = "Sets the File Filter used to filter out files of type.")
setFileFilter(FileFilter filter)1453     public void setFileFilter(FileFilter filter) {
1454         FileFilter oldValue = fileFilter;
1455         fileFilter = filter;
1456         if (filter != null) {
1457             if (isMultiSelectionEnabled() && selectedFiles != null && selectedFiles.length > 0) {
1458                 Vector<File> fList = new Vector<File>();
1459                 boolean failed = false;
1460                 for (File file : selectedFiles) {
1461                     if (filter.accept(file)) {
1462                         fList.add(file);
1463                     } else {
1464                         failed = true;
1465                     }
1466                 }
1467                 if (failed) {
1468                     setSelectedFiles((fList.size() == 0) ? null : fList.toArray(new File[fList.size()]));
1469                 }
1470             } else if (selectedFile != null && !filter.accept(selectedFile)) {
1471                 setSelectedFile(null);
1472             }
1473         }
1474         firePropertyChange(FILE_FILTER_CHANGED_PROPERTY, oldValue, fileFilter);
1475     }
1476 
1477 
1478     /**
1479      * Returns the currently selected file filter.
1480      *
1481      * @return the current file filter
1482      * @see #setFileFilter
1483      * @see #addChoosableFileFilter
1484      */
getFileFilter()1485     public FileFilter getFileFilter() {
1486         return fileFilter;
1487     }
1488 
1489     /**
1490      * Sets the file view to be used to retrieve UI information, such as
1491      * the icon that represents a file or the type description of a file.
1492      *
1493      * @param fileView a {@code FileView} to be used to retrieve UI information
1494      *
1495      * @see #getFileView
1496      */
1497     @BeanProperty(preferred = true, description
1498             = "Sets the File View used to get file type information.")
setFileView(FileView fileView)1499     public void setFileView(FileView fileView) {
1500         FileView oldValue = this.fileView;
1501         this.fileView = fileView;
1502         firePropertyChange(FILE_VIEW_CHANGED_PROPERTY, oldValue, fileView);
1503     }
1504 
1505     /**
1506      * Returns the current file view.
1507      *
1508      * @return the current file view
1509      * @see #setFileView
1510      */
getFileView()1511     public FileView getFileView() {
1512         return fileView;
1513     }
1514 
1515     // ******************************
1516     // *****FileView delegation *****
1517     // ******************************
1518 
1519     // NOTE: all of the following methods attempt to delegate
1520     // first to the client set fileView, and if <code>null</code> is returned
1521     // (or there is now client defined fileView) then calls the
1522     // UI's default fileView.
1523 
1524     /**
1525      * Returns the filename.
1526      * @param f the <code>File</code>
1527      * @return the <code>String</code> containing the filename for
1528      *          <code>f</code>
1529      * @see FileView#getName
1530      */
getName(File f)1531     public String getName(File f) {
1532         String filename = null;
1533         if(f != null) {
1534             if(getFileView() != null) {
1535                 filename = getFileView().getName(f);
1536             }
1537 
1538             FileView uiFileView = getUI().getFileView(this);
1539 
1540             if(filename == null && uiFileView != null) {
1541                 filename = uiFileView.getName(f);
1542             }
1543         }
1544         return filename;
1545     }
1546 
1547     /**
1548      * Returns the file description.
1549      * @param f the <code>File</code>
1550      * @return the <code>String</code> containing the file description for
1551      *          <code>f</code>
1552      * @see FileView#getDescription
1553      */
getDescription(File f)1554     public String getDescription(File f) {
1555         String description = null;
1556         if(f != null) {
1557             if(getFileView() != null) {
1558                 description = getFileView().getDescription(f);
1559             }
1560 
1561             FileView uiFileView = getUI().getFileView(this);
1562 
1563             if(description == null && uiFileView != null) {
1564                 description = uiFileView.getDescription(f);
1565             }
1566         }
1567         return description;
1568     }
1569 
1570     /**
1571      * Returns the file type.
1572      * @param f the <code>File</code>
1573      * @return the <code>String</code> containing the file type description for
1574      *          <code>f</code>
1575      * @see FileView#getTypeDescription
1576      */
getTypeDescription(File f)1577     public String getTypeDescription(File f) {
1578         String typeDescription = null;
1579         if(f != null) {
1580             if(getFileView() != null) {
1581                 typeDescription = getFileView().getTypeDescription(f);
1582             }
1583 
1584             FileView uiFileView = getUI().getFileView(this);
1585 
1586             if(typeDescription == null && uiFileView != null) {
1587                 typeDescription = uiFileView.getTypeDescription(f);
1588             }
1589         }
1590         return typeDescription;
1591     }
1592 
1593     /**
1594      * Returns the icon for this file or type of file, depending
1595      * on the system.
1596      * @param f the <code>File</code>
1597      * @return the <code>Icon</code> for this file, or type of file
1598      * @see FileView#getIcon
1599      */
getIcon(File f)1600     public Icon getIcon(File f) {
1601         Icon icon = null;
1602         if (f != null) {
1603             if(getFileView() != null) {
1604                 icon = getFileView().getIcon(f);
1605             }
1606 
1607             FileView uiFileView = getUI().getFileView(this);
1608 
1609             if(icon == null && uiFileView != null) {
1610                 icon = uiFileView.getIcon(f);
1611             }
1612         }
1613         return icon;
1614     }
1615 
1616     /**
1617      * Returns true if the file (directory) can be visited.
1618      * Returns false if the directory cannot be traversed.
1619      * @param f the <code>File</code>
1620      * @return true if the file/directory can be traversed, otherwise false
1621      * @see FileView#isTraversable
1622      */
isTraversable(File f)1623     public boolean isTraversable(File f) {
1624         Boolean traversable = null;
1625         if (f != null) {
1626             FileView fileView = getFileView();
1627             if (fileView != null) {
1628                 traversable = fileView.isTraversable(f);
1629             }
1630             FileChooserUI ui = getUI();
1631             if (traversable == null && ui != null) {
1632                 FileView uiFileView = ui.getFileView(this);
1633                 if (uiFileView != null) {
1634                     traversable = uiFileView.isTraversable(f);
1635                 }
1636             }
1637             FileSystemView fileSystemView = getFileSystemView();
1638             if (traversable == null && fileSystemView != null) {
1639                 traversable = fileSystemView.isTraversable(f);
1640             }
1641         }
1642         return traversable != null && traversable;
1643     }
1644 
1645     /**
1646      * Returns true if the file should be displayed.
1647      * @param f the <code>File</code>
1648      * @return true if the file should be displayed, otherwise false
1649      * @see FileFilter#accept
1650      */
accept(File f)1651     public boolean accept(File f) {
1652         FileFilter filter = fileFilter;
1653         return f == null || filter == null || filter.accept(f);
1654     }
1655 
1656     /**
1657      * Sets the file system view that the <code>JFileChooser</code> uses for
1658      * accessing and creating file system resources, such as finding
1659      * the floppy drive and getting a list of root drives.
1660      * @param fsv  the new <code>FileSystemView</code>
1661      *
1662      * @see FileSystemView
1663      */
1664     @BeanProperty(expert = true, description
1665             = "Sets the FileSytemView used to get filesystem information.")
setFileSystemView(FileSystemView fsv)1666     public void setFileSystemView(FileSystemView fsv) {
1667         FileSystemView oldValue = fileSystemView;
1668         fileSystemView = fsv;
1669         firePropertyChange(FILE_SYSTEM_VIEW_CHANGED_PROPERTY, oldValue, fileSystemView);
1670     }
1671 
1672     /**
1673      * Returns the file system view.
1674      * @return the <code>FileSystemView</code> object
1675      * @see #setFileSystemView
1676      */
getFileSystemView()1677     public FileSystemView getFileSystemView() {
1678         return fileSystemView;
1679     }
1680 
1681     // **************************
1682     // ***** Event Handling *****
1683     // **************************
1684 
1685     /**
1686      * Called by the UI when the user hits the Approve button
1687      * (labeled "Open" or "Save", by default). This can also be
1688      * called by the programmer.
1689      * This method causes an action event to fire
1690      * with the command string equal to
1691      * <code>APPROVE_SELECTION</code>.
1692      *
1693      * @see #APPROVE_SELECTION
1694      */
approveSelection()1695     public void approveSelection() {
1696         returnValue = APPROVE_OPTION;
1697         if(dialog != null) {
1698             dialog.setVisible(false);
1699         }
1700         fireActionPerformed(APPROVE_SELECTION);
1701     }
1702 
1703     /**
1704      * Called by the UI when the user chooses the Cancel button.
1705      * This can also be called by the programmer.
1706      * This method causes an action event to fire
1707      * with the command string equal to
1708      * <code>CANCEL_SELECTION</code>.
1709      *
1710      * @see #CANCEL_SELECTION
1711      */
cancelSelection()1712     public void cancelSelection() {
1713         returnValue = CANCEL_OPTION;
1714         if(dialog != null) {
1715             dialog.setVisible(false);
1716         }
1717         fireActionPerformed(CANCEL_SELECTION);
1718     }
1719 
1720     /**
1721      * Adds an <code>ActionListener</code> to the file chooser.
1722      *
1723      * @param l  the listener to be added
1724      *
1725      * @see #approveSelection
1726      * @see #cancelSelection
1727      */
addActionListener(ActionListener l)1728     public void addActionListener(ActionListener l) {
1729         listenerList.add(ActionListener.class, l);
1730     }
1731 
1732     /**
1733      * Removes an <code>ActionListener</code> from the file chooser.
1734      *
1735      * @param l  the listener to be removed
1736      *
1737      * @see #addActionListener
1738      */
removeActionListener(ActionListener l)1739     public void removeActionListener(ActionListener l) {
1740         listenerList.remove(ActionListener.class, l);
1741     }
1742 
1743     /**
1744      * Returns an array of all the action listeners
1745      * registered on this file chooser.
1746      *
1747      * @return all of this file chooser's <code>ActionListener</code>s
1748      *         or an empty
1749      *         array if no action listeners are currently registered
1750      *
1751      * @see #addActionListener
1752      * @see #removeActionListener
1753      *
1754      * @since 1.4
1755      */
1756     @BeanProperty(bound = false)
getActionListeners()1757     public ActionListener[] getActionListeners() {
1758         return listenerList.getListeners(ActionListener.class);
1759     }
1760 
1761     /**
1762      * Notifies all listeners that have registered interest for
1763      * notification on this event type. The event instance
1764      * is lazily created using the <code>command</code> parameter.
1765      *
1766      * @param command a string that may specify a command associated with
1767      *                the event
1768      * @see EventListenerList
1769      */
1770     @SuppressWarnings("deprecation")
fireActionPerformed(String command)1771     protected void fireActionPerformed(String command) {
1772         // Guaranteed to return a non-null array
1773         Object[] listeners = listenerList.getListenerList();
1774         long mostRecentEventTime = EventQueue.getMostRecentEventTime();
1775         int modifiers = 0;
1776         AWTEvent currentEvent = EventQueue.getCurrentEvent();
1777         if (currentEvent instanceof InputEvent) {
1778             modifiers = ((InputEvent)currentEvent).getModifiers();
1779         } else if (currentEvent instanceof ActionEvent) {
1780             modifiers = ((ActionEvent)currentEvent).getModifiers();
1781         }
1782         ActionEvent e = null;
1783         // Process the listeners last to first, notifying
1784         // those that are interested in this event
1785         for (int i = listeners.length-2; i>=0; i-=2) {
1786             if (listeners[i]==ActionListener.class) {
1787                 // Lazily create the event:
1788                 if (e == null) {
1789                     e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
1790                                         command, mostRecentEventTime,
1791                                         modifiers);
1792                 }
1793                 ((ActionListener)listeners[i+1]).actionPerformed(e);
1794             }
1795         }
1796     }
1797 
1798     private static class WeakPCL implements PropertyChangeListener {
1799         WeakReference<JFileChooser> jfcRef;
1800 
WeakPCL(JFileChooser jfc)1801         public WeakPCL(JFileChooser jfc) {
1802             jfcRef = new WeakReference<JFileChooser>(jfc);
1803         }
propertyChange(PropertyChangeEvent ev)1804         public void propertyChange(PropertyChangeEvent ev) {
1805             assert ev.getPropertyName().equals(SHOW_HIDDEN_PROP);
1806             JFileChooser jfc = jfcRef.get();
1807             if (jfc == null) {
1808                 // Our JFileChooser is no longer around, so we no longer need to
1809                 // listen for PropertyChangeEvents.
1810                 Toolkit.getDefaultToolkit().removePropertyChangeListener(SHOW_HIDDEN_PROP, this);
1811             }
1812             else {
1813                 boolean oldValue = jfc.useFileHiding;
1814                 jfc.useFileHiding = !((Boolean)ev.getNewValue()).booleanValue();
1815                 jfc.firePropertyChange(FILE_HIDING_CHANGED_PROPERTY, oldValue, jfc.useFileHiding);
1816             }
1817         }
1818     }
1819 
1820     // *********************************
1821     // ***** Pluggable L&F methods *****
1822     // *********************************
1823 
1824     /**
1825      * Resets the UI property to a value from the current look and feel.
1826      *
1827      * @see JComponent#updateUI
1828      */
updateUI()1829     public void updateUI() {
1830         if (isAcceptAllFileFilterUsed()) {
1831             removeChoosableFileFilter(getAcceptAllFileFilter());
1832         }
1833         FileChooserUI ui = ((FileChooserUI)UIManager.getUI(this));
1834         if (fileSystemView == null) {
1835             // We were probably deserialized
1836             setFileSystemView(FileSystemView.getFileSystemView());
1837         }
1838         setUI(ui);
1839 
1840         if(isAcceptAllFileFilterUsed()) {
1841             addChoosableFileFilter(getAcceptAllFileFilter());
1842         }
1843     }
1844 
1845     /**
1846      * Returns a string that specifies the name of the L&amp;F class
1847      * that renders this component.
1848      *
1849      * @return the string "FileChooserUI"
1850      * @see JComponent#getUIClassID
1851      * @see UIDefaults#getUI
1852      */
1853     @BeanProperty(bound = false, expert = true, description
1854             = "A string that specifies the name of the L&F class.")
getUIClassID()1855     public String getUIClassID() {
1856         return uiClassID;
1857     }
1858 
1859     /**
1860      * Gets the UI object which implements the L&amp;F for this component.
1861      *
1862      * @return the FileChooserUI object that implements the FileChooserUI L&amp;F
1863      */
1864     @BeanProperty(bound = false)
getUI()1865     public FileChooserUI getUI() {
1866         return (FileChooserUI) ui;
1867     }
1868 
1869     /**
1870      * See <code>readObject</code> and <code>writeObject</code> in
1871      * <code>JComponent</code> for more
1872      * information about serialization in Swing.
1873      */
readObject(java.io.ObjectInputStream in)1874     private void readObject(java.io.ObjectInputStream in)
1875             throws IOException, ClassNotFoundException {
1876         ObjectInputStream.GetField f = in.readFields();
1877 
1878         dialogTitle = (String) f.get("dialogTitle", null);
1879         approveButtonText = (String) f.get("approveButtonText", null);
1880         approveButtonToolTipText =
1881                 (String) f.get("approveButtonToolTipText", null);
1882         approveButtonMnemonic = f.get("approveButtonMnemonic", 0);
1883         @SuppressWarnings("unchecked")
1884         Vector<FileFilter> newFilters = (Vector<FileFilter>) f.get("filters", null);
1885         if (newFilters == null) {
1886             throw new InvalidObjectException("Null filters");
1887         }
1888         filters = newFilters;
1889         dialog = (JDialog) f.get("dialog", null);
1890         int newDialogType = f.get("dialogType", OPEN_DIALOG);
1891         checkDialogType(newDialogType);
1892         dialogType = newDialogType;
1893         returnValue = f.get("returnValue", 0);
1894         accessory = (JComponent) f.get("accessory", null);
1895         fileView = (FileView) f.get("fileView", null);
1896         controlsShown = f.get("controlsShown", false);
1897         useFileHiding = f.get("useFileHiding", false);
1898         int newFileSelectionMode = f.get("fileSelectionMode", FILES_ONLY);
1899         checkFileSelectionMode(newFileSelectionMode);
1900         fileSelectionMode = newFileSelectionMode;
1901         multiSelectionEnabled = f.get("multiSelectionEnabled", false);
1902         useAcceptAllFileFilter = f.get("useAcceptAllFileFilter", false);
1903         boolean newDragEnabled = f.get("dragEnabled", false);
1904         checkDragEnabled(newDragEnabled);
1905         dragEnabled = newDragEnabled;
1906         fileFilter = (FileFilter) f.get("fileFilter", null);
1907         fileSystemView = (FileSystemView) f.get("fileSystemView", null);
1908         currentDirectory = (File) f.get("currentDirectory", null);
1909         selectedFile = (File) f.get("selectedFile", null);
1910         selectedFiles = (File[]) f.get("selectedFiles", null);
1911         accessibleContext = (AccessibleContext) f.get("accessibleContext", null);
1912 
1913         installShowFilesListener();
1914     }
1915 
1916     /**
1917      * See <code>readObject</code> and <code>writeObject</code> in
1918      * <code>JComponent</code> for more
1919      * information about serialization in Swing.
1920      */
writeObject(ObjectOutputStream s)1921     private void writeObject(ObjectOutputStream s) throws IOException {
1922         FileSystemView fsv = null;
1923 
1924         if (isAcceptAllFileFilterUsed()) {
1925             //The AcceptAllFileFilter is UI specific, it will be reset by
1926             //updateUI() after deserialization
1927             removeChoosableFileFilter(getAcceptAllFileFilter());
1928         }
1929         if (fileSystemView.equals(FileSystemView.getFileSystemView())) {
1930             //The default FileSystemView is platform specific, it will be
1931             //reset by updateUI() after deserialization
1932             fsv = fileSystemView;
1933             fileSystemView = null;
1934         }
1935         s.defaultWriteObject();
1936         if (fsv != null) {
1937             fileSystemView = fsv;
1938         }
1939         if (isAcceptAllFileFilterUsed()) {
1940             addChoosableFileFilter(getAcceptAllFileFilter());
1941         }
1942         if (getUIClassID().equals(uiClassID)) {
1943             byte count = JComponent.getWriteObjCounter(this);
1944             JComponent.setWriteObjCounter(this, --count);
1945             if (count == 0 && ui != null) {
1946                 ui.installUI(this);
1947             }
1948         }
1949     }
1950 
1951 
1952     /**
1953      * Returns a string representation of this <code>JFileChooser</code>.
1954      * This method
1955      * is intended to be used only for debugging purposes, and the
1956      * content and format of the returned string may vary between
1957      * implementations. The returned string may be empty but may not
1958      * be <code>null</code>.
1959      *
1960      * @return  a string representation of this <code>JFileChooser</code>
1961      */
paramString()1962     protected String paramString() {
1963         String approveButtonTextString = (approveButtonText != null ?
1964                                           approveButtonText: "");
1965         String dialogTitleString = (dialogTitle != null ?
1966                                     dialogTitle: "");
1967         String dialogTypeString;
1968         if (dialogType == OPEN_DIALOG) {
1969             dialogTypeString = "OPEN_DIALOG";
1970         } else if (dialogType == SAVE_DIALOG) {
1971             dialogTypeString = "SAVE_DIALOG";
1972         } else if (dialogType == CUSTOM_DIALOG) {
1973             dialogTypeString = "CUSTOM_DIALOG";
1974         } else dialogTypeString = "";
1975         String returnValueString;
1976         if (returnValue == CANCEL_OPTION) {
1977             returnValueString = "CANCEL_OPTION";
1978         } else if (returnValue == APPROVE_OPTION) {
1979             returnValueString = "APPROVE_OPTION";
1980         } else if (returnValue == ERROR_OPTION) {
1981             returnValueString = "ERROR_OPTION";
1982         } else returnValueString = "";
1983         String useFileHidingString = (useFileHiding ?
1984                                     "true" : "false");
1985         String fileSelectionModeString;
1986         if (fileSelectionMode == FILES_ONLY) {
1987             fileSelectionModeString = "FILES_ONLY";
1988         } else if (fileSelectionMode == DIRECTORIES_ONLY) {
1989             fileSelectionModeString = "DIRECTORIES_ONLY";
1990         } else if (fileSelectionMode == FILES_AND_DIRECTORIES) {
1991             fileSelectionModeString = "FILES_AND_DIRECTORIES";
1992         } else fileSelectionModeString = "";
1993         String currentDirectoryString = (currentDirectory != null ?
1994                                          currentDirectory.toString() : "");
1995         String selectedFileString = (selectedFile != null ?
1996                                      selectedFile.toString() : "");
1997 
1998         return super.paramString() +
1999         ",approveButtonText=" + approveButtonTextString +
2000         ",currentDirectory=" + currentDirectoryString +
2001         ",dialogTitle=" + dialogTitleString +
2002         ",dialogType=" + dialogTypeString +
2003         ",fileSelectionMode=" + fileSelectionModeString +
2004         ",returnValue=" + returnValueString +
2005         ",selectedFile=" + selectedFileString +
2006         ",useFileHiding=" + useFileHidingString;
2007     }
2008 
2009 /////////////////
2010 // Accessibility support
2011 ////////////////
2012 
2013     /**
2014      * {@code AccessibleContext} associated with this {@code JFileChooser}
2015      */
2016     protected AccessibleContext accessibleContext = null;
2017 
2018     /**
2019      * Gets the AccessibleContext associated with this JFileChooser.
2020      * For file choosers, the AccessibleContext takes the form of an
2021      * AccessibleJFileChooser.
2022      * A new AccessibleJFileChooser instance is created if necessary.
2023      *
2024      * @return an AccessibleJFileChooser that serves as the
2025      *         AccessibleContext of this JFileChooser
2026      */
2027     @BeanProperty(bound = false)
getAccessibleContext()2028     public AccessibleContext getAccessibleContext() {
2029         if (accessibleContext == null) {
2030             accessibleContext = new AccessibleJFileChooser();
2031         }
2032         return accessibleContext;
2033     }
2034 
2035     /**
2036      * This class implements accessibility support for the
2037      * <code>JFileChooser</code> class.  It provides an implementation of the
2038      * Java Accessibility API appropriate to file chooser user-interface
2039      * elements.
2040      */
2041     @SuppressWarnings("serial") // Superclass is not serializable across versions
2042     protected class AccessibleJFileChooser extends AccessibleJComponent {
2043 
2044         /**
2045          * Gets the role of this object.
2046          *
2047          * @return an instance of AccessibleRole describing the role of the
2048          * object
2049          * @see AccessibleRole
2050          */
getAccessibleRole()2051         public AccessibleRole getAccessibleRole() {
2052             return AccessibleRole.FILE_CHOOSER;
2053         }
2054 
2055     } // inner class AccessibleJFileChooser
2056 
2057     private class FCHierarchyListener implements HierarchyListener,
2058             Serializable {
2059         @Override
hierarchyChanged(HierarchyEvent e)2060         public void hierarchyChanged(HierarchyEvent e) {
2061             if ((e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED)
2062                     == HierarchyEvent.PARENT_CHANGED) {
2063                 JFileChooser fc = JFileChooser.this;
2064                 JRootPane rootPane = SwingUtilities.getRootPane(fc);
2065                 if (rootPane != null) {
2066                     rootPane.setDefaultButton(fc.getUI().getDefaultButton(fc));
2067                 }
2068             }
2069         }
2070     }
2071 }
2072