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