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