1 /* 2 * Copyright (c) 1998, 2008, 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.plaf.basic; 27 28 import javax.swing.*; 29 import javax.swing.filechooser.*; 30 import javax.swing.filechooser.FileFilter; 31 import javax.swing.event.*; 32 import javax.swing.plaf.*; 33 import java.awt.*; 34 import java.awt.event.*; 35 import java.awt.datatransfer.*; 36 import java.beans.*; 37 import java.io.*; 38 import java.util.*; 39 import java.util.List; 40 import java.util.regex.*; 41 import sun.awt.shell.ShellFolder; 42 import sun.swing.*; 43 import sun.swing.SwingUtilities2; 44 45 /** 46 * Basic L&F implementation of a FileChooser. 47 * 48 * @author Jeff Dinkins 49 */ 50 public class BasicFileChooserUI extends FileChooserUI { 51 52 /* FileView icons */ 53 protected Icon directoryIcon = null; 54 protected Icon fileIcon = null; 55 protected Icon computerIcon = null; 56 protected Icon hardDriveIcon = null; 57 protected Icon floppyDriveIcon = null; 58 59 protected Icon newFolderIcon = null; 60 protected Icon upFolderIcon = null; 61 protected Icon homeFolderIcon = null; 62 protected Icon listViewIcon = null; 63 protected Icon detailsViewIcon = null; 64 protected Icon viewMenuIcon = null; 65 66 protected int saveButtonMnemonic = 0; 67 protected int openButtonMnemonic = 0; 68 protected int cancelButtonMnemonic = 0; 69 protected int updateButtonMnemonic = 0; 70 protected int helpButtonMnemonic = 0; 71 72 /** 73 * The mnemonic keycode used for the approve button when a directory 74 * is selected and the current selection mode is FILES_ONLY. 75 * 76 * @since 1.4 77 */ 78 protected int directoryOpenButtonMnemonic = 0; 79 80 protected String saveButtonText = null; 81 protected String openButtonText = null; 82 protected String cancelButtonText = null; 83 protected String updateButtonText = null; 84 protected String helpButtonText = null; 85 86 /** 87 * The label text displayed on the approve button when a directory 88 * is selected and the current selection mode is FILES_ONLY. 89 * 90 * @since 1.4 91 */ 92 protected String directoryOpenButtonText = null; 93 94 private String openDialogTitleText = null; 95 private String saveDialogTitleText = null; 96 97 protected String saveButtonToolTipText = null; 98 protected String openButtonToolTipText = null; 99 protected String cancelButtonToolTipText = null; 100 protected String updateButtonToolTipText = null; 101 protected String helpButtonToolTipText = null; 102 103 /** 104 * The tooltip text displayed on the approve button when a directory 105 * is selected and the current selection mode is FILES_ONLY. 106 * 107 * @since 1.4 108 */ 109 protected String directoryOpenButtonToolTipText = null; 110 111 // Some generic FileChooser functions 112 private Action approveSelectionAction = new ApproveSelectionAction(); 113 private Action cancelSelectionAction = new CancelSelectionAction(); 114 private Action updateAction = new UpdateAction(); 115 private Action newFolderAction; 116 private Action goHomeAction = new GoHomeAction(); 117 private Action changeToParentDirectoryAction = new ChangeToParentDirectoryAction(); 118 119 private String newFolderErrorSeparator = null; 120 private String newFolderErrorText = null; 121 private String newFolderParentDoesntExistTitleText = null; 122 private String newFolderParentDoesntExistText = null; 123 private String fileDescriptionText = null; 124 private String directoryDescriptionText = null; 125 126 private JFileChooser filechooser = null; 127 128 private boolean directorySelected = false; 129 private File directory = null; 130 131 private PropertyChangeListener propertyChangeListener = null; 132 private AcceptAllFileFilter acceptAllFileFilter = new AcceptAllFileFilter(); 133 private FileFilter actualFileFilter = null; 134 private GlobFilter globFilter = null; 135 private BasicDirectoryModel model = null; 136 private BasicFileView fileView = new BasicFileView(); 137 private boolean usesSingleFilePane; 138 private boolean readOnly; 139 140 // The accessoryPanel is a container to place the JFileChooser accessory component 141 private JPanel accessoryPanel = null; 142 private Handler handler; 143 144 /** 145 * Creates a {@code BasicFileChooserUI} implementation 146 * for the specified component. By default 147 * the {@code BasicLookAndFeel} class uses 148 * {@code createUI} methods of all basic UIs classes 149 * to instantiate UIs. 150 * 151 * @param c the {@code JFileChooser} which needs a UI 152 * @return the {@code BasicFileChooserUI} object 153 * 154 * @see UIDefaults#getUI(JComponent) 155 * @since 1.7 156 */ 157 public static ComponentUI createUI(JComponent c) { 158 return new BasicFileChooserUI((JFileChooser) c); 159 } 160 161 public BasicFileChooserUI(JFileChooser b) { 162 } 163 164 public void installUI(JComponent c) { 165 accessoryPanel = new JPanel(new BorderLayout()); 166 filechooser = (JFileChooser) c; 167 168 createModel(); 169 170 clearIconCache(); 171 172 installDefaults(filechooser); 173 installComponents(filechooser); 174 installListeners(filechooser); 175 filechooser.applyComponentOrientation(filechooser.getComponentOrientation()); 176 } 177 178 public void uninstallUI(JComponent c) { 179 uninstallListeners(filechooser); 180 uninstallComponents(filechooser); 181 uninstallDefaults(filechooser); 182 183 if(accessoryPanel != null) { 184 accessoryPanel.removeAll(); 185 } 186 187 accessoryPanel = null; 188 getFileChooser().removeAll(); 189 190 handler = null; 191 } 192 193 public void installComponents(JFileChooser fc) { 194 } 195 196 public void uninstallComponents(JFileChooser fc) { 197 } 198 199 protected void installListeners(JFileChooser fc) { 200 propertyChangeListener = createPropertyChangeListener(fc); 201 if(propertyChangeListener != null) { 202 fc.addPropertyChangeListener(propertyChangeListener); 203 } 204 fc.addPropertyChangeListener(getModel()); 205 206 InputMap inputMap = getInputMap(JComponent. 207 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 208 SwingUtilities.replaceUIInputMap(fc, JComponent. 209 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap); 210 ActionMap actionMap = getActionMap(); 211 SwingUtilities.replaceUIActionMap(fc, actionMap); 212 } 213 214 InputMap getInputMap(int condition) { 215 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) { 216 return (InputMap)DefaultLookup.get(getFileChooser(), this, 217 "FileChooser.ancestorInputMap"); 218 } 219 return null; 220 } 221 222 ActionMap getActionMap() { 223 return createActionMap(); 224 } 225 226 ActionMap createActionMap() { 227 ActionMap map = new ActionMapUIResource(); 228 229 Action refreshAction = new UIAction(FilePane.ACTION_REFRESH) { 230 public void actionPerformed(ActionEvent evt) { 231 getFileChooser().rescanCurrentDirectory(); 232 } 233 }; 234 235 map.put(FilePane.ACTION_APPROVE_SELECTION, getApproveSelectionAction()); 236 map.put(FilePane.ACTION_CANCEL, getCancelSelectionAction()); 237 map.put(FilePane.ACTION_REFRESH, refreshAction); 238 map.put(FilePane.ACTION_CHANGE_TO_PARENT_DIRECTORY, 239 getChangeToParentDirectoryAction()); 240 return map; 241 } 242 243 244 protected void uninstallListeners(JFileChooser fc) { 245 if(propertyChangeListener != null) { 246 fc.removePropertyChangeListener(propertyChangeListener); 247 } 248 fc.removePropertyChangeListener(getModel()); 249 SwingUtilities.replaceUIInputMap(fc, JComponent. 250 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); 251 SwingUtilities.replaceUIActionMap(fc, null); 252 } 253 254 255 protected void installDefaults(JFileChooser fc) { 256 installIcons(fc); 257 installStrings(fc); 258 usesSingleFilePane = UIManager.getBoolean("FileChooser.usesSingleFilePane"); 259 readOnly = UIManager.getBoolean("FileChooser.readOnly"); 260 TransferHandler th = fc.getTransferHandler(); 261 if (th == null || th instanceof UIResource) { 262 fc.setTransferHandler(defaultTransferHandler); 263 } 264 LookAndFeel.installProperty(fc, "opaque", Boolean.FALSE); 265 } 266 267 protected void installIcons(JFileChooser fc) { 268 directoryIcon = UIManager.getIcon("FileView.directoryIcon"); 269 fileIcon = UIManager.getIcon("FileView.fileIcon"); 270 computerIcon = UIManager.getIcon("FileView.computerIcon"); 271 hardDriveIcon = UIManager.getIcon("FileView.hardDriveIcon"); 272 floppyDriveIcon = UIManager.getIcon("FileView.floppyDriveIcon"); 273 274 newFolderIcon = UIManager.getIcon("FileChooser.newFolderIcon"); 275 upFolderIcon = UIManager.getIcon("FileChooser.upFolderIcon"); 276 homeFolderIcon = UIManager.getIcon("FileChooser.homeFolderIcon"); 277 detailsViewIcon = UIManager.getIcon("FileChooser.detailsViewIcon"); 278 listViewIcon = UIManager.getIcon("FileChooser.listViewIcon"); 279 viewMenuIcon = UIManager.getIcon("FileChooser.viewMenuIcon"); 280 } 281 282 protected void installStrings(JFileChooser fc) { 283 284 Locale l = fc.getLocale(); 285 newFolderErrorText = UIManager.getString("FileChooser.newFolderErrorText",l); 286 newFolderErrorSeparator = UIManager.getString("FileChooser.newFolderErrorSeparator",l); 287 288 newFolderParentDoesntExistTitleText = UIManager.getString("FileChooser.newFolderParentDoesntExistTitleText", l); 289 newFolderParentDoesntExistText = UIManager.getString("FileChooser.newFolderParentDoesntExistText", l); 290 291 fileDescriptionText = UIManager.getString("FileChooser.fileDescriptionText",l); 292 directoryDescriptionText = UIManager.getString("FileChooser.directoryDescriptionText",l); 293 294 saveButtonText = UIManager.getString("FileChooser.saveButtonText",l); 295 openButtonText = UIManager.getString("FileChooser.openButtonText",l); 296 saveDialogTitleText = UIManager.getString("FileChooser.saveDialogTitleText",l); 297 openDialogTitleText = UIManager.getString("FileChooser.openDialogTitleText",l); 298 cancelButtonText = UIManager.getString("FileChooser.cancelButtonText",l); 299 updateButtonText = UIManager.getString("FileChooser.updateButtonText",l); 300 helpButtonText = UIManager.getString("FileChooser.helpButtonText",l); 301 directoryOpenButtonText = UIManager.getString("FileChooser.directoryOpenButtonText",l); 302 303 saveButtonMnemonic = getMnemonic("FileChooser.saveButtonMnemonic", l); 304 openButtonMnemonic = getMnemonic("FileChooser.openButtonMnemonic", l); 305 cancelButtonMnemonic = getMnemonic("FileChooser.cancelButtonMnemonic", l); 306 updateButtonMnemonic = getMnemonic("FileChooser.updateButtonMnemonic", l); 307 helpButtonMnemonic = getMnemonic("FileChooser.helpButtonMnemonic", l); 308 directoryOpenButtonMnemonic = getMnemonic("FileChooser.directoryOpenButtonMnemonic", l); 309 310 saveButtonToolTipText = UIManager.getString("FileChooser.saveButtonToolTipText",l); 311 openButtonToolTipText = UIManager.getString("FileChooser.openButtonToolTipText",l); 312 cancelButtonToolTipText = UIManager.getString("FileChooser.cancelButtonToolTipText",l); 313 updateButtonToolTipText = UIManager.getString("FileChooser.updateButtonToolTipText",l); 314 helpButtonToolTipText = UIManager.getString("FileChooser.helpButtonToolTipText",l); 315 directoryOpenButtonToolTipText = UIManager.getString("FileChooser.directoryOpenButtonToolTipText",l); 316 } 317 318 protected void uninstallDefaults(JFileChooser fc) { 319 uninstallIcons(fc); 320 uninstallStrings(fc); 321 if (fc.getTransferHandler() instanceof UIResource) { 322 fc.setTransferHandler(null); 323 } 324 } 325 326 protected void uninstallIcons(JFileChooser fc) { 327 directoryIcon = null; 328 fileIcon = null; 329 computerIcon = null; 330 hardDriveIcon = null; 331 floppyDriveIcon = null; 332 333 newFolderIcon = null; 334 upFolderIcon = null; 335 homeFolderIcon = null; 336 detailsViewIcon = null; 337 listViewIcon = null; 338 viewMenuIcon = null; 339 } 340 341 protected void uninstallStrings(JFileChooser fc) { 342 saveButtonText = null; 343 openButtonText = null; 344 cancelButtonText = null; 345 updateButtonText = null; 346 helpButtonText = null; 347 directoryOpenButtonText = null; 348 349 saveButtonToolTipText = null; 350 openButtonToolTipText = null; 351 cancelButtonToolTipText = null; 352 updateButtonToolTipText = null; 353 helpButtonToolTipText = null; 354 directoryOpenButtonToolTipText = null; 355 } 356 357 protected void createModel() { 358 if (model != null) { 359 model.invalidateFileCache(); 360 } 361 model = new BasicDirectoryModel(getFileChooser()); 362 } 363 364 public BasicDirectoryModel getModel() { 365 return model; 366 } 367 368 public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { 369 return null; 370 } 371 372 public String getFileName() { 373 return null; 374 } 375 376 public String getDirectoryName() { 377 return null; 378 } 379 380 public void setFileName(String filename) { 381 } 382 383 public void setDirectoryName(String dirname) { 384 } 385 386 public void rescanCurrentDirectory(JFileChooser fc) { 387 } 388 389 public void ensureFileIsVisible(JFileChooser fc, File f) { 390 } 391 392 public JFileChooser getFileChooser() { 393 return filechooser; 394 } 395 396 public JPanel getAccessoryPanel() { 397 return accessoryPanel; 398 } 399 400 protected JButton getApproveButton(JFileChooser fc) { 401 return null; 402 } 403 404 public JButton getDefaultButton(JFileChooser fc) { 405 return getApproveButton(fc); 406 } 407 408 public String getApproveButtonToolTipText(JFileChooser fc) { 409 String tooltipText = fc.getApproveButtonToolTipText(); 410 if(tooltipText != null) { 411 return tooltipText; 412 } 413 414 if(fc.getDialogType() == JFileChooser.OPEN_DIALOG) { 415 return openButtonToolTipText; 416 } else if(fc.getDialogType() == JFileChooser.SAVE_DIALOG) { 417 return saveButtonToolTipText; 418 } 419 return null; 420 } 421 422 public void clearIconCache() { 423 fileView.clearIconCache(); 424 } 425 426 427 // ******************************************** 428 // ************ Create Listeners ************** 429 // ******************************************** 430 431 private Handler getHandler() { 432 if (handler == null) { 433 handler = new Handler(); 434 } 435 return handler; 436 } 437 438 protected MouseListener createDoubleClickListener(JFileChooser fc, 439 JList list) { 440 return new Handler(list); 441 } 442 443 public ListSelectionListener createListSelectionListener(JFileChooser fc) { 444 return getHandler(); 445 } 446 447 private class Handler implements MouseListener, ListSelectionListener { 448 JList list; 449 450 Handler() { 451 } 452 453 Handler(JList list) { 454 this.list = list; 455 } 456 457 public void mouseClicked(MouseEvent evt) { 458 // Note: we can't depend on evt.getSource() because of backward 459 // compatability 460 if (list != null && 461 SwingUtilities.isLeftMouseButton(evt) && 462 (evt.getClickCount()%2 == 0)) { 463 464 int index = SwingUtilities2.loc2IndexFileList(list, evt.getPoint()); 465 if (index >= 0) { 466 File f = (File)list.getModel().getElementAt(index); 467 try { 468 // Strip trailing ".." 469 f = ShellFolder.getNormalizedFile(f); 470 } catch (IOException ex) { 471 // That's ok, we'll use f as is 472 } 473 if(getFileChooser().isTraversable(f)) { 474 list.clearSelection(); 475 changeDirectory(f); 476 } else { 477 getFileChooser().approveSelection(); 478 } 479 } 480 } 481 } 482 483 public void mouseEntered(MouseEvent evt) { 484 if (list != null) { 485 TransferHandler th1 = getFileChooser().getTransferHandler(); 486 TransferHandler th2 = list.getTransferHandler(); 487 if (th1 != th2) { 488 list.setTransferHandler(th1); 489 } 490 if (getFileChooser().getDragEnabled() != list.getDragEnabled()) { 491 list.setDragEnabled(getFileChooser().getDragEnabled()); 492 } 493 } 494 } 495 496 public void mouseExited(MouseEvent evt) { 497 } 498 499 public void mousePressed(MouseEvent evt) { 500 } 501 502 public void mouseReleased(MouseEvent evt) { 503 } 504 505 public void valueChanged(ListSelectionEvent evt) { 506 if(!evt.getValueIsAdjusting()) { 507 JFileChooser chooser = getFileChooser(); 508 FileSystemView fsv = chooser.getFileSystemView(); 509 JList list = (JList)evt.getSource(); 510 511 int fsm = chooser.getFileSelectionMode(); 512 boolean useSetDirectory = usesSingleFilePane && 513 (fsm == JFileChooser.FILES_ONLY); 514 515 if (chooser.isMultiSelectionEnabled()) { 516 File[] files = null; 517 Object[] objects = list.getSelectedValues(); 518 if (objects != null) { 519 if (objects.length == 1 520 && ((File)objects[0]).isDirectory() 521 && chooser.isTraversable(((File)objects[0])) 522 && (useSetDirectory || !fsv.isFileSystem(((File)objects[0])))) { 523 setDirectorySelected(true); 524 setDirectory(((File)objects[0])); 525 } else { 526 ArrayList<File> fList = new ArrayList<File>(objects.length); 527 for (Object object : objects) { 528 File f = (File) object; 529 boolean isDir = f.isDirectory(); 530 if ((chooser.isFileSelectionEnabled() && !isDir) 531 || (chooser.isDirectorySelectionEnabled() 532 && fsv.isFileSystem(f) 533 && isDir)) { 534 fList.add(f); 535 } 536 } 537 if (fList.size() > 0) { 538 files = fList.toArray(new File[fList.size()]); 539 } 540 setDirectorySelected(false); 541 } 542 } 543 chooser.setSelectedFiles(files); 544 } else { 545 File file = (File)list.getSelectedValue(); 546 if (file != null 547 && file.isDirectory() 548 && chooser.isTraversable(file) 549 && (useSetDirectory || !fsv.isFileSystem(file))) { 550 551 setDirectorySelected(true); 552 setDirectory(file); 553 if (usesSingleFilePane) { 554 chooser.setSelectedFile(null); 555 } 556 } else { 557 setDirectorySelected(false); 558 if (file != null) { 559 chooser.setSelectedFile(file); 560 } 561 } 562 } 563 } 564 } 565 } 566 567 protected class DoubleClickListener extends MouseAdapter { 568 // NOTE: This class exists only for backward compatability. All 569 // its functionality has been moved into Handler. If you need to add 570 // new functionality add it to the Handler, but make sure this 571 // class calls into the Handler. 572 Handler handler; 573 public DoubleClickListener(JList list) { 574 handler = new Handler(list); 575 } 576 577 /** 578 * The JList used for representing the files is created by subclasses, but the 579 * selection is monitored in this class. The TransferHandler installed in the 580 * JFileChooser is also installed in the file list as it is used as the actual 581 * transfer source. The list is updated on a mouse enter to reflect the current 582 * data transfer state of the file chooser. 583 */ 584 public void mouseEntered(MouseEvent e) { 585 handler.mouseEntered(e); 586 } 587 588 public void mouseClicked(MouseEvent e) { 589 handler.mouseClicked(e); 590 } 591 } 592 593 protected class SelectionListener implements ListSelectionListener { 594 // NOTE: This class exists only for backward compatability. All 595 // its functionality has been moved into Handler. If you need to add 596 // new functionality add it to the Handler, but make sure this 597 // class calls into the Handler. 598 public void valueChanged(ListSelectionEvent e) { 599 getHandler().valueChanged(e); 600 } 601 } 602 603 /** 604 * Property to remember whether a directory is currently selected in the UI. 605 * 606 * @return <code>true</code> iff a directory is currently selected. 607 * @since 1.4 608 */ 609 protected boolean isDirectorySelected() { 610 return directorySelected; 611 } 612 613 /** 614 * Property to remember whether a directory is currently selected in the UI. 615 * This is normally called by the UI on a selection event. 616 * 617 * @param b iff a directory is currently selected. 618 * @since 1.4 619 */ 620 protected void setDirectorySelected(boolean b) { 621 directorySelected = b; 622 } 623 624 /** 625 * Property to remember the directory that is currently selected in the UI. 626 * 627 * @return the value of the <code>directory</code> property 628 * @see #setDirectory 629 * @since 1.4 630 */ 631 protected File getDirectory() { 632 return directory; 633 } 634 635 /** 636 * Property to remember the directory that is currently selected in the UI. 637 * This is normally called by the UI on a selection event. 638 * 639 * @param f the <code>File</code> object representing the directory that is 640 * currently selected 641 * @since 1.4 642 */ 643 protected void setDirectory(File f) { 644 directory = f; 645 } 646 647 /** 648 * Returns the mnemonic for the given key. 649 */ 650 private int getMnemonic(String key, Locale l) { 651 return SwingUtilities2.getUIDefaultsInt(key, l); 652 } 653 654 // ******************************************************* 655 // ************ FileChooser UI PLAF methods ************** 656 // ******************************************************* 657 658 /** 659 * Returns the default accept all file filter 660 */ 661 public FileFilter getAcceptAllFileFilter(JFileChooser fc) { 662 return acceptAllFileFilter; 663 } 664 665 666 public FileView getFileView(JFileChooser fc) { 667 return fileView; 668 } 669 670 671 /** 672 * Returns the title of this dialog 673 */ 674 public String getDialogTitle(JFileChooser fc) { 675 String dialogTitle = fc.getDialogTitle(); 676 if (dialogTitle != null) { 677 return dialogTitle; 678 } else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) { 679 return openDialogTitleText; 680 } else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) { 681 return saveDialogTitleText; 682 } else { 683 return getApproveButtonText(fc); 684 } 685 } 686 687 688 public int getApproveButtonMnemonic(JFileChooser fc) { 689 int mnemonic = fc.getApproveButtonMnemonic(); 690 if (mnemonic > 0) { 691 return mnemonic; 692 } else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) { 693 return openButtonMnemonic; 694 } else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) { 695 return saveButtonMnemonic; 696 } else { 697 return mnemonic; 698 } 699 } 700 701 public String getApproveButtonText(JFileChooser fc) { 702 String buttonText = fc.getApproveButtonText(); 703 if (buttonText != null) { 704 return buttonText; 705 } else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) { 706 return openButtonText; 707 } else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) { 708 return saveButtonText; 709 } else { 710 return null; 711 } 712 } 713 714 715 // ***************************** 716 // ***** Directory Actions ***** 717 // ***************************** 718 719 public Action getNewFolderAction() { 720 if (newFolderAction == null) { 721 newFolderAction = new NewFolderAction(); 722 // Note: Don't return null for readOnly, it might 723 // break older apps. 724 if (readOnly) { 725 newFolderAction.setEnabled(false); 726 } 727 } 728 return newFolderAction; 729 } 730 731 public Action getGoHomeAction() { 732 return goHomeAction; 733 } 734 735 public Action getChangeToParentDirectoryAction() { 736 return changeToParentDirectoryAction; 737 } 738 739 public Action getApproveSelectionAction() { 740 return approveSelectionAction; 741 } 742 743 public Action getCancelSelectionAction() { 744 return cancelSelectionAction; 745 } 746 747 public Action getUpdateAction() { 748 return updateAction; 749 } 750 751 752 /** 753 * Creates a new folder. 754 */ 755 protected class NewFolderAction extends AbstractAction { 756 protected NewFolderAction() { 757 super(FilePane.ACTION_NEW_FOLDER); 758 } 759 public void actionPerformed(ActionEvent e) { 760 if (readOnly) { 761 return; 762 } 763 JFileChooser fc = getFileChooser(); 764 File currentDirectory = fc.getCurrentDirectory(); 765 766 if (!currentDirectory.exists()) { 767 JOptionPane.showMessageDialog( 768 fc, 769 newFolderParentDoesntExistText, 770 newFolderParentDoesntExistTitleText, JOptionPane.WARNING_MESSAGE); 771 return; 772 } 773 774 File newFolder; 775 try { 776 newFolder = fc.getFileSystemView().createNewFolder(currentDirectory); 777 if (fc.isMultiSelectionEnabled()) { 778 fc.setSelectedFiles(new File[] { newFolder }); 779 } else { 780 fc.setSelectedFile(newFolder); 781 } 782 } catch (IOException exc) { 783 JOptionPane.showMessageDialog( 784 fc, 785 newFolderErrorText + newFolderErrorSeparator + exc, 786 newFolderErrorText, JOptionPane.ERROR_MESSAGE); 787 return; 788 } 789 790 fc.rescanCurrentDirectory(); 791 } 792 } 793 794 /** 795 * Acts on the "home" key event or equivalent event. 796 */ 797 protected class GoHomeAction extends AbstractAction { 798 protected GoHomeAction() { 799 super("Go Home"); 800 } 801 public void actionPerformed(ActionEvent e) { 802 JFileChooser fc = getFileChooser(); 803 changeDirectory(fc.getFileSystemView().getHomeDirectory()); 804 } 805 } 806 807 protected class ChangeToParentDirectoryAction extends AbstractAction { 808 protected ChangeToParentDirectoryAction() { 809 super("Go Up"); 810 putValue(Action.ACTION_COMMAND_KEY, FilePane.ACTION_CHANGE_TO_PARENT_DIRECTORY); 811 } 812 public void actionPerformed(ActionEvent e) { 813 Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); 814 if (focusOwner == null || !(focusOwner instanceof javax.swing.text.JTextComponent)) { 815 getFileChooser().changeToParentDirectory(); 816 } 817 } 818 } 819 820 /** 821 * Responds to an Open or Save request 822 */ 823 protected class ApproveSelectionAction extends AbstractAction { 824 protected ApproveSelectionAction() { 825 super(FilePane.ACTION_APPROVE_SELECTION); 826 } 827 public void actionPerformed(ActionEvent e) { 828 if (isDirectorySelected()) { 829 File dir = getDirectory(); 830 if (dir != null) { 831 try { 832 // Strip trailing ".." 833 dir = ShellFolder.getNormalizedFile(dir); 834 } catch (IOException ex) { 835 // Ok, use f as is 836 } 837 changeDirectory(dir); 838 return; 839 } 840 } 841 842 JFileChooser chooser = getFileChooser(); 843 844 String filename = getFileName(); 845 FileSystemView fs = chooser.getFileSystemView(); 846 File dir = chooser.getCurrentDirectory(); 847 848 if (filename != null) { 849 // Remove whitespaces from end of filename 850 int i = filename.length() - 1; 851 852 while (i >=0 && filename.charAt(i) <= ' ') { 853 i--; 854 } 855 856 filename = filename.substring(0, i + 1); 857 } 858 859 if (filename == null || filename.length() == 0) { 860 // no file selected, multiple selection off, therefore cancel the approve action 861 resetGlobFilter(); 862 return; 863 } 864 865 File selectedFile = null; 866 File[] selectedFiles = null; 867 868 // Unix: Resolve '~' to user's home directory 869 if (File.separatorChar == '/') { 870 if (filename.startsWith("~/")) { 871 filename = System.getProperty("user.home") + filename.substring(1); 872 } else if (filename.equals("~")) { 873 filename = System.getProperty("user.home"); 874 } 875 } 876 877 if (chooser.isMultiSelectionEnabled() && filename.length() > 1 && 878 filename.charAt(0) == '"' && filename.charAt(filename.length() - 1) == '"') { 879 List<File> fList = new ArrayList<File>(); 880 881 String[] files = filename.substring(1, filename.length() - 1).split("\" \""); 882 // Optimize searching files by names in "children" array 883 Arrays.sort(files); 884 885 File[] children = null; 886 int childIndex = 0; 887 888 for (String str : files) { 889 File file = fs.createFileObject(str); 890 if (!file.isAbsolute()) { 891 if (children == null) { 892 children = fs.getFiles(dir, false); 893 Arrays.sort(children); 894 } 895 for (int k = 0; k < children.length; k++) { 896 int l = (childIndex + k) % children.length; 897 if (children[l].getName().equals(str)) { 898 file = children[l]; 899 childIndex = l + 1; 900 break; 901 } 902 } 903 } 904 fList.add(file); 905 } 906 907 if (!fList.isEmpty()) { 908 selectedFiles = fList.toArray(new File[fList.size()]); 909 } 910 resetGlobFilter(); 911 } else { 912 selectedFile = fs.createFileObject(filename); 913 if (!selectedFile.isAbsolute()) { 914 selectedFile = fs.getChild(dir, filename); 915 } 916 // check for wildcard pattern 917 FileFilter currentFilter = chooser.getFileFilter(); 918 if (!selectedFile.exists() && isGlobPattern(filename)) { 919 changeDirectory(selectedFile.getParentFile()); 920 if (globFilter == null) { 921 globFilter = new GlobFilter(); 922 } 923 try { 924 globFilter.setPattern(selectedFile.getName()); 925 if (!(currentFilter instanceof GlobFilter)) { 926 actualFileFilter = currentFilter; 927 } 928 chooser.setFileFilter(null); 929 chooser.setFileFilter(globFilter); 930 return; 931 } catch (PatternSyntaxException pse) { 932 // Not a valid glob pattern. Abandon filter. 933 } 934 } 935 936 resetGlobFilter(); 937 938 // Check for directory change action 939 boolean isDir = (selectedFile != null && selectedFile.isDirectory()); 940 boolean isTrav = (selectedFile != null && chooser.isTraversable(selectedFile)); 941 boolean isDirSelEnabled = chooser.isDirectorySelectionEnabled(); 942 boolean isFileSelEnabled = chooser.isFileSelectionEnabled(); 943 boolean isCtrl = (e != null && (e.getModifiers() & 944 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0); 945 946 if (isDir && isTrav && (isCtrl || !isDirSelEnabled)) { 947 changeDirectory(selectedFile); 948 return; 949 } else if ((isDir || !isFileSelEnabled) 950 && (!isDir || !isDirSelEnabled) 951 && (!isDirSelEnabled || selectedFile.exists())) { 952 selectedFile = null; 953 } 954 } 955 956 if (selectedFiles != null || selectedFile != null) { 957 if (selectedFiles != null || chooser.isMultiSelectionEnabled()) { 958 if (selectedFiles == null) { 959 selectedFiles = new File[] { selectedFile }; 960 } 961 chooser.setSelectedFiles(selectedFiles); 962 // Do it again. This is a fix for bug 4949273 to force the 963 // selected value in case the ListSelectionModel clears it 964 // for non-existing file names. 965 chooser.setSelectedFiles(selectedFiles); 966 } else { 967 chooser.setSelectedFile(selectedFile); 968 } 969 chooser.approveSelection(); 970 } else { 971 if (chooser.isMultiSelectionEnabled()) { 972 chooser.setSelectedFiles(null); 973 } else { 974 chooser.setSelectedFile(null); 975 } 976 chooser.cancelSelection(); 977 } 978 } 979 } 980 981 982 private void resetGlobFilter() { 983 if (actualFileFilter != null) { 984 JFileChooser chooser = getFileChooser(); 985 FileFilter currentFilter = chooser.getFileFilter(); 986 if (currentFilter != null && currentFilter.equals(globFilter)) { 987 chooser.setFileFilter(actualFileFilter); 988 chooser.removeChoosableFileFilter(globFilter); 989 } 990 actualFileFilter = null; 991 } 992 } 993 994 private static boolean isGlobPattern(String filename) { 995 return ((File.separatorChar == '\\' && (filename.indexOf('*') >= 0 996 || filename.indexOf('?') >= 0)) 997 || (File.separatorChar == '/' && (filename.indexOf('*') >= 0 998 || filename.indexOf('?') >= 0 999 || filename.indexOf('[') >= 0))); 1000 } 1001 1002 1003 /* A file filter which accepts file patterns containing 1004 * the special wildcards *? on Windows and *?[] on Unix. 1005 */ 1006 class GlobFilter extends FileFilter { 1007 Pattern pattern; 1008 String globPattern; 1009 1010 public void setPattern(String globPattern) { 1011 char[] gPat = globPattern.toCharArray(); 1012 char[] rPat = new char[gPat.length * 2]; 1013 boolean isWin32 = (File.separatorChar == '\\'); 1014 boolean inBrackets = false; 1015 int j = 0; 1016 1017 this.globPattern = globPattern; 1018 1019 if (isWin32) { 1020 // On windows, a pattern ending with *.* is equal to ending with * 1021 int len = gPat.length; 1022 if (globPattern.endsWith("*.*")) { 1023 len -= 2; 1024 } 1025 for (int i = 0; i < len; i++) { 1026 switch(gPat[i]) { 1027 case '*': 1028 rPat[j++] = '.'; 1029 rPat[j++] = '*'; 1030 break; 1031 1032 case '?': 1033 rPat[j++] = '.'; 1034 break; 1035 1036 case '\\': 1037 rPat[j++] = '\\'; 1038 rPat[j++] = '\\'; 1039 break; 1040 1041 default: 1042 if ("+()^$.{}[]".indexOf(gPat[i]) >= 0) { 1043 rPat[j++] = '\\'; 1044 } 1045 rPat[j++] = gPat[i]; 1046 break; 1047 } 1048 } 1049 } else { 1050 for (int i = 0; i < gPat.length; i++) { 1051 switch(gPat[i]) { 1052 case '*': 1053 if (!inBrackets) { 1054 rPat[j++] = '.'; 1055 } 1056 rPat[j++] = '*'; 1057 break; 1058 1059 case '?': 1060 rPat[j++] = inBrackets ? '?' : '.'; 1061 break; 1062 1063 case '[': 1064 inBrackets = true; 1065 rPat[j++] = gPat[i]; 1066 1067 if (i < gPat.length - 1) { 1068 switch (gPat[i+1]) { 1069 case '!': 1070 case '^': 1071 rPat[j++] = '^'; 1072 i++; 1073 break; 1074 1075 case ']': 1076 rPat[j++] = gPat[++i]; 1077 break; 1078 } 1079 } 1080 break; 1081 1082 case ']': 1083 rPat[j++] = gPat[i]; 1084 inBrackets = false; 1085 break; 1086 1087 case '\\': 1088 if (i == 0 && gPat.length > 1 && gPat[1] == '~') { 1089 rPat[j++] = gPat[++i]; 1090 } else { 1091 rPat[j++] = '\\'; 1092 if (i < gPat.length - 1 && "*?[]".indexOf(gPat[i+1]) >= 0) { 1093 rPat[j++] = gPat[++i]; 1094 } else { 1095 rPat[j++] = '\\'; 1096 } 1097 } 1098 break; 1099 1100 default: 1101 //if ("+()|^$.{}<>".indexOf(gPat[i]) >= 0) { 1102 if (!Character.isLetterOrDigit(gPat[i])) { 1103 rPat[j++] = '\\'; 1104 } 1105 rPat[j++] = gPat[i]; 1106 break; 1107 } 1108 } 1109 } 1110 this.pattern = Pattern.compile(new String(rPat, 0, j), Pattern.CASE_INSENSITIVE); 1111 } 1112 1113 public boolean accept(File f) { 1114 if (f == null) { 1115 return false; 1116 } 1117 if (f.isDirectory()) { 1118 return true; 1119 } 1120 return pattern.matcher(f.getName()).matches(); 1121 } 1122 1123 public String getDescription() { 1124 return globPattern; 1125 } 1126 } 1127 1128 /** 1129 * Responds to a cancel request. 1130 */ 1131 protected class CancelSelectionAction extends AbstractAction { 1132 public void actionPerformed(ActionEvent e) { 1133 getFileChooser().cancelSelection(); 1134 } 1135 } 1136 1137 /** 1138 * Rescans the files in the current directory 1139 */ 1140 protected class UpdateAction extends AbstractAction { 1141 public void actionPerformed(ActionEvent e) { 1142 JFileChooser fc = getFileChooser(); 1143 fc.setCurrentDirectory(fc.getFileSystemView().createFileObject(getDirectoryName())); 1144 fc.rescanCurrentDirectory(); 1145 } 1146 } 1147 1148 1149 private void changeDirectory(File dir) { 1150 JFileChooser fc = getFileChooser(); 1151 // Traverse shortcuts on Windows 1152 if (dir != null && FilePane.usesShellFolder(fc)) { 1153 try { 1154 ShellFolder shellFolder = ShellFolder.getShellFolder(dir); 1155 1156 if (shellFolder.isLink()) { 1157 File linkedTo = shellFolder.getLinkLocation(); 1158 1159 if (linkedTo != null && fc.isTraversable(linkedTo)) { 1160 dir = linkedTo; 1161 } else { 1162 return; 1163 } 1164 } 1165 } catch (FileNotFoundException ex) { 1166 return; 1167 } 1168 } 1169 fc.setCurrentDirectory(dir); 1170 if (fc.getFileSelectionMode() == JFileChooser.FILES_AND_DIRECTORIES && 1171 fc.getFileSystemView().isFileSystem(dir)) { 1172 1173 setFileName(dir.getAbsolutePath()); 1174 } 1175 } 1176 1177 1178 // ***************************************** 1179 // ***** default AcceptAll file filter ***** 1180 // ***************************************** 1181 protected class AcceptAllFileFilter extends FileFilter { 1182 1183 public AcceptAllFileFilter() { 1184 } 1185 1186 public boolean accept(File f) { 1187 return true; 1188 } 1189 1190 public String getDescription() { 1191 return UIManager.getString("FileChooser.acceptAllFileFilterText"); 1192 } 1193 } 1194 1195 1196 // *********************** 1197 // * FileView operations * 1198 // *********************** 1199 protected class BasicFileView extends FileView { 1200 /* FileView type descriptions */ 1201 // PENDING(jeff) - pass in the icon cache size 1202 protected Hashtable<File,Icon> iconCache = new Hashtable<File,Icon>(); 1203 1204 public BasicFileView() { 1205 } 1206 1207 public void clearIconCache() { 1208 iconCache = new Hashtable<File,Icon>(); 1209 } 1210 1211 public String getName(File f) { 1212 // Note: Returns display name rather than file name 1213 String fileName = null; 1214 if(f != null) { 1215 fileName = getFileChooser().getFileSystemView().getSystemDisplayName(f); 1216 } 1217 return fileName; 1218 } 1219 1220 1221 public String getDescription(File f) { 1222 return f.getName(); 1223 } 1224 1225 public String getTypeDescription(File f) { 1226 String type = getFileChooser().getFileSystemView().getSystemTypeDescription(f); 1227 if (type == null) { 1228 if (f.isDirectory()) { 1229 type = directoryDescriptionText; 1230 } else { 1231 type = fileDescriptionText; 1232 } 1233 } 1234 return type; 1235 } 1236 1237 public Icon getCachedIcon(File f) { 1238 return iconCache.get(f); 1239 } 1240 1241 public void cacheIcon(File f, Icon i) { 1242 if(f == null || i == null) { 1243 return; 1244 } 1245 iconCache.put(f, i); 1246 } 1247 1248 public Icon getIcon(File f) { 1249 Icon icon = getCachedIcon(f); 1250 if(icon != null) { 1251 return icon; 1252 } 1253 icon = fileIcon; 1254 if (f != null) { 1255 FileSystemView fsv = getFileChooser().getFileSystemView(); 1256 1257 if (fsv.isFloppyDrive(f)) { 1258 icon = floppyDriveIcon; 1259 } else if (fsv.isDrive(f)) { 1260 icon = hardDriveIcon; 1261 } else if (fsv.isComputerNode(f)) { 1262 icon = computerIcon; 1263 } else if (f.isDirectory()) { 1264 icon = directoryIcon; 1265 } 1266 } 1267 cacheIcon(f, icon); 1268 return icon; 1269 } 1270 1271 public Boolean isHidden(File f) { 1272 String name = f.getName(); 1273 if(name != null && name.charAt(0) == '.') { 1274 return Boolean.TRUE; 1275 } else { 1276 return Boolean.FALSE; 1277 } 1278 } 1279 } 1280 1281 private static final TransferHandler defaultTransferHandler = new FileTransferHandler(); 1282 1283 /** 1284 * Data transfer support for the file chooser. Since files are currently presented 1285 * as a list, the list support is reused with the added flavor of DataFlavor.javaFileListFlavor 1286 */ 1287 static class FileTransferHandler extends TransferHandler implements UIResource { 1288 1289 /** 1290 * Create a Transferable to use as the source for a data transfer. 1291 * 1292 * @param c The component holding the data to be transfered. This 1293 * argument is provided to enable sharing of TransferHandlers by 1294 * multiple components. 1295 * @return The representation of the data to be transfered. 1296 * 1297 */ 1298 protected Transferable createTransferable(JComponent c) { 1299 Object[] values = null; 1300 if (c instanceof JList) { 1301 values = ((JList)c).getSelectedValues(); 1302 } else if (c instanceof JTable) { 1303 JTable table = (JTable)c; 1304 int[] rows = table.getSelectedRows(); 1305 if (rows != null) { 1306 values = new Object[rows.length]; 1307 for (int i=0; i<rows.length; i++) { 1308 values[i] = table.getValueAt(rows[i], 0); 1309 } 1310 } 1311 } 1312 if (values == null || values.length == 0) { 1313 return null; 1314 } 1315 1316 StringBuffer plainBuf = new StringBuffer(); 1317 StringBuffer htmlBuf = new StringBuffer(); 1318 1319 htmlBuf.append("<html>\n<body>\n<ul>\n"); 1320 1321 for (Object obj : values) { 1322 String val = ((obj == null) ? "" : obj.toString()); 1323 plainBuf.append(val + "\n"); 1324 htmlBuf.append(" <li>" + val + "\n"); 1325 } 1326 1327 // remove the last newline 1328 plainBuf.deleteCharAt(plainBuf.length() - 1); 1329 htmlBuf.append("</ul>\n</body>\n</html>"); 1330 1331 return new FileTransferable(plainBuf.toString(), htmlBuf.toString(), values); 1332 } 1333 1334 public int getSourceActions(JComponent c) { 1335 return COPY; 1336 } 1337 1338 static class FileTransferable extends BasicTransferable { 1339 1340 Object[] fileData; 1341 1342 FileTransferable(String plainData, String htmlData, Object[] fileData) { 1343 super(plainData, htmlData); 1344 this.fileData = fileData; 1345 } 1346 1347 /** 1348 * Best format of the file chooser is DataFlavor.javaFileListFlavor. 1349 */ 1350 protected DataFlavor[] getRicherFlavors() { 1351 DataFlavor[] flavors = new DataFlavor[1]; 1352 flavors[0] = DataFlavor.javaFileListFlavor; 1353 return flavors; 1354 } 1355 1356 /** 1357 * The only richer format supported is the file list flavor 1358 */ 1359 protected Object getRicherData(DataFlavor flavor) { 1360 if (DataFlavor.javaFileListFlavor.equals(flavor)) { 1361 ArrayList<Object> files = new ArrayList<Object>(); 1362 for (Object file : this.fileData) { 1363 files.add(file); 1364 } 1365 return files; 1366 } 1367 return null; 1368 } 1369 1370 } 1371 } 1372 }