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