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 & 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&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&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 & 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&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&F class. 1854 */ 1855 public String getUIClassID() { 1856 return uiClassID; 1857 } 1858 1859 /** 1860 * Gets the UI object which implements the L&F for this component. 1861 * 1862 * @return the FileChooserUI object that implements the FileChooserUI L&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 }