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      */
createUI(JComponent c)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      */
BasicFileChooserUI(JFileChooser b)193     public BasicFileChooserUI(JFileChooser b) {
194     }
195 
196     /**
197      * Installs the UI.
198      * @param c the component
199      */
installUI(JComponent c)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      */
uninstallUI(JComponent c)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      */
installComponents(JFileChooser fc)237     public void installComponents(JFileChooser fc) {
238     }
239 
240     /**
241      * Uninstalls the components.
242      * @param fc the file chooser
243      */
uninstallComponents(JFileChooser fc)244     public void uninstallComponents(JFileChooser fc) {
245     }
246 
247     /**
248      * Installs the listeners.
249      * @param fc the file chooser
250      */
installListeners(JFileChooser fc)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 
getInputMap(int condition)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 
getActionMap()274     ActionMap getActionMap() {
275         return createActionMap();
276     }
277 
createActionMap()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      */
uninstallListeners(JFileChooser fc)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      */
installDefaults(JFileChooser fc)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      */
installIcons(JFileChooser fc)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      */
installStrings(JFileChooser fc)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      */
uninstallDefaults(JFileChooser fc)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      */
uninstallIcons(JFileChooser fc)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      */
uninstallStrings(JFileChooser fc)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      */
createModel()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      */
getModel()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      */
createPropertyChangeListener(JFileChooser fc)460     public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) {
461         return null;
462     }
463 
464     /**
465      * Returns the file name.
466      * @return the file name
467      */
getFileName()468     public String getFileName() {
469         return null;
470     }
471 
472     /**
473      * Returns the directory name.
474      * @return the directory name
475      */
getDirectoryName()476     public String getDirectoryName() {
477         return null;
478     }
479 
480     /**
481      * Sets the file name.
482      * @param filename the file name
483      */
setFileName(String filename)484     public void setFileName(String filename) {
485     }
486 
487     /**
488      * Sets the directory name.
489      * @param dirname the file name
490      */
setDirectoryName(String dirname)491     public void setDirectoryName(String dirname) {
492     }
493 
494     /**
495      * {@inheritDoc}
496      */
rescanCurrentDirectory(JFileChooser fc)497     public void rescanCurrentDirectory(JFileChooser fc) {
498     }
499 
500     /**
501      * {@inheritDoc}
502      */
ensureFileIsVisible(JFileChooser fc, File f)503     public void ensureFileIsVisible(JFileChooser fc, File f) {
504     }
505 
506     /**
507      * Returns the file chooser.
508      * @return the file chooser
509      */
getFileChooser()510     public JFileChooser getFileChooser() {
511         return filechooser;
512     }
513 
514     /**
515      * Returns the accessory panel.
516      * @return the accessory panel
517      */
getAccessoryPanel()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      */
getApproveButton(JFileChooser fc)527     protected JButton getApproveButton(JFileChooser fc) {
528         return null;
529     }
530 
531     /**
532      * {@inheritDoc}
533      */
getDefaultButton(JFileChooser fc)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      */
getApproveButtonToolTipText(JFileChooser fc)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      */
clearIconCache()560     public void clearIconCache() {
561         fileView.clearIconCache();
562     }
563 
564 
565     // ********************************************
566     // ************ Create Listeners **************
567     // ********************************************
568 
getHandler()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      */
createDoubleClickListener(JFileChooser fc, JList<?> list)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      */
createListSelectionListener(JFileChooser fc)592     public ListSelectionListener createListSelectionListener(JFileChooser fc) {
593         return getHandler();
594     }
595 
596     private class Handler implements MouseListener, ListSelectionListener {
597         JList<?> list;
598 
Handler()599         Handler() {
600         }
601 
Handler(JList<?> list)602         Handler(JList<?> list) {
603             this.list = list;
604         }
605 
mouseClicked(MouseEvent evt)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 
mouseEntered(MouseEvent evt)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 
mouseExited(MouseEvent evt)645         public void mouseExited(MouseEvent evt) {
646         }
647 
mousePressed(MouseEvent evt)648         public void mousePressed(MouseEvent evt) {
649         }
650 
mouseReleased(MouseEvent evt)651         public void mouseReleased(MouseEvent evt) {
652         }
653 
654         @SuppressWarnings("deprecation")
valueChanged(ListSelectionEvent evt)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          */
DoubleClickListener(JList<?> list)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          */
mouseEntered(MouseEvent e)742         public void mouseEntered(MouseEvent e) {
743             handler.mouseEntered(e);
744         }
745 
746         /** {@inheritDoc} */
mouseClicked(MouseEvent e)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} */
valueChanged(ListSelectionEvent e)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      */
isDirectorySelected()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      */
setDirectorySelected(boolean b)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      */
getDirectory()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      */
setDirectory(File f)806     protected void setDirectory(File f) {
807         directory = f;
808     }
809 
810     /**
811      * Returns the mnemonic for the given key.
812      */
getMnemonic(String key, Locale l)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      */
getAcceptAllFileFilter(JFileChooser fc)824     public FileFilter getAcceptAllFileFilter(JFileChooser fc) {
825         return acceptAllFileFilter;
826     }
827 
828 
getFileView(JFileChooser fc)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      */
getDialogTitle(JFileChooser fc)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      */
getApproveButtonMnemonic(JFileChooser fc)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} */
getApproveButtonText(JFileChooser fc)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      */
getNewFolderAction()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      */
getGoHomeAction()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      */
getChangeToParentDirectoryAction()917     public Action getChangeToParentDirectoryAction() {
918         return changeToParentDirectoryAction;
919     }
920 
921     /**
922      * Returns an approve selection action.
923      * @return an approve selection  action
924      */
getApproveSelectionAction()925     public Action getApproveSelectionAction() {
926         return approveSelectionAction;
927     }
928 
929     /**
930      * Returns a cancel selection action.
931      * @return a cancel selection action
932      */
getCancelSelectionAction()933     public Action getCancelSelectionAction() {
934         return cancelSelectionAction;
935     }
936 
937     /**
938      * Returns an update action.
939      * @return an update action
940      */
getUpdateAction()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}. */
NewFolderAction()952         protected NewFolderAction() {
953             super(FilePane.ACTION_NEW_FOLDER);
954         }
955         /** {@inheritDoc} */
actionPerformed(ActionEvent e)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}. */
GoHomeAction()997         protected GoHomeAction() {
998             super("Go Home");
999         }
actionPerformed(ActionEvent e)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}. */
ChangeToParentDirectoryAction()1012         protected ChangeToParentDirectoryAction() {
1013             super("Go Up");
1014             putValue(Action.ACTION_COMMAND_KEY, FilePane.ACTION_CHANGE_TO_PARENT_DIRECTORY);
1015         }
1016         /** {@inheritDoc} */
actionPerformed(ActionEvent e)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}. */
ApproveSelectionAction()1028         protected ApproveSelectionAction() {
1029             super(FilePane.ACTION_APPROVE_SELECTION);
1030         }
1031         /** {@inheritDoc} */
actionPerformed(ActionEvent e)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                 @SuppressWarnings("deprecation")
1138                 boolean isCtrl = (e != null && (e.getModifiers() &
1139                             Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0);
1140 
1141                 if (isDir && isTrav && (isCtrl || !isDirSelEnabled)) {
1142                     changeDirectory(selectedFile);
1143                     return;
1144                 } else if ((isDir || !isFileSelEnabled)
1145                         && (!isDir || !isDirSelEnabled)
1146                         && (!isDirSelEnabled || selectedFile.exists())) {
1147                     selectedFile = null;
1148                 }
1149             }
1150 
1151             if (selectedFiles != null || selectedFile != null) {
1152                 if (selectedFiles != null || chooser.isMultiSelectionEnabled()) {
1153                     if (selectedFiles == null) {
1154                         selectedFiles = new File[] { selectedFile };
1155                     }
1156                     chooser.setSelectedFiles(selectedFiles);
1157                     // Do it again. This is a fix for bug 4949273 to force the
1158                     // selected value in case the ListSelectionModel clears it
1159                     // for non-existing file names.
1160                     chooser.setSelectedFiles(selectedFiles);
1161                 } else {
1162                     chooser.setSelectedFile(selectedFile);
1163                 }
1164                 chooser.approveSelection();
1165             } else {
1166                 if (chooser.isMultiSelectionEnabled()) {
1167                     chooser.setSelectedFiles(null);
1168                 } else {
1169                     chooser.setSelectedFile(null);
1170                 }
1171                 chooser.cancelSelection();
1172             }
1173         }
1174     }
1175 
1176 
resetGlobFilter()1177     private void resetGlobFilter() {
1178         if (actualFileFilter != null) {
1179             JFileChooser chooser = getFileChooser();
1180             FileFilter currentFilter = chooser.getFileFilter();
1181             if (currentFilter != null && currentFilter.equals(globFilter)) {
1182                 chooser.setFileFilter(actualFileFilter);
1183                 chooser.removeChoosableFileFilter(globFilter);
1184             }
1185             actualFileFilter = null;
1186         }
1187     }
1188 
isGlobPattern(String filename)1189     private static boolean isGlobPattern(String filename) {
1190         return ((File.separatorChar == '\\' && (filename.indexOf('*') >= 0
1191                                                   || filename.indexOf('?') >= 0))
1192                 || (File.separatorChar == '/' && (filename.indexOf('*') >= 0
1193                                                   || filename.indexOf('?') >= 0
1194                                                   || filename.indexOf('[') >= 0)));
1195     }
1196 
1197 
1198     /* A file filter which accepts file patterns containing
1199      * the special wildcards *? on Windows and *?[] on Unix.
1200      */
1201     class GlobFilter extends FileFilter {
1202         Pattern pattern;
1203         String globPattern;
1204 
setPattern(String globPattern)1205         public void setPattern(String globPattern) {
1206             char[] gPat = globPattern.toCharArray();
1207             char[] rPat = new char[gPat.length * 2];
1208             boolean isWin32 = (File.separatorChar == '\\');
1209             boolean inBrackets = false;
1210             int j = 0;
1211 
1212             this.globPattern = globPattern;
1213 
1214             if (isWin32) {
1215                 // On windows, a pattern ending with *.* is equal to ending with *
1216                 int len = gPat.length;
1217                 if (globPattern.endsWith("*.*")) {
1218                     len -= 2;
1219                 }
1220                 for (int i = 0; i < len; i++) {
1221                     switch(gPat[i]) {
1222                       case '*':
1223                         rPat[j++] = '.';
1224                         rPat[j++] = '*';
1225                         break;
1226 
1227                       case '?':
1228                         rPat[j++] = '.';
1229                         break;
1230 
1231                       case '\\':
1232                         rPat[j++] = '\\';
1233                         rPat[j++] = '\\';
1234                         break;
1235 
1236                       default:
1237                         if ("+()^$.{}[]".indexOf(gPat[i]) >= 0) {
1238                             rPat[j++] = '\\';
1239                         }
1240                         rPat[j++] = gPat[i];
1241                         break;
1242                     }
1243                 }
1244             } else {
1245                 for (int i = 0; i < gPat.length; i++) {
1246                     switch(gPat[i]) {
1247                       case '*':
1248                         if (!inBrackets) {
1249                             rPat[j++] = '.';
1250                         }
1251                         rPat[j++] = '*';
1252                         break;
1253 
1254                       case '?':
1255                         rPat[j++] = inBrackets ? '?' : '.';
1256                         break;
1257 
1258                       case '[':
1259                         inBrackets = true;
1260                         rPat[j++] = gPat[i];
1261 
1262                         if (i < gPat.length - 1) {
1263                             switch (gPat[i+1]) {
1264                               case '!':
1265                               case '^':
1266                                 rPat[j++] = '^';
1267                                 i++;
1268                                 break;
1269 
1270                               case ']':
1271                                 rPat[j++] = gPat[++i];
1272                                 break;
1273                             }
1274                         }
1275                         break;
1276 
1277                       case ']':
1278                         rPat[j++] = gPat[i];
1279                         inBrackets = false;
1280                         break;
1281 
1282                       case '\\':
1283                         if (i == 0 && gPat.length > 1 && gPat[1] == '~') {
1284                             rPat[j++] = gPat[++i];
1285                         } else {
1286                             rPat[j++] = '\\';
1287                             if (i < gPat.length - 1 && "*?[]".indexOf(gPat[i+1]) >= 0) {
1288                                 rPat[j++] = gPat[++i];
1289                             } else {
1290                                 rPat[j++] = '\\';
1291                             }
1292                         }
1293                         break;
1294 
1295                       default:
1296                         //if ("+()|^$.{}<>".indexOf(gPat[i]) >= 0) {
1297                         if (!Character.isLetterOrDigit(gPat[i])) {
1298                             rPat[j++] = '\\';
1299                         }
1300                         rPat[j++] = gPat[i];
1301                         break;
1302                     }
1303                 }
1304             }
1305             this.pattern = Pattern.compile(new String(rPat, 0, j), Pattern.CASE_INSENSITIVE);
1306         }
1307 
accept(File f)1308         public boolean accept(File f) {
1309             if (f == null) {
1310                 return false;
1311             }
1312             if (f.isDirectory()) {
1313                 return true;
1314             }
1315             return pattern.matcher(f.getName()).matches();
1316         }
1317 
getDescription()1318         public String getDescription() {
1319             return globPattern;
1320         }
1321     }
1322 
1323     /**
1324      * Responds to a cancel request.
1325      */
1326     @SuppressWarnings("serial") // Superclass is not serializable across versions
1327     protected class CancelSelectionAction extends AbstractAction {
1328         /** {@inheritDoc} */
actionPerformed(ActionEvent e)1329         public void actionPerformed(ActionEvent e) {
1330             getFileChooser().cancelSelection();
1331         }
1332     }
1333 
1334     /**
1335      * Rescans the files in the current directory
1336      */
1337     @SuppressWarnings("serial") // Superclass is not serializable across versions
1338     protected class UpdateAction extends AbstractAction {
1339         /** {@inheritDoc} */
actionPerformed(ActionEvent e)1340         public void actionPerformed(ActionEvent e) {
1341             JFileChooser fc = getFileChooser();
1342             fc.setCurrentDirectory(fc.getFileSystemView().createFileObject(getDirectoryName()));
1343             fc.rescanCurrentDirectory();
1344         }
1345     }
1346 
1347 
changeDirectory(File dir)1348     private void changeDirectory(File dir) {
1349         JFileChooser fc = getFileChooser();
1350         // Traverse shortcuts on Windows
1351         if (dir != null && FilePane.usesShellFolder(fc)) {
1352             try {
1353                 ShellFolder shellFolder = ShellFolder.getShellFolder(dir);
1354 
1355                 if (shellFolder.isLink()) {
1356                     File linkedTo = shellFolder.getLinkLocation();
1357 
1358                     // If linkedTo is null we try to use dir
1359                     if (linkedTo != null) {
1360                         if (fc.isTraversable(linkedTo)) {
1361                             dir = linkedTo;
1362                         } else {
1363                             return;
1364                         }
1365                     } else {
1366                         dir = shellFolder;
1367                     }
1368                 }
1369             } catch (FileNotFoundException ex) {
1370                 return;
1371             }
1372         }
1373         fc.setCurrentDirectory(dir);
1374         if (fc.getFileSelectionMode() == JFileChooser.FILES_AND_DIRECTORIES &&
1375             fc.getFileSystemView().isFileSystem(dir)) {
1376 
1377             setFileName(dir.getAbsolutePath());
1378         }
1379     }
1380 
1381 
1382     // *****************************************
1383     // ***** default AcceptAll file filter *****
1384     // *****************************************
1385     /**
1386      * Accept all file filter.
1387      */
1388     protected class AcceptAllFileFilter extends FileFilter {
1389 
1390         /** Constructs an {@code AcceptAllFileFilter}. */
AcceptAllFileFilter()1391         public AcceptAllFileFilter() {
1392         }
1393 
1394         /**
1395          * Returns true.
1396          * @param f the file
1397          * @return true
1398          */
accept(File f)1399         public boolean accept(File f) {
1400             return true;
1401         }
1402 
1403         /**
1404          * {@inheritDoc}
1405          */
getDescription()1406         public String getDescription() {
1407             return UIManager.getString("FileChooser.acceptAllFileFilterText");
1408         }
1409     }
1410 
1411 
1412     // ***********************
1413     // * FileView operations *
1414     // ***********************
1415     /**
1416      * A basic file view.
1417      */
1418     protected class BasicFileView extends FileView {
1419         /* FileView type descriptions */
1420         /** The icon cache */
1421         protected Hashtable<File,Icon> iconCache = new Hashtable<File,Icon>();
1422 
1423         /** Constructs a {@code BasicFileView}. */
BasicFileView()1424         public BasicFileView() {
1425         }
1426 
1427         /**
1428          * Clears the icon cache.
1429          */
clearIconCache()1430         public void clearIconCache() {
1431             iconCache = new Hashtable<File,Icon>();
1432         }
1433 
1434         /** {@inheritDoc} */
getName(File f)1435         public String getName(File f) {
1436             // Note: Returns display name rather than file name
1437             String fileName = null;
1438             if(f != null) {
1439                 fileName = getFileChooser().getFileSystemView().getSystemDisplayName(f);
1440             }
1441             return fileName;
1442         }
1443 
1444         /** {@inheritDoc} */
getDescription(File f)1445         public String getDescription(File f) {
1446             return f.getName();
1447         }
1448 
1449         /** {@inheritDoc} */
getTypeDescription(File f)1450         public String getTypeDescription(File f) {
1451             String type = getFileChooser().getFileSystemView().getSystemTypeDescription(f);
1452             if (type == null) {
1453                 if (f.isDirectory()) {
1454                     type = directoryDescriptionText;
1455                 } else {
1456                     type = fileDescriptionText;
1457                 }
1458             }
1459             return type;
1460         }
1461 
1462         /**
1463          * Returns the cached icon for the file.
1464          * @param f the file
1465          * @return the cached icon for the file
1466          */
getCachedIcon(File f)1467         public Icon getCachedIcon(File f) {
1468             return iconCache.get(f);
1469         }
1470 
1471         /**
1472          * Caches an icon for a file.
1473          * @param f the file
1474          * @param i the icon
1475          */
cacheIcon(File f, Icon i)1476         public void cacheIcon(File f, Icon i) {
1477             if(f == null || i == null) {
1478                 return;
1479             }
1480             iconCache.put(f, i);
1481         }
1482 
1483         /** {@inheritDoc} */
getIcon(File f)1484         public Icon getIcon(File f) {
1485             Icon icon = getCachedIcon(f);
1486             if(icon != null) {
1487                 return icon;
1488             }
1489             icon = fileIcon;
1490             if (f != null) {
1491                 FileSystemView fsv = getFileChooser().getFileSystemView();
1492 
1493                 if (fsv.isFloppyDrive(f)) {
1494                     icon = floppyDriveIcon;
1495                 } else if (fsv.isDrive(f)) {
1496                     icon = hardDriveIcon;
1497                 } else if (fsv.isComputerNode(f)) {
1498                     icon = computerIcon;
1499                 } else if (f.isDirectory()) {
1500                     icon = directoryIcon;
1501                 }
1502             }
1503             cacheIcon(f, icon);
1504             return icon;
1505         }
1506 
1507         /**
1508          * Returns whether or not a file is hidden.
1509          * @param f the file
1510          * @return whether or not a file is hidden
1511          */
isHidden(File f)1512         public Boolean isHidden(File f) {
1513             String name = f.getName();
1514             if(name != null && name.charAt(0) == '.') {
1515                 return Boolean.TRUE;
1516             } else {
1517                 return Boolean.FALSE;
1518             }
1519         }
1520     }
1521 
1522     private static final TransferHandler defaultTransferHandler = new FileTransferHandler();
1523 
1524     /**
1525      * Data transfer support for the file chooser.  Since files are currently presented
1526      * as a list, the list support is reused with the added flavor of DataFlavor.javaFileListFlavor
1527      */
1528     @SuppressWarnings("serial") // JDK-implementation class
1529     static class FileTransferHandler extends TransferHandler implements UIResource {
1530 
1531         /**
1532          * Create a Transferable to use as the source for a data transfer.
1533          *
1534          * @param c  The component holding the data to be transfered.  This
1535          *  argument is provided to enable sharing of TransferHandlers by
1536          *  multiple components.
1537          * @return  The representation of the data to be transfered.
1538          *
1539          */
1540         @SuppressWarnings("deprecation")
createTransferable(JComponent c)1541         protected Transferable createTransferable(JComponent c) {
1542             Object[] values = null;
1543             if (c instanceof JList) {
1544                 values = ((JList)c).getSelectedValues();
1545             } else if (c instanceof JTable) {
1546                 JTable table = (JTable)c;
1547                 int[] rows = table.getSelectedRows();
1548                 if (rows != null) {
1549                     values = new Object[rows.length];
1550                     for (int i=0; i<rows.length; i++) {
1551                         values[i] = table.getValueAt(rows[i], 0);
1552                     }
1553                 }
1554             }
1555             if (values == null || values.length == 0) {
1556                 return null;
1557             }
1558 
1559             StringBuilder plainBuf = new StringBuilder();
1560             StringBuilder htmlBuf = new StringBuilder();
1561 
1562             htmlBuf.append("<html>\n<body>\n<ul>\n");
1563 
1564             for (Object obj : values) {
1565                 String val = ((obj == null) ? "" : obj.toString());
1566                 plainBuf.append(val).append('\n');
1567                 htmlBuf.append("  <li>").append(val).append('\n');
1568             }
1569 
1570             // remove the last newline
1571             plainBuf.deleteCharAt(plainBuf.length() - 1);
1572             htmlBuf.append("</ul>\n</body>\n</html>");
1573 
1574             return new FileTransferable(plainBuf.toString(), htmlBuf.toString(), values);
1575         }
1576 
getSourceActions(JComponent c)1577         public int getSourceActions(JComponent c) {
1578             return COPY;
1579         }
1580 
1581         static class FileTransferable extends BasicTransferable {
1582 
1583             Object[] fileData;
1584 
FileTransferable(String plainData, String htmlData, Object[] fileData)1585             FileTransferable(String plainData, String htmlData, Object[] fileData) {
1586                 super(plainData, htmlData);
1587                 this.fileData = fileData;
1588             }
1589 
1590             /**
1591              * Best format of the file chooser is DataFlavor.javaFileListFlavor.
1592              */
getRicherFlavors()1593             protected DataFlavor[] getRicherFlavors() {
1594                 DataFlavor[] flavors = new DataFlavor[1];
1595                 flavors[0] = DataFlavor.javaFileListFlavor;
1596                 return flavors;
1597             }
1598 
1599             /**
1600              * The only richer format supported is the file list flavor
1601              */
getRicherData(DataFlavor flavor)1602             protected Object getRicherData(DataFlavor flavor) {
1603                 if (DataFlavor.javaFileListFlavor.equals(flavor)) {
1604                     ArrayList<Object> files = new ArrayList<Object>();
1605                     for (Object file : this.fileData) {
1606                         files.add(file);
1607                     }
1608                     return files;
1609                 }
1610                 return null;
1611             }
1612 
1613         }
1614     }
1615 }
1616