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