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