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://java.sun.com/docs/books/tutorial/uiswing/components/filechooser.html">How to Use File Choosers</a>, 61 * a section in <em>The Java Tutorial</em>. 62 * 63 * <p> 64 * 65 * The following code pops up a file chooser for the user's home directory that 66 * sees only .jpg and .gif images: 67 * <pre> 68 * JFileChooser chooser = new JFileChooser(); 69 * FileNameExtensionFilter filter = new FileNameExtensionFilter( 70 * "JPG & GIF Images", "jpg", "gif"); 71 * chooser.setFileFilter(filter); 72 * int returnVal = chooser.showOpenDialog(parent); 73 * if(returnVal == JFileChooser.APPROVE_OPTION) { 74 * System.out.println("You chose to open this file: " + 75 * chooser.getSelectedFile().getName()); 76 * } 77 * </pre> 78 * <p> 79 * <strong>Warning:</strong> Swing is not thread safe. For more 80 * information see <a 81 * href="package-summary.html#threading">Swing's Threading 82 * Policy</a>. 83 * 84 * @beaninfo 85 * attribute: isContainer false 86 * description: A component which allows for the interactive selection of a file. 87 * 88 * @author Jeff Dinkins 89 * 90 */ 91 public class JFileChooser extends JComponent implements Accessible { 92 93 /** 94 * @see #getUIClassID 95 * @see #readObject 96 */ 97 private static final String uiClassID = "FileChooserUI"; 98 99 // ************************ 100 // ***** Dialog Types ***** 101 // ************************ 102 103 /** 104 * Type value indicating that the <code>JFileChooser</code> supports an 105 * "Open" file operation. 106 */ 107 public static final int OPEN_DIALOG = 0; 108 109 /** 110 * Type value indicating that the <code>JFileChooser</code> supports a 111 * "Save" file operation. 112 */ 113 public static final int SAVE_DIALOG = 1; 114 115 /** 116 * Type value indicating that the <code>JFileChooser</code> supports a 117 * developer-specified file operation. 118 */ 119 public static final int CUSTOM_DIALOG = 2; 120 121 122 // ******************************** 123 // ***** Dialog Return Values ***** 124 // ******************************** 125 126 /** 127 * Return value if cancel is chosen. 128 */ 129 public static final int CANCEL_OPTION = 1; 130 131 /** 132 * Return value if approve (yes, ok) is chosen. 133 */ 134 public static final int APPROVE_OPTION = 0; 135 136 /** 137 * Return value if an error occured. 138 */ 139 public static final int ERROR_OPTION = -1; 140 141 142 // ********************************** 143 // ***** JFileChooser properties ***** 144 // ********************************** 145 146 147 /** Instruction to display only files. */ 148 public static final int FILES_ONLY = 0; 149 150 /** Instruction to display only directories. */ 151 public static final int DIRECTORIES_ONLY = 1; 152 153 /** Instruction to display both files and directories. */ 154 public static final int FILES_AND_DIRECTORIES = 2; 155 156 /** Instruction to cancel the current selection. */ 157 public static final String CANCEL_SELECTION = "CancelSelection"; 158 159 /** 160 * Instruction to approve the current selection 161 * (same as pressing yes or ok). 162 */ 163 public static final String APPROVE_SELECTION = "ApproveSelection"; 164 165 /** Identifies change in the text on the approve (yes, ok) button. */ 166 public static final String APPROVE_BUTTON_TEXT_CHANGED_PROPERTY = "ApproveButtonTextChangedProperty"; 167 168 /** 169 * Identifies change in the tooltip text for the approve (yes, ok) 170 * button. 171 */ 172 public static final String APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY = "ApproveButtonToolTipTextChangedProperty"; 173 174 /** Identifies change in the mnemonic for the approve (yes, ok) button. */ 175 public static final String APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY = "ApproveButtonMnemonicChangedProperty"; 176 177 /** Instruction to display the control buttons. */ 178 public static final String CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY = "ControlButtonsAreShownChangedProperty"; 179 180 /** Identifies user's directory change. */ 181 public static final String DIRECTORY_CHANGED_PROPERTY = "directoryChanged"; 182 183 /** Identifies change in user's single-file selection. */ 184 public static final String SELECTED_FILE_CHANGED_PROPERTY = "SelectedFileChangedProperty"; 185 186 /** Identifies change in user's multiple-file selection. */ 187 public static final String SELECTED_FILES_CHANGED_PROPERTY = "SelectedFilesChangedProperty"; 188 189 /** Enables multiple-file selections. */ 190 public static final String MULTI_SELECTION_ENABLED_CHANGED_PROPERTY = "MultiSelectionEnabledChangedProperty"; 191 192 /** 193 * Says that a different object is being used to find available drives 194 * on the system. 195 */ 196 public static final String FILE_SYSTEM_VIEW_CHANGED_PROPERTY = "FileSystemViewChanged"; 197 198 /** 199 * Says that a different object is being used to retrieve file 200 * information. 201 */ 202 public static final String FILE_VIEW_CHANGED_PROPERTY = "fileViewChanged"; 203 204 /** Identifies a change in the display-hidden-files property. */ 205 public static final String FILE_HIDING_CHANGED_PROPERTY = "FileHidingChanged"; 206 207 /** User changed the kind of files to display. */ 208 public static final String FILE_FILTER_CHANGED_PROPERTY = "fileFilterChanged"; 209 210 /** 211 * Identifies a change in the kind of selection (single, 212 * multiple, etc.). 213 */ 214 public static final String FILE_SELECTION_MODE_CHANGED_PROPERTY = "fileSelectionChanged"; 215 216 /** 217 * Says that a different accessory component is in use 218 * (for example, to preview files). 219 */ 220 public static final String ACCESSORY_CHANGED_PROPERTY = "AccessoryChangedProperty"; 221 222 /** 223 * Identifies whether a the AcceptAllFileFilter is used or not. 224 */ 225 public static final String ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY = "acceptAllFileFilterUsedChanged"; 226 227 /** Identifies a change in the dialog title. */ 228 public static final String DIALOG_TITLE_CHANGED_PROPERTY = "DialogTitleChangedProperty"; 229 230 /** 231 * Identifies a change in the type of files displayed (files only, 232 * directories only, or both files and directories). 233 */ 234 public static final String DIALOG_TYPE_CHANGED_PROPERTY = "DialogTypeChangedProperty"; 235 236 /** 237 * Identifies a change in the list of predefined file filters 238 * the user can choose from. 239 */ 240 public static final String CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY = "ChoosableFileFilterChangedProperty"; 241 242 // ****************************** 243 // ***** instance variables ***** 244 // ****************************** 245 246 private String dialogTitle = null; 247 private String approveButtonText = null; 248 private String approveButtonToolTipText = null; 249 private int approveButtonMnemonic = 0; 250 251 private Vector<FileFilter> filters = new Vector<FileFilter>(5); 252 private JDialog dialog = null; 253 private int dialogType = OPEN_DIALOG; 254 private int returnValue = ERROR_OPTION; 255 private JComponent accessory = null; 256 257 private FileView fileView = null; 258 259 private boolean controlsShown = true; 260 261 private boolean useFileHiding = true; 262 private static final String SHOW_HIDDEN_PROP = "awt.file.showHiddenFiles"; 263 264 // Listens to changes in the native setting for showing hidden files. 265 // The Listener is removed and the native setting is ignored if 266 // setFileHidingEnabled() is ever called. 267 private transient PropertyChangeListener showFilesListener = null; 268 269 private int fileSelectionMode = FILES_ONLY; 270 271 private boolean multiSelectionEnabled = false; 272 273 private boolean useAcceptAllFileFilter = true; 274 275 private boolean dragEnabled = false; 276 277 private FileFilter fileFilter = null; 278 279 private FileSystemView fileSystemView = null; 280 281 private File currentDirectory = null; 282 private File selectedFile = null; 283 private File[] selectedFiles; 284 285 // ************************************* 286 // ***** JFileChooser Constructors ***** 287 // ************************************* 288 289 /** 290 * Constructs a <code>JFileChooser</code> pointing to the user's 291 * default directory. This default depends on the operating system. 292 * It is typically the "My Documents" folder on Windows, and the 293 * user's home directory on Unix. 294 */ 295 public JFileChooser() { 296 this((File) null, (FileSystemView) null); 297 } 298 299 /** 300 * Constructs a <code>JFileChooser</code> using the given path. 301 * Passing in a <code>null</code> 302 * string causes the file chooser to point to the user's default directory. 303 * This default depends on the operating system. It is 304 * typically the "My Documents" folder on Windows, and the user's 305 * home directory on Unix. 306 * 307 * @param currentDirectoryPath a <code>String</code> giving the path 308 * to a file or directory 309 */ 310 public JFileChooser(String currentDirectoryPath) { 311 this(currentDirectoryPath, (FileSystemView) null); 312 } 313 314 /** 315 * Constructs a <code>JFileChooser</code> using the given <code>File</code> 316 * as the path. Passing in a <code>null</code> file 317 * causes the file chooser to point to the user's default directory. 318 * This default depends on the operating system. It is 319 * typically the "My Documents" folder on Windows, and the user's 320 * home directory on Unix. 321 * 322 * @param currentDirectory a <code>File</code> object specifying 323 * the path to a file or directory 324 */ 325 public JFileChooser(File currentDirectory) { 326 this(currentDirectory, (FileSystemView) null); 327 } 328 329 /** 330 * Constructs a <code>JFileChooser</code> using the given 331 * <code>FileSystemView</code>. 332 */ 333 public JFileChooser(FileSystemView fsv) { 334 this((File) null, fsv); 335 } 336 337 338 /** 339 * Constructs a <code>JFileChooser</code> using the given current directory 340 * and <code>FileSystemView</code>. 341 */ 342 public JFileChooser(File currentDirectory, FileSystemView fsv) { 343 setup(fsv); 344 setCurrentDirectory(currentDirectory); 345 } 346 347 /** 348 * Constructs a <code>JFileChooser</code> using the given current directory 349 * path and <code>FileSystemView</code>. 350 */ 351 public JFileChooser(String currentDirectoryPath, FileSystemView fsv) { 352 setup(fsv); 353 if(currentDirectoryPath == null) { 354 setCurrentDirectory(null); 355 } else { 356 setCurrentDirectory(fileSystemView.createFileObject(currentDirectoryPath)); 357 } 358 } 359 360 /** 361 * Performs common constructor initialization and setup. 362 */ 363 protected void setup(FileSystemView view) { 364 installShowFilesListener(); 365 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 }