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