1 /* 2 * Copyright (c) 1998, 2010, 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 // compatibility 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 compatibility. 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 compatibility. 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 getFileChooser().changeToParentDirectory(); 814 } 815 } 816 817 /** 818 * Responds to an Open or Save request 819 */ 820 protected class ApproveSelectionAction extends AbstractAction { 821 protected ApproveSelectionAction() { 822 super(FilePane.ACTION_APPROVE_SELECTION); 823 } 824 public void actionPerformed(ActionEvent e) { 825 if (isDirectorySelected()) { 826 File dir = getDirectory(); 827 if (dir != null) { 828 try { 829 // Strip trailing ".." 830 dir = ShellFolder.getNormalizedFile(dir); 831 } catch (IOException ex) { 832 // Ok, use f as is 833 } 834 changeDirectory(dir); 835 return; 836 } 837 } 838 839 JFileChooser chooser = getFileChooser(); 840 841 String filename = getFileName(); 842 FileSystemView fs = chooser.getFileSystemView(); 843 File dir = chooser.getCurrentDirectory(); 844 845 if (filename != null) { 846 // Remove whitespaces from end of filename 847 int i = filename.length() - 1; 848 849 while (i >=0 && filename.charAt(i) <= ' ') { 850 i--; 851 } 852 853 filename = filename.substring(0, i + 1); 854 } 855 856 if (filename == null || filename.length() == 0) { 857 // no file selected, multiple selection off, therefore cancel the approve action 858 resetGlobFilter(); 859 return; 860 } 861 862 File selectedFile = null; 863 File[] selectedFiles = null; 864 865 // Unix: Resolve '~' to user's home directory 866 if (File.separatorChar == '/') { 867 if (filename.startsWith("~/")) { 868 filename = System.getProperty("user.home") + filename.substring(1); 869 } else if (filename.equals("~")) { 870 filename = System.getProperty("user.home"); 871 } 872 } 873 874 if (chooser.isMultiSelectionEnabled() && filename.length() > 1 && 875 filename.charAt(0) == '"' && filename.charAt(filename.length() - 1) == '"') { 876 List<File> fList = new ArrayList<File>(); 877 878 String[] files = filename.substring(1, filename.length() - 1).split("\" \""); 879 // Optimize searching files by names in "children" array 880 Arrays.sort(files); 881 882 File[] children = null; 883 int childIndex = 0; 884 885 for (String str : files) { 886 File file = fs.createFileObject(str); 887 if (!file.isAbsolute()) { 888 if (children == null) { 889 children = fs.getFiles(dir, false); 890 Arrays.sort(children); 891 } 892 for (int k = 0; k < children.length; k++) { 893 int l = (childIndex + k) % children.length; 894 if (children[l].getName().equals(str)) { 895 file = children[l]; 896 childIndex = l + 1; 897 break; 898 } 899 } 900 } 901 fList.add(file); 902 } 903 904 if (!fList.isEmpty()) { 905 selectedFiles = fList.toArray(new File[fList.size()]); 906 } 907 resetGlobFilter(); 908 } else { 909 selectedFile = fs.createFileObject(filename); 910 if (!selectedFile.isAbsolute()) { 911 selectedFile = fs.getChild(dir, filename); 912 } 913 // check for wildcard pattern 914 FileFilter currentFilter = chooser.getFileFilter(); 915 if (!selectedFile.exists() && isGlobPattern(filename)) { 916 changeDirectory(selectedFile.getParentFile()); 917 if (globFilter == null) { 918 globFilter = new GlobFilter(); 919 } 920 try { 921 globFilter.setPattern(selectedFile.getName()); 922 if (!(currentFilter instanceof GlobFilter)) { 923 actualFileFilter = currentFilter; 924 } 925 chooser.setFileFilter(null); 926 chooser.setFileFilter(globFilter); 927 return; 928 } catch (PatternSyntaxException pse) { 929 // Not a valid glob pattern. Abandon filter. 930 } 931 } 932 933 resetGlobFilter(); 934 935 // Check for directory change action 936 boolean isDir = (selectedFile != null && selectedFile.isDirectory()); 937 boolean isTrav = (selectedFile != null && chooser.isTraversable(selectedFile)); 938 boolean isDirSelEnabled = chooser.isDirectorySelectionEnabled(); 939 boolean isFileSelEnabled = chooser.isFileSelectionEnabled(); 940 boolean isCtrl = (e != null && (e.getModifiers() & 941 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0); 942 943 if (isDir && isTrav && (isCtrl || !isDirSelEnabled)) { 944 changeDirectory(selectedFile); 945 return; 946 } else if ((isDir || !isFileSelEnabled) 947 && (!isDir || !isDirSelEnabled) 948 && (!isDirSelEnabled || selectedFile.exists())) { 949 selectedFile = null; 950 } 951 } 952 953 if (selectedFiles != null || selectedFile != null) { 954 if (selectedFiles != null || chooser.isMultiSelectionEnabled()) { 955 if (selectedFiles == null) { 956 selectedFiles = new File[] { selectedFile }; 957 } 958 chooser.setSelectedFiles(selectedFiles); 959 // Do it again. This is a fix for bug 4949273 to force the 960 // selected value in case the ListSelectionModel clears it 961 // for non-existing file names. 962 chooser.setSelectedFiles(selectedFiles); 963 } else { 964 chooser.setSelectedFile(selectedFile); 965 } 966 chooser.approveSelection(); 967 } else { 968 if (chooser.isMultiSelectionEnabled()) { 969 chooser.setSelectedFiles(null); 970 } else { 971 chooser.setSelectedFile(null); 972 } 973 chooser.cancelSelection(); 974 } 975 } 976 } 977 978 979 private void resetGlobFilter() { 980 if (actualFileFilter != null) { 981 JFileChooser chooser = getFileChooser(); 982 FileFilter currentFilter = chooser.getFileFilter(); 983 if (currentFilter != null && currentFilter.equals(globFilter)) { 984 chooser.setFileFilter(actualFileFilter); 985 chooser.removeChoosableFileFilter(globFilter); 986 } 987 actualFileFilter = null; 988 } 989 } 990 991 private static boolean isGlobPattern(String filename) { 992 return ((File.separatorChar == '\\' && (filename.indexOf('*') >= 0 993 || filename.indexOf('?') >= 0)) 994 || (File.separatorChar == '/' && (filename.indexOf('*') >= 0 995 || filename.indexOf('?') >= 0 996 || filename.indexOf('[') >= 0))); 997 } 998 999 1000 /* A file filter which accepts file patterns containing 1001 * the special wildcards *? on Windows and *?[] on Unix. 1002 */ 1003 class GlobFilter extends FileFilter { 1004 Pattern pattern; 1005 String globPattern; 1006 1007 public void setPattern(String globPattern) { 1008 char[] gPat = globPattern.toCharArray(); 1009 char[] rPat = new char[gPat.length * 2]; 1010 boolean isWin32 = (File.separatorChar == '\\'); 1011 boolean inBrackets = false; 1012 int j = 0; 1013 1014 this.globPattern = globPattern; 1015 1016 if (isWin32) { 1017 // On windows, a pattern ending with *.* is equal to ending with * 1018 int len = gPat.length; 1019 if (globPattern.endsWith("*.*")) { 1020 len -= 2; 1021 } 1022 for (int i = 0; i < len; i++) { 1023 switch(gPat[i]) { 1024 case '*': 1025 rPat[j++] = '.'; 1026 rPat[j++] = '*'; 1027 break; 1028 1029 case '?': 1030 rPat[j++] = '.'; 1031 break; 1032 1033 case '\\': 1034 rPat[j++] = '\\'; 1035 rPat[j++] = '\\'; 1036 break; 1037 1038 default: 1039 if ("+()^$.{}[]".indexOf(gPat[i]) >= 0) { 1040 rPat[j++] = '\\'; 1041 } 1042 rPat[j++] = gPat[i]; 1043 break; 1044 } 1045 } 1046 } else { 1047 for (int i = 0; i < gPat.length; i++) { 1048 switch(gPat[i]) { 1049 case '*': 1050 if (!inBrackets) { 1051 rPat[j++] = '.'; 1052 } 1053 rPat[j++] = '*'; 1054 break; 1055 1056 case '?': 1057 rPat[j++] = inBrackets ? '?' : '.'; 1058 break; 1059 1060 case '[': 1061 inBrackets = true; 1062 rPat[j++] = gPat[i]; 1063 1064 if (i < gPat.length - 1) { 1065 switch (gPat[i+1]) { 1066 case '!': 1067 case '^': 1068 rPat[j++] = '^'; 1069 i++; 1070 break; 1071 1072 case ']': 1073 rPat[j++] = gPat[++i]; 1074 break; 1075 } 1076 } 1077 break; 1078 1079 case ']': 1080 rPat[j++] = gPat[i]; 1081 inBrackets = false; 1082 break; 1083 1084 case '\\': 1085 if (i == 0 && gPat.length > 1 && gPat[1] == '~') { 1086 rPat[j++] = gPat[++i]; 1087 } else { 1088 rPat[j++] = '\\'; 1089 if (i < gPat.length - 1 && "*?[]".indexOf(gPat[i+1]) >= 0) { 1090 rPat[j++] = gPat[++i]; 1091 } else { 1092 rPat[j++] = '\\'; 1093 } 1094 } 1095 break; 1096 1097 default: 1098 //if ("+()|^$.{}<>".indexOf(gPat[i]) >= 0) { 1099 if (!Character.isLetterOrDigit(gPat[i])) { 1100 rPat[j++] = '\\'; 1101 } 1102 rPat[j++] = gPat[i]; 1103 break; 1104 } 1105 } 1106 } 1107 this.pattern = Pattern.compile(new String(rPat, 0, j), Pattern.CASE_INSENSITIVE); 1108 } 1109 1110 public boolean accept(File f) { 1111 if (f == null) { 1112 return false; 1113 } 1114 if (f.isDirectory()) { 1115 return true; 1116 } 1117 return pattern.matcher(f.getName()).matches(); 1118 } 1119 1120 public String getDescription() { 1121 return globPattern; 1122 } 1123 } 1124 1125 /** 1126 * Responds to a cancel request. 1127 */ 1128 protected class CancelSelectionAction extends AbstractAction { 1129 public void actionPerformed(ActionEvent e) { 1130 getFileChooser().cancelSelection(); 1131 } 1132 } 1133 1134 /** 1135 * Rescans the files in the current directory 1136 */ 1137 protected class UpdateAction extends AbstractAction { 1138 public void actionPerformed(ActionEvent e) { 1139 JFileChooser fc = getFileChooser(); 1140 fc.setCurrentDirectory(fc.getFileSystemView().createFileObject(getDirectoryName())); 1141 fc.rescanCurrentDirectory(); 1142 } 1143 } 1144 1145 1146 private void changeDirectory(File dir) { 1147 JFileChooser fc = getFileChooser(); 1148 // Traverse shortcuts on Windows 1149 if (dir != null && FilePane.usesShellFolder(fc)) { 1150 try { 1151 ShellFolder shellFolder = ShellFolder.getShellFolder(dir); 1152 1153 if (shellFolder.isLink()) { 1154 File linkedTo = shellFolder.getLinkLocation(); 1155 1156 // If linkedTo is null we try to use dir 1157 if (linkedTo != null) { 1158 if (fc.isTraversable(linkedTo)) { 1159 dir = linkedTo; 1160 } else { 1161 return; 1162 } 1163 } else { 1164 dir = shellFolder; 1165 } 1166 } 1167 } catch (FileNotFoundException ex) { 1168 return; 1169 } 1170 } 1171 fc.setCurrentDirectory(dir); 1172 if (fc.getFileSelectionMode() == JFileChooser.FILES_AND_DIRECTORIES && 1173 fc.getFileSystemView().isFileSystem(dir)) { 1174 1175 setFileName(dir.getAbsolutePath()); 1176 } 1177 } 1178 1179 1180 // ***************************************** 1181 // ***** default AcceptAll file filter ***** 1182 // ***************************************** 1183 protected class AcceptAllFileFilter extends FileFilter { 1184 1185 public AcceptAllFileFilter() { 1186 } 1187 1188 public boolean accept(File f) { 1189 return true; 1190 } 1191 1192 public String getDescription() { 1193 return UIManager.getString("FileChooser.acceptAllFileFilterText"); 1194 } 1195 } 1196 1197 1198 // *********************** 1199 // * FileView operations * 1200 // *********************** 1201 protected class BasicFileView extends FileView { 1202 /* FileView type descriptions */ 1203 // PENDING(jeff) - pass in the icon cache size 1204 protected Hashtable<File,Icon> iconCache = new Hashtable<File,Icon>(); 1205 1206 public BasicFileView() { 1207 } 1208 1209 public void clearIconCache() { 1210 iconCache = new Hashtable<File,Icon>(); 1211 } 1212 1213 public String getName(File f) { 1214 // Note: Returns display name rather than file name 1215 String fileName = null; 1216 if(f != null) { 1217 fileName = getFileChooser().getFileSystemView().getSystemDisplayName(f); 1218 } 1219 return fileName; 1220 } 1221 1222 1223 public String getDescription(File f) { 1224 return f.getName(); 1225 } 1226 1227 public String getTypeDescription(File f) { 1228 String type = getFileChooser().getFileSystemView().getSystemTypeDescription(f); 1229 if (type == null) { 1230 if (f.isDirectory()) { 1231 type = directoryDescriptionText; 1232 } else { 1233 type = fileDescriptionText; 1234 } 1235 } 1236 return type; 1237 } 1238 1239 public Icon getCachedIcon(File f) { 1240 return iconCache.get(f); 1241 } 1242 1243 public void cacheIcon(File f, Icon i) { 1244 if(f == null || i == null) { 1245 return; 1246 } 1247 iconCache.put(f, i); 1248 } 1249 1250 public Icon getIcon(File f) { 1251 Icon icon = getCachedIcon(f); 1252 if(icon != null) { 1253 return icon; 1254 } 1255 icon = fileIcon; 1256 if (f != null) { 1257 FileSystemView fsv = getFileChooser().getFileSystemView(); 1258 1259 if (fsv.isFloppyDrive(f)) { 1260 icon = floppyDriveIcon; 1261 } else if (fsv.isDrive(f)) { 1262 icon = hardDriveIcon; 1263 } else if (fsv.isComputerNode(f)) { 1264 icon = computerIcon; 1265 } else if (f.isDirectory()) { 1266 icon = directoryIcon; 1267 } 1268 } 1269 cacheIcon(f, icon); 1270 return icon; 1271 } 1272 1273 public Boolean isHidden(File f) { 1274 String name = f.getName(); 1275 if(name != null && name.charAt(0) == '.') { 1276 return Boolean.TRUE; 1277 } else { 1278 return Boolean.FALSE; 1279 } 1280 } 1281 } 1282 1283 private static final TransferHandler defaultTransferHandler = new FileTransferHandler(); 1284 1285 /** 1286 * Data transfer support for the file chooser. Since files are currently presented 1287 * as a list, the list support is reused with the added flavor of DataFlavor.javaFileListFlavor 1288 */ 1289 static class FileTransferHandler extends TransferHandler implements UIResource { 1290 1291 /** 1292 * Create a Transferable to use as the source for a data transfer. 1293 * 1294 * @param c The component holding the data to be transfered. This 1295 * argument is provided to enable sharing of TransferHandlers by 1296 * multiple components. 1297 * @return The representation of the data to be transfered. 1298 * 1299 */ 1300 protected Transferable createTransferable(JComponent c) { 1301 Object[] values = null; 1302 if (c instanceof JList) { 1303 values = ((JList)c).getSelectedValues(); 1304 } else if (c instanceof JTable) { 1305 JTable table = (JTable)c; 1306 int[] rows = table.getSelectedRows(); 1307 if (rows != null) { 1308 values = new Object[rows.length]; 1309 for (int i=0; i<rows.length; i++) { 1310 values[i] = table.getValueAt(rows[i], 0); 1311 } 1312 } 1313 } 1314 if (values == null || values.length == 0) { 1315 return null; 1316 } 1317 1318 StringBuffer plainBuf = new StringBuffer(); 1319 StringBuffer htmlBuf = new StringBuffer(); 1320 1321 htmlBuf.append("<html>\n<body>\n<ul>\n"); 1322 1323 for (Object obj : values) { 1324 String val = ((obj == null) ? "" : obj.toString()); 1325 plainBuf.append(val + "\n"); 1326 htmlBuf.append(" <li>" + val + "\n"); 1327 } 1328 1329 // remove the last newline 1330 plainBuf.deleteCharAt(plainBuf.length() - 1); 1331 htmlBuf.append("</ul>\n</body>\n</html>"); 1332 1333 return new FileTransferable(plainBuf.toString(), htmlBuf.toString(), values); 1334 } 1335 1336 public int getSourceActions(JComponent c) { 1337 return COPY; 1338 } 1339 1340 static class FileTransferable extends BasicTransferable { 1341 1342 Object[] fileData; 1343 1344 FileTransferable(String plainData, String htmlData, Object[] fileData) { 1345 super(plainData, htmlData); 1346 this.fileData = fileData; 1347 } 1348 1349 /** 1350 * Best format of the file chooser is DataFlavor.javaFileListFlavor. 1351 */ 1352 protected DataFlavor[] getRicherFlavors() { 1353 DataFlavor[] flavors = new DataFlavor[1]; 1354 flavors[0] = DataFlavor.javaFileListFlavor; 1355 return flavors; 1356 } 1357 1358 /** 1359 * The only richer format supported is the file list flavor 1360 */ 1361 protected Object getRicherData(DataFlavor flavor) { 1362 if (DataFlavor.javaFileListFlavor.equals(flavor)) { 1363 ArrayList<Object> files = new ArrayList<Object>(); 1364 for (Object file : this.fileData) { 1365 files.add(file); 1366 } 1367 return files; 1368 } 1369 return null; 1370 } 1371 1372 } 1373 } 1374 }