1 /*
2  * Copyright (c) 2003, 2013, 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 package sun.swing;
26 
27 import java.awt.*;
28 import java.awt.event.*;
29 import java.beans.PropertyChangeEvent;
30 import java.beans.PropertyChangeListener;
31 import java.io.*;
32 import java.text.DateFormat;
33 import java.text.MessageFormat;
34 import java.util.*;
35 import java.util.List;
36 import java.util.concurrent.Callable;
37 
38 import javax.accessibility.AccessibleContext;
39 import javax.swing.*;
40 import javax.swing.border.*;
41 import javax.swing.event.*;
42 import javax.swing.filechooser.*;
43 import javax.swing.plaf.basic.*;
44 import javax.swing.table.*;
45 import javax.swing.text.*;
46 
47 import sun.awt.AWTAccessor;
48 import sun.awt.AWTAccessor.MouseEventAccessor;
49 import sun.awt.shell.*;
50 
51 /**
52  * <b>WARNING:</b> This class is an implementation detail and is only
53  * public so that it can be used by two packages. You should NOT consider
54  * this public API.
55  * <p>
56  * This component is intended to be used in a subclass of
57  * javax.swing.plaf.basic.BasicFileChooserUI. It realies heavily on the
58  * implementation of BasicFileChooserUI, and is intended to be API compatible
59  * with earlier implementations of MetalFileChooserUI and WindowsFileChooserUI.
60  *
61  * @author Leif Samuelsson
62  */
63 public class FilePane extends JPanel implements PropertyChangeListener {
64     // Constants for actions. These are used for the actions' ACTION_COMMAND_KEY
65     // and as keys in the action maps for FilePane and the corresponding UI classes
66 
67     public final static String ACTION_APPROVE_SELECTION = "approveSelection";
68     public final static String ACTION_CANCEL            = "cancelSelection";
69     public final static String ACTION_EDIT_FILE_NAME    = "editFileName";
70     public final static String ACTION_REFRESH           = "refresh";
71     public final static String ACTION_CHANGE_TO_PARENT_DIRECTORY = "Go Up";
72     public final static String ACTION_NEW_FOLDER        = "New Folder";
73     public final static String ACTION_VIEW_LIST         = "viewTypeList";
74     public final static String ACTION_VIEW_DETAILS      = "viewTypeDetails";
75 
76     private Action[] actions;
77 
78     // "enums" for setViewType()
79     public  static final int VIEWTYPE_LIST     = 0;
80     public  static final int VIEWTYPE_DETAILS  = 1;
81     private static final int VIEWTYPE_COUNT    = 2;
82 
83     private int viewType = -1;
84     private JPanel[] viewPanels = new JPanel[VIEWTYPE_COUNT];
85     private JPanel currentViewPanel;
86     private String[] viewTypeActionNames;
87 
88     private String filesListAccessibleName = null;
89     private String filesDetailsAccessibleName = null;
90 
91     private JPopupMenu contextMenu;
92     private JMenu viewMenu;
93 
94     private String viewMenuLabelText;
95     private String refreshActionLabelText;
96     private String newFolderActionLabelText;
97 
98     private String kiloByteString;
99     private String megaByteString;
100     private String gigaByteString;
101 
102     private String renameErrorTitleText;
103     private String renameErrorText;
104     private String renameErrorFileExistsText;
105 
106     private static final Cursor waitCursor =
107         Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
108 
109     private final KeyListener detailsKeyListener = new KeyAdapter() {
110         private final long timeFactor;
111 
112         private final StringBuilder typedString = new StringBuilder();
113 
114         private long lastTime = 1000L;
115 
116         {
117             Long l = (Long) UIManager.get("Table.timeFactor");
118             timeFactor = (l != null) ? l : 1000L;
119         }
120 
121         /**
122          * Moves the keyboard focus to the first element whose prefix matches
123          * the sequence of alphanumeric keys pressed by the user with delay
124          * less than value of <code>timeFactor</code>. Subsequent same key
125          * presses move the keyboard focus to the next object that starts with
126          * the same letter until another key is pressed, then it is treated
127          * as the prefix with appropriate number of the same letters followed
128          * by first typed another letter.
129          */
130         public void keyTyped(KeyEvent e) {
131             BasicDirectoryModel model = getModel();
132             int rowCount = model.getSize();
133 
134             if (detailsTable == null || rowCount == 0 ||
135                     e.isAltDown() || e.isControlDown() || e.isMetaDown()) {
136                 return;
137             }
138 
139             InputMap inputMap = detailsTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
140             KeyStroke key = KeyStroke.getKeyStrokeForEvent(e);
141 
142             if (inputMap != null && inputMap.get(key) != null) {
143                 return;
144             }
145 
146             int startIndex = detailsTable.getSelectionModel().getLeadSelectionIndex();
147 
148             if (startIndex < 0) {
149                 startIndex = 0;
150             }
151 
152             if (startIndex >= rowCount) {
153                 startIndex = rowCount - 1;
154             }
155 
156             char c = e.getKeyChar();
157 
158             long time = e.getWhen();
159 
160             if (time - lastTime < timeFactor) {
161                 if (typedString.length() == 1 && typedString.charAt(0) == c) {
162                     // Subsequent same key presses move the keyboard focus to the next
163                     // object that starts with the same letter.
164                     startIndex++;
165                 } else {
166                     typedString.append(c);
167                 }
168             } else {
169                 startIndex++;
170 
171                 typedString.setLength(0);
172                 typedString.append(c);
173             }
174 
175             lastTime = time;
176 
177             if (startIndex >= rowCount) {
178                 startIndex = 0;
179             }
180 
181             // Find next file
182             int index = getNextMatch(startIndex, rowCount - 1);
183 
184             if (index < 0 && startIndex > 0) { // wrap
185                 index = getNextMatch(0, startIndex - 1);
186             }
187 
188             if (index >= 0) {
189                 detailsTable.getSelectionModel().setSelectionInterval(index, index);
190 
191                 Rectangle cellRect = detailsTable.getCellRect(index,
192                         detailsTable.convertColumnIndexToView(COLUMN_FILENAME), false);
193                 detailsTable.scrollRectToVisible(cellRect);
194             }
195         }
196 
197         private int getNextMatch(int startIndex, int finishIndex) {
198             BasicDirectoryModel model = getModel();
199             JFileChooser fileChooser = getFileChooser();
200             DetailsTableRowSorter rowSorter = getRowSorter();
201 
202             String prefix = typedString.toString().toLowerCase();
203 
204             // Search element
205             for (int index = startIndex; index <= finishIndex; index++) {
206                 File file = (File) model.getElementAt(rowSorter.convertRowIndexToModel(index));
207 
208                 String fileName = fileChooser.getName(file).toLowerCase();
209 
210                 if (fileName.startsWith(prefix)) {
211                     return index;
212                 }
213             }
214 
215             return -1;
216         }
217     };
218 
219     private FocusListener editorFocusListener = new FocusAdapter() {
220         public void focusLost(FocusEvent e) {
221             if (! e.isTemporary()) {
222                 applyEdit();
223             }
224         }
225     };
226 
227     private static FocusListener repaintListener = new FocusListener() {
228         public void focusGained(FocusEvent fe) {
229             repaintSelection(fe.getSource());
230         }
231 
232         public void focusLost(FocusEvent fe) {
233             repaintSelection(fe.getSource());
234         }
235 
236         private void repaintSelection(Object source) {
237             if (source instanceof JList) {
238                 repaintListSelection((JList)source);
239             } else if (source instanceof JTable) {
240                 repaintTableSelection((JTable)source);
241             }
242         }
243 
244         private void repaintListSelection(JList list) {
245             int[] indices = list.getSelectedIndices();
246             for (int i : indices) {
247                 Rectangle bounds = list.getCellBounds(i, i);
248                 list.repaint(bounds);
249             }
250         }
251 
252         private void repaintTableSelection(JTable table) {
253             int minRow = table.getSelectionModel().getMinSelectionIndex();
254             int maxRow = table.getSelectionModel().getMaxSelectionIndex();
255             if (minRow == -1 || maxRow == -1) {
256                 return;
257             }
258 
259             int col0 = table.convertColumnIndexToView(COLUMN_FILENAME);
260 
261             Rectangle first = table.getCellRect(minRow, col0, false);
262             Rectangle last = table.getCellRect(maxRow, col0, false);
263             Rectangle dirty = first.union(last);
264             table.repaint(dirty);
265         }
266     };
267 
268     private boolean smallIconsView = false;
269     private Border  listViewBorder;
270     private Color   listViewBackground;
271     private boolean listViewWindowsStyle;
272     private boolean readOnly;
273     private boolean fullRowSelection = false;
274 
275     private ListSelectionModel listSelectionModel;
276     private JList list;
277     private JTable detailsTable;
278 
279     private static final int COLUMN_FILENAME = 0;
280 
281     // Provides a way to recognize a newly created folder, so it can
282     // be selected when it appears in the model.
283     private File newFolderFile;
284 
285     // Used for accessing methods in the corresponding UI class
286     private FileChooserUIAccessor fileChooserUIAccessor;
287     private DetailsTableModel detailsTableModel;
288     private DetailsTableRowSorter rowSorter;
289 
FilePane(FileChooserUIAccessor fileChooserUIAccessor)290     public FilePane(FileChooserUIAccessor fileChooserUIAccessor) {
291         super(new BorderLayout());
292 
293         this.fileChooserUIAccessor = fileChooserUIAccessor;
294 
295         installDefaults();
296         createActionMap();
297     }
298 
uninstallUI()299     public void uninstallUI() {
300         if (getModel() != null) {
301             getModel().removePropertyChangeListener(this);
302         }
303     }
304 
getFileChooser()305     protected JFileChooser getFileChooser() {
306         return fileChooserUIAccessor.getFileChooser();
307     }
308 
getModel()309     protected BasicDirectoryModel getModel() {
310         return fileChooserUIAccessor.getModel();
311     }
312 
getViewType()313     public int getViewType() {
314         return viewType;
315     }
316 
setViewType(int viewType)317     public void setViewType(int viewType) {
318         if (viewType == this.viewType) {
319             return;
320         }
321 
322         int oldValue = this.viewType;
323         this.viewType = viewType;
324 
325         JPanel createdViewPanel = null;
326         Component newFocusOwner = null;
327 
328         switch (viewType) {
329           case VIEWTYPE_LIST:
330             if (viewPanels[viewType] == null) {
331                 createdViewPanel = fileChooserUIAccessor.createList();
332                 if (createdViewPanel == null) {
333                     createdViewPanel = createList();
334                 }
335 
336                 list = (JList) findChildComponent(createdViewPanel, JList.class);
337                 if (listSelectionModel == null) {
338                     listSelectionModel = list.getSelectionModel();
339                     if (detailsTable != null) {
340                         detailsTable.setSelectionModel(listSelectionModel);
341                     }
342                 } else {
343                     list.setSelectionModel(listSelectionModel);
344                 }
345             }
346             list.setLayoutOrientation(JList.VERTICAL_WRAP);
347             newFocusOwner = list;
348             break;
349 
350           case VIEWTYPE_DETAILS:
351             if (viewPanels[viewType] == null) {
352                 createdViewPanel = fileChooserUIAccessor.createDetailsView();
353                 if (createdViewPanel == null) {
354                     createdViewPanel = createDetailsView();
355                 }
356 
357                 detailsTable = (JTable) findChildComponent(createdViewPanel, JTable.class);
358                 detailsTable.setRowHeight(Math.max(detailsTable.getFont().getSize() + 4, 16 + 1));
359                 if (listSelectionModel != null) {
360                     detailsTable.setSelectionModel(listSelectionModel);
361                 }
362             }
363             newFocusOwner = detailsTable;
364             break;
365         }
366 
367         if (createdViewPanel != null) {
368             viewPanels[viewType] = createdViewPanel;
369             recursivelySetInheritsPopupMenu(createdViewPanel, true);
370         }
371 
372         boolean isFocusOwner = false;
373 
374         if (currentViewPanel != null) {
375             Component owner = DefaultKeyboardFocusManager.
376                     getCurrentKeyboardFocusManager().getPermanentFocusOwner();
377 
378             isFocusOwner = owner == detailsTable || owner == list;
379 
380             remove(currentViewPanel);
381         }
382 
383         currentViewPanel = viewPanels[viewType];
384         add(currentViewPanel, BorderLayout.CENTER);
385 
386         if (isFocusOwner && newFocusOwner != null) {
387             newFocusOwner.requestFocusInWindow();
388         }
389 
390         revalidate();
391         repaint();
392         updateViewMenu();
393         firePropertyChange("viewType", oldValue, viewType);
394     }
395 
396     class ViewTypeAction extends AbstractAction {
397         private int viewType;
398 
ViewTypeAction(int viewType)399         ViewTypeAction(int viewType) {
400             super(viewTypeActionNames[viewType]);
401             this.viewType = viewType;
402 
403             String cmd;
404             switch (viewType) {
405                 case VIEWTYPE_LIST:    cmd = ACTION_VIEW_LIST;    break;
406                 case VIEWTYPE_DETAILS: cmd = ACTION_VIEW_DETAILS; break;
407                 default:               cmd = (String)getValue(Action.NAME);
408             }
409             putValue(Action.ACTION_COMMAND_KEY, cmd);
410         }
411 
actionPerformed(ActionEvent e)412         public void actionPerformed(ActionEvent e) {
413             setViewType(viewType);
414         }
415     }
416 
getViewTypeAction(int viewType)417     public Action getViewTypeAction(int viewType) {
418         return new ViewTypeAction(viewType);
419     }
420 
recursivelySetInheritsPopupMenu(Container container, boolean b)421     private static void recursivelySetInheritsPopupMenu(Container container, boolean b) {
422         if (container instanceof JComponent) {
423             ((JComponent)container).setInheritsPopupMenu(b);
424         }
425         int n = container.getComponentCount();
426         for (int i = 0; i < n; i++) {
427             recursivelySetInheritsPopupMenu((Container)container.getComponent(i), b);
428         }
429     }
430 
installDefaults()431     protected void installDefaults() {
432         Locale l = getFileChooser().getLocale();
433 
434         listViewBorder       = UIManager.getBorder("FileChooser.listViewBorder");
435         listViewBackground   = UIManager.getColor("FileChooser.listViewBackground");
436         listViewWindowsStyle = UIManager.getBoolean("FileChooser.listViewWindowsStyle");
437         readOnly             = UIManager.getBoolean("FileChooser.readOnly");
438 
439         // TODO: On windows, get the following localized strings from the OS
440 
441         viewMenuLabelText =
442                         UIManager.getString("FileChooser.viewMenuLabelText", l);
443         refreshActionLabelText =
444                         UIManager.getString("FileChooser.refreshActionLabelText", l);
445         newFolderActionLabelText =
446                         UIManager.getString("FileChooser.newFolderActionLabelText", l);
447 
448         viewTypeActionNames = new String[VIEWTYPE_COUNT];
449         viewTypeActionNames[VIEWTYPE_LIST] =
450                         UIManager.getString("FileChooser.listViewActionLabelText", l);
451         viewTypeActionNames[VIEWTYPE_DETAILS] =
452                         UIManager.getString("FileChooser.detailsViewActionLabelText", l);
453 
454         kiloByteString = UIManager.getString("FileChooser.fileSizeKiloBytes", l);
455         megaByteString = UIManager.getString("FileChooser.fileSizeMegaBytes", l);
456         gigaByteString = UIManager.getString("FileChooser.fileSizeGigaBytes", l);
457         fullRowSelection = UIManager.getBoolean("FileView.fullRowSelection");
458 
459         filesListAccessibleName = UIManager.getString("FileChooser.filesListAccessibleName", l);
460         filesDetailsAccessibleName = UIManager.getString("FileChooser.filesDetailsAccessibleName", l);
461 
462         renameErrorTitleText = UIManager.getString("FileChooser.renameErrorTitleText", l);
463         renameErrorText = UIManager.getString("FileChooser.renameErrorText", l);
464         renameErrorFileExistsText = UIManager.getString("FileChooser.renameErrorFileExistsText", l);
465     }
466 
467     /**
468      * Fetches the command list for the FilePane. These commands
469      * are useful for binding to events, such as in a keymap.
470      *
471      * @return the command list
472      */
getActions()473     public Action[] getActions() {
474         if (actions == null) {
475             class FilePaneAction extends AbstractAction {
476                 FilePaneAction(String name) {
477                     this(name, name);
478                 }
479 
480                 FilePaneAction(String name, String cmd) {
481                     super(name);
482                     putValue(Action.ACTION_COMMAND_KEY, cmd);
483                 }
484 
485                 public void actionPerformed(ActionEvent e) {
486                     String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);
487 
488                     if (cmd == ACTION_CANCEL) {
489                         if (editFile != null) {
490                            cancelEdit();
491                         } else {
492                            getFileChooser().cancelSelection();
493                         }
494                     } else if (cmd == ACTION_EDIT_FILE_NAME) {
495                         JFileChooser fc = getFileChooser();
496                         int index = listSelectionModel.getMinSelectionIndex();
497                         if (index >= 0 && editFile == null &&
498                             (!fc.isMultiSelectionEnabled() ||
499                              fc.getSelectedFiles().length <= 1)) {
500 
501                             editFileName(index);
502                         }
503                     } else if (cmd == ACTION_REFRESH) {
504                         getFileChooser().rescanCurrentDirectory();
505                     }
506                 }
507 
508                 public boolean isEnabled() {
509                     String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);
510                     if (cmd == ACTION_CANCEL) {
511                         return getFileChooser().isEnabled();
512                     } else if (cmd == ACTION_EDIT_FILE_NAME) {
513                         return !readOnly && getFileChooser().isEnabled();
514                     } else {
515                         return true;
516                     }
517                 }
518             }
519 
520             ArrayList<Action> actionList = new ArrayList<Action>(8);
521             Action action;
522 
523             actionList.add(new FilePaneAction(ACTION_CANCEL));
524             actionList.add(new FilePaneAction(ACTION_EDIT_FILE_NAME));
525             actionList.add(new FilePaneAction(refreshActionLabelText, ACTION_REFRESH));
526 
527             action = fileChooserUIAccessor.getApproveSelectionAction();
528             if (action != null) {
529                 actionList.add(action);
530             }
531             action = fileChooserUIAccessor.getChangeToParentDirectoryAction();
532             if (action != null) {
533                 actionList.add(action);
534             }
535             action = getNewFolderAction();
536             if (action != null) {
537                 actionList.add(action);
538             }
539             action = getViewTypeAction(VIEWTYPE_LIST);
540             if (action != null) {
541                 actionList.add(action);
542             }
543             action = getViewTypeAction(VIEWTYPE_DETAILS);
544             if (action != null) {
545                 actionList.add(action);
546             }
547             actions = actionList.toArray(new Action[actionList.size()]);
548         }
549 
550         return actions;
551     }
552 
createActionMap()553     protected void createActionMap() {
554         addActionsToMap(super.getActionMap(), getActions());
555     }
556 
557 
addActionsToMap(ActionMap map, Action[] actions)558     public static void addActionsToMap(ActionMap map, Action[] actions) {
559         if (map != null && actions != null) {
560             for (Action a : actions) {
561                 String cmd = (String)a.getValue(Action.ACTION_COMMAND_KEY);
562                 if (cmd == null) {
563                     cmd = (String)a.getValue(Action.NAME);
564                 }
565                 map.put(cmd, a);
566             }
567         }
568     }
569 
570 
updateListRowCount(JList list)571     private void updateListRowCount(JList list) {
572         if (smallIconsView) {
573             list.setVisibleRowCount(getModel().getSize() / 3);
574         } else {
575             list.setVisibleRowCount(-1);
576         }
577     }
578 
createList()579     public JPanel createList() {
580         JPanel p = new JPanel(new BorderLayout());
581         final JFileChooser fileChooser = getFileChooser();
582         final JList<Object> list = new JList<Object>() {
583             public int getNextMatch(String prefix, int startIndex, Position.Bias bias) {
584                 ListModel model = getModel();
585                 int max = model.getSize();
586                 if (prefix == null || startIndex < 0 || startIndex >= max) {
587                     throw new IllegalArgumentException();
588                 }
589                 // start search from the next element before/after the selected element
590                 boolean backwards = (bias == Position.Bias.Backward);
591                 for (int i = startIndex; backwards ? i >= 0 : i < max; i += (backwards ?  -1 : 1)) {
592                     String filename = fileChooser.getName((File)model.getElementAt(i));
593                     if (filename.regionMatches(true, 0, prefix, 0, prefix.length())) {
594                         return i;
595                     }
596                 }
597                 return -1;
598             }
599         };
600         list.setCellRenderer(new FileRenderer());
601         list.setLayoutOrientation(JList.VERTICAL_WRAP);
602 
603         // 4835633 : tell BasicListUI that this is a file list
604         list.putClientProperty("List.isFileList", Boolean.TRUE);
605 
606         if (listViewWindowsStyle) {
607             list.addFocusListener(repaintListener);
608         }
609 
610         updateListRowCount(list);
611 
612         getModel().addListDataListener(new ListDataListener() {
613             public void intervalAdded(ListDataEvent e) {
614                 updateListRowCount(list);
615             }
616             public void intervalRemoved(ListDataEvent e) {
617                 updateListRowCount(list);
618             }
619             public void contentsChanged(ListDataEvent e) {
620                 if (isShowing()) {
621                     clearSelection();
622                 }
623                 updateListRowCount(list);
624             }
625         });
626 
627         getModel().addPropertyChangeListener(this);
628 
629         if (fileChooser.isMultiSelectionEnabled()) {
630             list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
631         } else {
632             list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
633         }
634         list.setModel(new SortableListModel());
635 
636         list.addListSelectionListener(createListSelectionListener());
637         list.addMouseListener(getMouseHandler());
638 
639         JScrollPane scrollpane = new JScrollPane(list);
640         if (listViewBackground != null) {
641             list.setBackground(listViewBackground);
642         }
643         if (listViewBorder != null) {
644             scrollpane.setBorder(listViewBorder);
645         }
646 
647         list.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesListAccessibleName);
648 
649         p.add(scrollpane, BorderLayout.CENTER);
650         return p;
651     }
652 
653     /**
654      * This model allows for sorting JList
655      */
656     private class SortableListModel extends AbstractListModel<Object>
657             implements TableModelListener, RowSorterListener {
658 
SortableListModel()659         public SortableListModel() {
660             getDetailsTableModel().addTableModelListener(this);
661             getRowSorter().addRowSorterListener(this);
662         }
663 
getSize()664         public int getSize() {
665             return getModel().getSize();
666         }
667 
getElementAt(int index)668         public Object getElementAt(int index) {
669             // JList doesn't support RowSorter so far, so we put it into the list model
670             return getModel().getElementAt(getRowSorter().convertRowIndexToModel(index));
671         }
672 
tableChanged(TableModelEvent e)673         public void tableChanged(TableModelEvent e) {
674             fireContentsChanged(this, 0, getSize());
675         }
676 
sorterChanged(RowSorterEvent e)677         public void sorterChanged(RowSorterEvent e) {
678             fireContentsChanged(this, 0, getSize());
679         }
680     }
681 
getDetailsTableModel()682     private DetailsTableModel getDetailsTableModel() {
683         if(detailsTableModel == null) {
684             detailsTableModel = new DetailsTableModel(getFileChooser());
685         }
686         return detailsTableModel;
687     }
688 
689     class DetailsTableModel extends AbstractTableModel implements ListDataListener {
690         JFileChooser chooser;
691         BasicDirectoryModel directoryModel;
692 
693         ShellFolderColumnInfo[] columns;
694         int[] columnMap;
695 
DetailsTableModel(JFileChooser fc)696         DetailsTableModel(JFileChooser fc) {
697             this.chooser = fc;
698             directoryModel = getModel();
699             directoryModel.addListDataListener(this);
700 
701             updateColumnInfo();
702         }
703 
updateColumnInfo()704         void updateColumnInfo() {
705             File dir = chooser.getCurrentDirectory();
706             if (dir != null && usesShellFolder(chooser)) {
707                 try {
708                     dir = ShellFolder.getShellFolder(dir);
709                 } catch (FileNotFoundException e) {
710                     // Leave dir without changing
711                 }
712             }
713 
714             ShellFolderColumnInfo[] allColumns = ShellFolder.getFolderColumns(dir);
715 
716             ArrayList<ShellFolderColumnInfo> visibleColumns =
717                     new ArrayList<ShellFolderColumnInfo>();
718             columnMap = new int[allColumns.length];
719             for (int i = 0; i < allColumns.length; i++) {
720                 ShellFolderColumnInfo column = allColumns[i];
721                 if (column.isVisible()) {
722                     columnMap[visibleColumns.size()] = i;
723                     visibleColumns.add(column);
724                 }
725             }
726 
727             columns = new ShellFolderColumnInfo[visibleColumns.size()];
728             visibleColumns.toArray(columns);
729             columnMap = Arrays.copyOf(columnMap, columns.length);
730 
731             List<? extends RowSorter.SortKey> sortKeys =
732                     (rowSorter == null) ? null : rowSorter.getSortKeys();
733             fireTableStructureChanged();
734             restoreSortKeys(sortKeys);
735         }
736 
restoreSortKeys(List<? extends RowSorter.SortKey> sortKeys)737         private void restoreSortKeys(List<? extends RowSorter.SortKey> sortKeys) {
738             if (sortKeys != null) {
739                 // check if preserved sortKeys are valid for this folder
740                 for (int i = 0; i < sortKeys.size(); i++) {
741                     RowSorter.SortKey sortKey = sortKeys.get(i);
742                     if (sortKey.getColumn() >= columns.length) {
743                         sortKeys = null;
744                         break;
745                     }
746                 }
747                 if (sortKeys != null) {
748                     rowSorter.setSortKeys(sortKeys);
749                 }
750             }
751         }
752 
getRowCount()753         public int getRowCount() {
754             return directoryModel.getSize();
755         }
756 
getColumnCount()757         public int getColumnCount() {
758             return columns.length;
759         }
760 
getValueAt(int row, int col)761         public Object getValueAt(int row, int col) {
762             // Note: It is very important to avoid getting info on drives, as
763             // this will trigger "No disk in A:" and similar dialogs.
764             //
765             // Use (f.exists() && !chooser.getFileSystemView().isFileSystemRoot(f)) to
766             // determine if it is safe to call methods directly on f.
767             return getFileColumnValue((File)directoryModel.getElementAt(row), col);
768         }
769 
getFileColumnValue(File f, int col)770         private Object getFileColumnValue(File f, int col) {
771             return (col == COLUMN_FILENAME)
772                     ? f // always return the file itself for the 1st column
773                     : ShellFolder.getFolderColumnValue(f, columnMap[col]);
774         }
775 
setValueAt(Object value, int row, int col)776         public void setValueAt(Object value, int row, int col) {
777             if (col == COLUMN_FILENAME) {
778                 final JFileChooser chooser = getFileChooser();
779                 File f = (File)getValueAt(row, col);
780                 if (f != null) {
781                     String oldDisplayName = chooser.getName(f);
782                     String oldFileName = f.getName();
783                     String newDisplayName = ((String)value).trim();
784                     String newFileName;
785 
786                     if (!newDisplayName.equals(oldDisplayName)) {
787                         newFileName = newDisplayName;
788                         //Check if extension is hidden from user
789                         int i1 = oldFileName.length();
790                         int i2 = oldDisplayName.length();
791                         if (i1 > i2 && oldFileName.charAt(i2) == '.') {
792                             newFileName = newDisplayName + oldFileName.substring(i2);
793                         }
794 
795                         // rename
796                         FileSystemView fsv = chooser.getFileSystemView();
797                         final File f2 = fsv.createFileObject(f.getParentFile(), newFileName);
798                         if (f2.exists()) {
799                             JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorFileExistsText,
800                                     oldFileName), renameErrorTitleText, JOptionPane.ERROR_MESSAGE);
801                         } else {
802                             if (FilePane.this.getModel().renameFile(f, f2)) {
803                                 if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
804                                     // The setSelectedFile method produces a new setValueAt invocation while the JTable
805                                     // is editing. Postpone file selection to be sure that edit mode of the JTable
806                                     // is completed
807                                     SwingUtilities.invokeLater(new Runnable() {
808                                         public void run() {
809                                             if (chooser.isMultiSelectionEnabled()) {
810                                                 chooser.setSelectedFiles(new File[]{f2});
811                                             } else {
812                                                 chooser.setSelectedFile(f2);
813                                             }
814                                         }
815                                     });
816                                 } else {
817                                     // Could be because of delay in updating Desktop folder
818                                     // chooser.setSelectedFile(null);
819                                 }
820                             } else {
821                                 JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorText, oldFileName),
822                                         renameErrorTitleText, JOptionPane.ERROR_MESSAGE);
823                             }
824                         }
825                     }
826                 }
827             }
828         }
829 
isCellEditable(int row, int column)830         public boolean isCellEditable(int row, int column) {
831             File currentDirectory = getFileChooser().getCurrentDirectory();
832             return (!readOnly && column == COLUMN_FILENAME && canWrite(currentDirectory));
833         }
834 
contentsChanged(ListDataEvent e)835         public void contentsChanged(ListDataEvent e) {
836             // Update the selection after the model has been updated
837             new DelayedSelectionUpdater();
838             fireTableDataChanged();
839         }
840 
intervalAdded(ListDataEvent e)841         public void intervalAdded(ListDataEvent e) {
842             int i0 = e.getIndex0();
843             int i1 = e.getIndex1();
844             if (i0 == i1) {
845                 File file = (File)getModel().getElementAt(i0);
846                 if (file.equals(newFolderFile)) {
847                     new DelayedSelectionUpdater(file);
848                     newFolderFile = null;
849                 }
850             }
851 
852             fireTableRowsInserted(e.getIndex0(), e.getIndex1());
853         }
intervalRemoved(ListDataEvent e)854         public void intervalRemoved(ListDataEvent e) {
855             fireTableRowsDeleted(e.getIndex0(), e.getIndex1());
856         }
857 
getColumns()858         public ShellFolderColumnInfo[] getColumns() {
859             return columns;
860         }
861     }
862 
863 
updateDetailsColumnModel(JTable table)864     private void updateDetailsColumnModel(JTable table) {
865         if (table != null) {
866             ShellFolderColumnInfo[] columns = detailsTableModel.getColumns();
867 
868             TableColumnModel columnModel = new DefaultTableColumnModel();
869             for (int i = 0; i < columns.length; i++) {
870                 ShellFolderColumnInfo dataItem = columns[i];
871                 TableColumn column = new TableColumn(i);
872 
873                 String title = dataItem.getTitle();
874                 if (title != null && title.startsWith("FileChooser.") && title.endsWith("HeaderText")) {
875                     // the column must have a string resource that we try to get
876                     String uiTitle = UIManager.getString(title, table.getLocale());
877                     if (uiTitle != null) {
878                         title = uiTitle;
879                     }
880                 }
881                 column.setHeaderValue(title);
882 
883                 Integer width = dataItem.getWidth();
884                 if (width != null) {
885                     column.setPreferredWidth(width);
886                     // otherwise we let JTable to decide the actual width
887                 }
888 
889                 columnModel.addColumn(column);
890             }
891 
892             // Install cell editor for editing file name
893             if (!readOnly && columnModel.getColumnCount() > COLUMN_FILENAME) {
894                 columnModel.getColumn(COLUMN_FILENAME).
895                         setCellEditor(getDetailsTableCellEditor());
896             }
897 
898             table.setColumnModel(columnModel);
899         }
900     }
901 
getRowSorter()902     private DetailsTableRowSorter getRowSorter() {
903         if (rowSorter == null) {
904             rowSorter = new DetailsTableRowSorter();
905         }
906         return rowSorter;
907     }
908 
909     private class DetailsTableRowSorter extends TableRowSorter<TableModel> {
DetailsTableRowSorter()910         public DetailsTableRowSorter() {
911             setModelWrapper(new SorterModelWrapper());
912         }
913 
updateComparators(ShellFolderColumnInfo [] columns)914         public void updateComparators(ShellFolderColumnInfo [] columns) {
915             for (int i = 0; i < columns.length; i++) {
916                 Comparator c = columns[i].getComparator();
917                 if (c != null) {
918                     c = new DirectoriesFirstComparatorWrapper(i, c);
919                 }
920                 setComparator(i, c);
921             }
922         }
923 
924         @Override
sort()925         public void sort() {
926             ShellFolder.invoke(new Callable<Void>() {
927                 public Void call() {
928                     DetailsTableRowSorter.super.sort();
929                     return null;
930                 }
931             });
932         }
933 
modelStructureChanged()934         public void modelStructureChanged() {
935             super.modelStructureChanged();
936             updateComparators(detailsTableModel.getColumns());
937         }
938 
939         private class SorterModelWrapper extends ModelWrapper<TableModel, Integer> {
getModel()940             public TableModel getModel() {
941                 return getDetailsTableModel();
942             }
943 
getColumnCount()944             public int getColumnCount() {
945                 return getDetailsTableModel().getColumnCount();
946             }
947 
getRowCount()948             public int getRowCount() {
949                 return getDetailsTableModel().getRowCount();
950             }
951 
getValueAt(int row, int column)952             public Object getValueAt(int row, int column) {
953                 return FilePane.this.getModel().getElementAt(row);
954             }
955 
getIdentifier(int row)956             public Integer getIdentifier(int row) {
957                 return row;
958             }
959         }
960     }
961 
962     /**
963      * This class sorts directories before files, comparing directory to
964      * directory and file to file using the wrapped comparator.
965      */
966     private class DirectoriesFirstComparatorWrapper implements Comparator<File> {
967         private Comparator comparator;
968         private int column;
969 
DirectoriesFirstComparatorWrapper(int column, Comparator comparator)970         public DirectoriesFirstComparatorWrapper(int column, Comparator comparator) {
971             this.column = column;
972             this.comparator = comparator;
973         }
974 
compare(File f1, File f2)975         public int compare(File f1, File f2) {
976             if (f1 != null && f2 != null) {
977                 boolean traversable1 = getFileChooser().isTraversable(f1);
978                 boolean traversable2 = getFileChooser().isTraversable(f2);
979                 // directories go first
980                 if (traversable1 && !traversable2) {
981                     return -1;
982                 }
983                 if (!traversable1 && traversable2) {
984                     return 1;
985                 }
986             }
987             if (detailsTableModel.getColumns()[column].isCompareByColumn()) {
988                 return comparator.compare(
989                         getDetailsTableModel().getFileColumnValue(f1, column),
990                         getDetailsTableModel().getFileColumnValue(f2, column)
991                 );
992             }
993             // For this column we need to pass the file itself (not a
994             // column value) to the comparator
995             return comparator.compare(f1, f2);
996         }
997     }
998 
999     private DetailsTableCellEditor tableCellEditor;
1000 
getDetailsTableCellEditor()1001     private DetailsTableCellEditor getDetailsTableCellEditor() {
1002         if (tableCellEditor == null) {
1003             tableCellEditor = new DetailsTableCellEditor(new JTextField());
1004         }
1005         return tableCellEditor;
1006     }
1007 
1008     private class DetailsTableCellEditor extends DefaultCellEditor {
1009         private final JTextField tf;
1010 
DetailsTableCellEditor(JTextField tf)1011         public DetailsTableCellEditor(JTextField tf) {
1012             super(tf);
1013             this.tf = tf;
1014             tf.setName("Table.editor");
1015             tf.addFocusListener(editorFocusListener);
1016         }
1017 
getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)1018         public Component getTableCellEditorComponent(JTable table, Object value,
1019                                                      boolean isSelected, int row, int column) {
1020             Component comp = super.getTableCellEditorComponent(table, value,
1021                     isSelected, row, column);
1022             if (value instanceof File) {
1023                 tf.setText(getFileChooser().getName((File) value));
1024                 tf.selectAll();
1025             }
1026             return comp;
1027         }
1028     }
1029 
1030 
1031     class DetailsTableCellRenderer extends DefaultTableCellRenderer {
1032         JFileChooser chooser;
1033         DateFormat df;
1034 
DetailsTableCellRenderer(JFileChooser chooser)1035         DetailsTableCellRenderer(JFileChooser chooser) {
1036             this.chooser = chooser;
1037             df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT,
1038                                                 chooser.getLocale());
1039         }
1040 
setBounds(int x, int y, int width, int height)1041         public void setBounds(int x, int y, int width, int height) {
1042         if (getHorizontalAlignment() == SwingConstants.LEADING &&
1043                     !fullRowSelection) {
1044                 // Restrict width to actual text
1045                 width = Math.min(width, this.getPreferredSize().width+4);
1046             } else {
1047                 x -= 4;
1048             }
1049             super.setBounds(x, y, width, height);
1050         }
1051 
1052 
getInsets(Insets i)1053         public Insets getInsets(Insets i) {
1054             // Provide some space between columns
1055             i = super.getInsets(i);
1056             i.left  += 4;
1057             i.right += 4;
1058             return i;
1059         }
1060 
getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)1061         public Component getTableCellRendererComponent(JTable table, Object value,
1062                               boolean isSelected, boolean hasFocus, int row, int column) {
1063 
1064             if ((table.convertColumnIndexToModel(column) != COLUMN_FILENAME ||
1065                     (listViewWindowsStyle && !table.isFocusOwner())) &&
1066                     !fullRowSelection) {
1067                 isSelected = false;
1068             }
1069 
1070             super.getTableCellRendererComponent(table, value, isSelected,
1071                                                        hasFocus, row, column);
1072 
1073             setIcon(null);
1074 
1075             int modelColumn = table.convertColumnIndexToModel(column);
1076             ShellFolderColumnInfo columnInfo = detailsTableModel.getColumns()[modelColumn];
1077 
1078             Integer alignment = columnInfo.getAlignment();
1079             if (alignment == null) {
1080                 alignment = (value instanceof Number)
1081                         ? SwingConstants.RIGHT
1082                         : SwingConstants.LEADING;
1083             }
1084 
1085             setHorizontalAlignment(alignment);
1086 
1087             // formatting cell text
1088             // TODO: it's rather a temporary trick, to be revised
1089             String text;
1090 
1091             if (value == null) {
1092                 text = "";
1093 
1094             } else if (value instanceof File) {
1095                 File file = (File)value;
1096                 text = chooser.getName(file);
1097                 Icon icon = chooser.getIcon(file);
1098                 setIcon(icon);
1099 
1100             } else if (value instanceof Long) {
1101                 long len = ((Long) value) / 1024L;
1102                 if (listViewWindowsStyle) {
1103                     text = MessageFormat.format(kiloByteString, len + 1);
1104                 } else if (len < 1024L) {
1105                     text = MessageFormat.format(kiloByteString, (len == 0L) ? 1L : len);
1106                 } else {
1107                     len /= 1024L;
1108                     if (len < 1024L) {
1109                         text = MessageFormat.format(megaByteString, len);
1110                     } else {
1111                         len /= 1024L;
1112                         text = MessageFormat.format(gigaByteString, len);
1113                     }
1114                 }
1115 
1116             } else if (value instanceof Date) {
1117                 text = df.format((Date)value);
1118 
1119             } else {
1120                 text = value.toString();
1121             }
1122 
1123             setText(text);
1124 
1125             return this;
1126         }
1127     }
1128 
createDetailsView()1129     public JPanel createDetailsView() {
1130         final JFileChooser chooser = getFileChooser();
1131 
1132         JPanel p = new JPanel(new BorderLayout());
1133 
1134         final JTable detailsTable = new JTable(getDetailsTableModel()) {
1135             // Handle Escape key events here
1136             protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
1137                 if (e.getKeyCode() == KeyEvent.VK_ESCAPE && getCellEditor() == null) {
1138                     // We are not editing, forward to filechooser.
1139                     chooser.dispatchEvent(e);
1140                     return true;
1141                 }
1142                 return super.processKeyBinding(ks, e, condition, pressed);
1143             }
1144 
1145             public void tableChanged(TableModelEvent e) {
1146                 super.tableChanged(e);
1147 
1148                 if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
1149                     // update header with possibly changed column set
1150                     updateDetailsColumnModel(this);
1151                 }
1152             }
1153         };
1154 
1155         detailsTable.setRowSorter(getRowSorter());
1156         detailsTable.setAutoCreateColumnsFromModel(false);
1157         detailsTable.setComponentOrientation(chooser.getComponentOrientation());
1158         detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
1159         detailsTable.setShowGrid(false);
1160         detailsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
1161         detailsTable.addKeyListener(detailsKeyListener);
1162 
1163         Font font = list.getFont();
1164         detailsTable.setFont(font);
1165         detailsTable.setIntercellSpacing(new Dimension(0, 0));
1166 
1167         TableCellRenderer headerRenderer =
1168                 new AlignableTableHeaderRenderer(detailsTable.getTableHeader().getDefaultRenderer());
1169         detailsTable.getTableHeader().setDefaultRenderer(headerRenderer);
1170         TableCellRenderer cellRenderer = new DetailsTableCellRenderer(chooser);
1171         detailsTable.setDefaultRenderer(Object.class, cellRenderer);
1172 
1173         // So that drag can be started on a mouse press
1174         detailsTable.getColumnModel().getSelectionModel().
1175             setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1176 
1177         detailsTable.addMouseListener(getMouseHandler());
1178         // No need to addListSelectionListener because selections are forwarded
1179         // to our JList.
1180 
1181         // 4835633 : tell BasicTableUI that this is a file list
1182         detailsTable.putClientProperty("Table.isFileList", Boolean.TRUE);
1183 
1184         if (listViewWindowsStyle) {
1185             detailsTable.addFocusListener(repaintListener);
1186         }
1187 
1188         // TAB/SHIFT-TAB should transfer focus and ENTER should select an item.
1189         // We don't want them to navigate within the table
1190         ActionMap am = SwingUtilities.getUIActionMap(detailsTable);
1191         am.remove("selectNextRowCell");
1192         am.remove("selectPreviousRowCell");
1193         am.remove("selectNextColumnCell");
1194         am.remove("selectPreviousColumnCell");
1195         detailsTable.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
1196                      null);
1197         detailsTable.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
1198                      null);
1199 
1200         JScrollPane scrollpane = new JScrollPane(detailsTable);
1201         scrollpane.setComponentOrientation(chooser.getComponentOrientation());
1202         LookAndFeel.installColors(scrollpane.getViewport(), "Table.background", "Table.foreground");
1203 
1204         // Adjust width of first column so the table fills the viewport when
1205         // first displayed (temporary listener).
1206         scrollpane.addComponentListener(new ComponentAdapter() {
1207             public void componentResized(ComponentEvent e) {
1208                 JScrollPane sp = (JScrollPane)e.getComponent();
1209                 fixNameColumnWidth(sp.getViewport().getSize().width);
1210                 sp.removeComponentListener(this);
1211             }
1212         });
1213 
1214         // 4835633.
1215         // If the mouse is pressed in the area below the Details view table, the
1216         // event is not dispatched to the Table MouseListener but to the
1217         // scrollpane.  Listen for that here so we can clear the selection.
1218         scrollpane.addMouseListener(new MouseAdapter() {
1219             public void mousePressed(MouseEvent e) {
1220                 JScrollPane jsp = ((JScrollPane)e.getComponent());
1221                 JTable table = (JTable)jsp.getViewport().getView();
1222 
1223                 if (!e.isShiftDown() || table.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {
1224                     clearSelection();
1225                     TableCellEditor tce = table.getCellEditor();
1226                     if (tce != null) {
1227                         tce.stopCellEditing();
1228                     }
1229                 }
1230             }
1231         });
1232 
1233         detailsTable.setForeground(list.getForeground());
1234         detailsTable.setBackground(list.getBackground());
1235 
1236         if (listViewBorder != null) {
1237             scrollpane.setBorder(listViewBorder);
1238         }
1239         p.add(scrollpane, BorderLayout.CENTER);
1240 
1241         detailsTableModel.fireTableStructureChanged();
1242 
1243         detailsTable.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesDetailsAccessibleName);
1244 
1245         return p;
1246     } // createDetailsView
1247 
1248     private class AlignableTableHeaderRenderer implements TableCellRenderer {
1249         TableCellRenderer wrappedRenderer;
1250 
AlignableTableHeaderRenderer(TableCellRenderer wrappedRenderer)1251         public AlignableTableHeaderRenderer(TableCellRenderer wrappedRenderer) {
1252             this.wrappedRenderer = wrappedRenderer;
1253         }
1254 
getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)1255         public Component getTableCellRendererComponent(
1256                                 JTable table, Object value, boolean isSelected,
1257                                 boolean hasFocus, int row, int column) {
1258 
1259             Component c = wrappedRenderer.getTableCellRendererComponent(
1260                                 table, value, isSelected, hasFocus, row, column);
1261 
1262             int modelColumn = table.convertColumnIndexToModel(column);
1263             ShellFolderColumnInfo columnInfo = detailsTableModel.getColumns()[modelColumn];
1264 
1265             Integer alignment = columnInfo.getAlignment();
1266             if (alignment == null) {
1267                 alignment = SwingConstants.CENTER;
1268             }
1269             if (c instanceof JLabel) {
1270                 ((JLabel) c).setHorizontalAlignment(alignment);
1271             }
1272 
1273             return c;
1274         }
1275     }
1276 
fixNameColumnWidth(int viewWidth)1277     private void fixNameColumnWidth(int viewWidth) {
1278         TableColumn nameCol = detailsTable.getColumnModel().getColumn(COLUMN_FILENAME);
1279         int tableWidth = detailsTable.getPreferredSize().width;
1280 
1281         if (tableWidth < viewWidth) {
1282             nameCol.setPreferredWidth(nameCol.getPreferredWidth() + viewWidth - tableWidth);
1283         }
1284     }
1285 
1286     private class DelayedSelectionUpdater implements Runnable {
1287         File editFile;
1288 
DelayedSelectionUpdater()1289         DelayedSelectionUpdater() {
1290             this(null);
1291         }
1292 
DelayedSelectionUpdater(File editFile)1293         DelayedSelectionUpdater(File editFile) {
1294             this.editFile = editFile;
1295             if (isShowing()) {
1296                 SwingUtilities.invokeLater(this);
1297             }
1298         }
1299 
run()1300         public void run() {
1301             setFileSelected();
1302             if (editFile != null) {
1303                 editFileName(getRowSorter().convertRowIndexToView(
1304                         getModel().indexOf(editFile)));
1305                 editFile = null;
1306             }
1307         }
1308     }
1309 
1310 
1311     /**
1312      * Creates a selection listener for the list of files and directories.
1313      *
1314      * @return a <code>ListSelectionListener</code>
1315      */
createListSelectionListener()1316     public ListSelectionListener createListSelectionListener() {
1317         return fileChooserUIAccessor.createListSelectionListener();
1318     }
1319 
1320     int lastIndex = -1;
1321     File editFile = null;
1322 
getEditIndex()1323     private int getEditIndex() {
1324         return lastIndex;
1325     }
1326 
setEditIndex(int i)1327     private void setEditIndex(int i) {
1328         lastIndex = i;
1329     }
1330 
resetEditIndex()1331     private void resetEditIndex() {
1332         lastIndex = -1;
1333     }
1334 
cancelEdit()1335     private void cancelEdit() {
1336         if (editFile != null) {
1337             editFile = null;
1338             list.remove(editCell);
1339             repaint();
1340         } else if (detailsTable != null && detailsTable.isEditing()) {
1341             detailsTable.getCellEditor().cancelCellEditing();
1342         }
1343     }
1344 
1345     JTextField editCell = null;
1346 
1347     /**
1348      * @param index visual index of the file to be edited
1349      */
editFileName(int index)1350     private void editFileName(int index) {
1351         JFileChooser chooser = getFileChooser();
1352         File currentDirectory = chooser.getCurrentDirectory();
1353 
1354         if (readOnly || !canWrite(currentDirectory)) {
1355             return;
1356         }
1357 
1358         ensureIndexIsVisible(index);
1359         switch (viewType) {
1360           case VIEWTYPE_LIST:
1361             editFile = (File)getModel().getElementAt(getRowSorter().convertRowIndexToModel(index));
1362             Rectangle r = list.getCellBounds(index, index);
1363             if (editCell == null) {
1364                 editCell = new JTextField();
1365                 editCell.setName("Tree.cellEditor");
1366                 editCell.addActionListener(new EditActionListener());
1367                 editCell.addFocusListener(editorFocusListener);
1368                 editCell.setNextFocusableComponent(list);
1369             }
1370             list.add(editCell);
1371             editCell.setText(chooser.getName(editFile));
1372             ComponentOrientation orientation = list.getComponentOrientation();
1373             editCell.setComponentOrientation(orientation);
1374 
1375             Icon icon = chooser.getIcon(editFile);
1376 
1377             // PENDING - grab padding (4) below from defaults table.
1378             int editX = icon == null ? 20 : icon.getIconWidth() + 4;
1379 
1380             if (orientation.isLeftToRight()) {
1381                 editCell.setBounds(editX + r.x, r.y, r.width - editX, r.height);
1382             } else {
1383                 editCell.setBounds(r.x, r.y, r.width - editX, r.height);
1384             }
1385             editCell.requestFocus();
1386             editCell.selectAll();
1387             break;
1388 
1389           case VIEWTYPE_DETAILS:
1390             detailsTable.editCellAt(index, COLUMN_FILENAME);
1391             break;
1392         }
1393     }
1394 
1395 
1396     class EditActionListener implements ActionListener {
actionPerformed(ActionEvent e)1397         public void actionPerformed(ActionEvent e) {
1398             applyEdit();
1399         }
1400     }
1401 
applyEdit()1402     private void applyEdit() {
1403         if (editFile != null && editFile.exists()) {
1404             JFileChooser chooser = getFileChooser();
1405             String oldDisplayName = chooser.getName(editFile);
1406             String oldFileName = editFile.getName();
1407             String newDisplayName = editCell.getText().trim();
1408             String newFileName;
1409 
1410             if (!newDisplayName.equals(oldDisplayName)) {
1411                 newFileName = newDisplayName;
1412                 //Check if extension is hidden from user
1413                 int i1 = oldFileName.length();
1414                 int i2 = oldDisplayName.length();
1415                 if (i1 > i2 && oldFileName.charAt(i2) == '.') {
1416                     newFileName = newDisplayName + oldFileName.substring(i2);
1417                 }
1418 
1419                 // rename
1420                 FileSystemView fsv = chooser.getFileSystemView();
1421                 File f2 = fsv.createFileObject(editFile.getParentFile(), newFileName);
1422                 if (f2.exists()) {
1423                     JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorFileExistsText, oldFileName),
1424                             renameErrorTitleText, JOptionPane.ERROR_MESSAGE);
1425                 } else {
1426                     if (getModel().renameFile(editFile, f2)) {
1427                         if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
1428                             if (chooser.isMultiSelectionEnabled()) {
1429                                 chooser.setSelectedFiles(new File[]{f2});
1430                             } else {
1431                                 chooser.setSelectedFile(f2);
1432                             }
1433                         } else {
1434                             //Could be because of delay in updating Desktop folder
1435                             //chooser.setSelectedFile(null);
1436                         }
1437                     } else {
1438                         JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorText, oldFileName),
1439                                 renameErrorTitleText, JOptionPane.ERROR_MESSAGE);
1440                     }
1441                 }
1442             }
1443         }
1444         if (detailsTable != null && detailsTable.isEditing()) {
1445             detailsTable.getCellEditor().stopCellEditing();
1446         }
1447         cancelEdit();
1448     }
1449 
1450     protected Action newFolderAction;
1451 
getNewFolderAction()1452     public Action getNewFolderAction() {
1453         if (!readOnly && newFolderAction == null) {
1454             newFolderAction = new AbstractAction(newFolderActionLabelText) {
1455                 private Action basicNewFolderAction;
1456 
1457                 // Initializer
1458                 {
1459                     putValue(Action.ACTION_COMMAND_KEY, FilePane.ACTION_NEW_FOLDER);
1460 
1461                     File currentDirectory = getFileChooser().getCurrentDirectory();
1462                     if (currentDirectory != null) {
1463                         setEnabled(canWrite(currentDirectory));
1464                     }
1465                 }
1466 
1467                 public void actionPerformed(ActionEvent ev) {
1468                     if (basicNewFolderAction == null) {
1469                         basicNewFolderAction = fileChooserUIAccessor.getNewFolderAction();
1470                     }
1471                     JFileChooser fc = getFileChooser();
1472                     File oldFile = fc.getSelectedFile();
1473                     basicNewFolderAction.actionPerformed(ev);
1474                     File newFile = fc.getSelectedFile();
1475                     if (newFile != null && !newFile.equals(oldFile) && newFile.isDirectory()) {
1476                         newFolderFile = newFile;
1477                     }
1478                 }
1479             };
1480         }
1481         return newFolderAction;
1482     }
1483 
1484     protected class FileRenderer extends DefaultListCellRenderer  {
1485 
getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)1486         public Component getListCellRendererComponent(JList list, Object value,
1487                                                       int index, boolean isSelected,
1488                                                       boolean cellHasFocus) {
1489 
1490             if (listViewWindowsStyle && !list.isFocusOwner()) {
1491                 isSelected = false;
1492             }
1493 
1494             super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1495             File file = (File) value;
1496             String fileName = getFileChooser().getName(file);
1497             setText(fileName);
1498             setFont(list.getFont());
1499 
1500             Icon icon = getFileChooser().getIcon(file);
1501             if (icon != null) {
1502                 setIcon(icon);
1503             } else {
1504                 if (getFileChooser().getFileSystemView().isTraversable(file)) {
1505                     setText(fileName+File.separator);
1506                 }
1507             }
1508 
1509             return this;
1510         }
1511     }
1512 
1513 
setFileSelected()1514     void setFileSelected() {
1515         if (getFileChooser().isMultiSelectionEnabled() && !isDirectorySelected()) {
1516             File[] files = getFileChooser().getSelectedFiles(); // Should be selected
1517             Object[] selectedObjects = list.getSelectedValues(); // Are actually selected
1518 
1519             listSelectionModel.setValueIsAdjusting(true);
1520             try {
1521                 int lead = listSelectionModel.getLeadSelectionIndex();
1522                 int anchor = listSelectionModel.getAnchorSelectionIndex();
1523 
1524                 Arrays.sort(files);
1525                 Arrays.sort(selectedObjects);
1526 
1527                 int shouldIndex = 0;
1528                 int actuallyIndex = 0;
1529 
1530                 // Remove files that shouldn't be selected and add files which should be selected
1531                 // Note: Assume files are already sorted in compareTo order.
1532                 while (shouldIndex < files.length &&
1533                        actuallyIndex < selectedObjects.length) {
1534                     int comparison = files[shouldIndex].compareTo((File)selectedObjects[actuallyIndex]);
1535                     if (comparison < 0) {
1536                         doSelectFile(files[shouldIndex++]);
1537                     } else if (comparison > 0) {
1538                         doDeselectFile(selectedObjects[actuallyIndex++]);
1539                     } else {
1540                         // Do nothing
1541                         shouldIndex++;
1542                         actuallyIndex++;
1543                     }
1544 
1545                 }
1546 
1547                 while (shouldIndex < files.length) {
1548                     doSelectFile(files[shouldIndex++]);
1549                 }
1550 
1551                 while (actuallyIndex < selectedObjects.length) {
1552                     doDeselectFile(selectedObjects[actuallyIndex++]);
1553                 }
1554 
1555                 // restore the anchor and lead
1556                 if (listSelectionModel instanceof DefaultListSelectionModel) {
1557                     ((DefaultListSelectionModel)listSelectionModel).
1558                         moveLeadSelectionIndex(lead);
1559                     listSelectionModel.setAnchorSelectionIndex(anchor);
1560                 }
1561             } finally {
1562                 listSelectionModel.setValueIsAdjusting(false);
1563             }
1564         } else {
1565             JFileChooser chooser = getFileChooser();
1566             File f;
1567             if (isDirectorySelected()) {
1568                 f = getDirectory();
1569             } else {
1570                 f = chooser.getSelectedFile();
1571             }
1572             int i;
1573             if (f != null && (i = getModel().indexOf(f)) >= 0) {
1574                 int viewIndex = getRowSorter().convertRowIndexToView(i);
1575                 listSelectionModel.setSelectionInterval(viewIndex, viewIndex);
1576                 ensureIndexIsVisible(viewIndex);
1577             } else {
1578                 clearSelection();
1579             }
1580         }
1581     }
1582 
doSelectFile(File fileToSelect)1583     private void doSelectFile(File fileToSelect) {
1584         int index = getModel().indexOf(fileToSelect);
1585         // could be missed in the current directory if it changed
1586         if (index >= 0) {
1587             index = getRowSorter().convertRowIndexToView(index);
1588             listSelectionModel.addSelectionInterval(index, index);
1589         }
1590     }
1591 
doDeselectFile(Object fileToDeselect)1592     private void doDeselectFile(Object fileToDeselect) {
1593         int index = getRowSorter().convertRowIndexToView(
1594                                 getModel().indexOf(fileToDeselect));
1595         listSelectionModel.removeSelectionInterval(index, index);
1596     }
1597 
1598     /* The following methods are used by the PropertyChange Listener */
1599 
doSelectedFileChanged(PropertyChangeEvent e)1600     private void doSelectedFileChanged(PropertyChangeEvent e) {
1601         applyEdit();
1602         File f = (File) e.getNewValue();
1603         JFileChooser fc = getFileChooser();
1604         if (f != null
1605             && ((fc.isFileSelectionEnabled() && !f.isDirectory())
1606                 || (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {
1607 
1608             setFileSelected();
1609         }
1610     }
1611 
doSelectedFilesChanged(PropertyChangeEvent e)1612     private void doSelectedFilesChanged(PropertyChangeEvent e) {
1613         applyEdit();
1614         File[] files = (File[]) e.getNewValue();
1615         JFileChooser fc = getFileChooser();
1616         if (files != null
1617             && files.length > 0
1618             && (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) {
1619             setFileSelected();
1620         }
1621     }
1622 
doDirectoryChanged(PropertyChangeEvent e)1623     private void doDirectoryChanged(PropertyChangeEvent e) {
1624         getDetailsTableModel().updateColumnInfo();
1625 
1626         JFileChooser fc = getFileChooser();
1627         FileSystemView fsv = fc.getFileSystemView();
1628 
1629         applyEdit();
1630         resetEditIndex();
1631         ensureIndexIsVisible(0);
1632         File currentDirectory = fc.getCurrentDirectory();
1633         if (currentDirectory != null) {
1634             if (!readOnly) {
1635                 getNewFolderAction().setEnabled(canWrite(currentDirectory));
1636             }
1637             fileChooserUIAccessor.getChangeToParentDirectoryAction().setEnabled(!fsv.isRoot(currentDirectory));
1638         }
1639         if (list != null) {
1640             list.clearSelection();
1641         }
1642     }
1643 
doFilterChanged(PropertyChangeEvent e)1644     private void doFilterChanged(PropertyChangeEvent e) {
1645         applyEdit();
1646         resetEditIndex();
1647         clearSelection();
1648     }
1649 
doFileSelectionModeChanged(PropertyChangeEvent e)1650     private void doFileSelectionModeChanged(PropertyChangeEvent e) {
1651         applyEdit();
1652         resetEditIndex();
1653         clearSelection();
1654     }
1655 
doMultiSelectionChanged(PropertyChangeEvent e)1656     private void doMultiSelectionChanged(PropertyChangeEvent e) {
1657         if (getFileChooser().isMultiSelectionEnabled()) {
1658             listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
1659         } else {
1660             listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1661             clearSelection();
1662             getFileChooser().setSelectedFiles(null);
1663         }
1664     }
1665 
1666     /*
1667      * Listen for filechooser property changes, such as
1668      * the selected file changing, or the type of the dialog changing.
1669      */
propertyChange(PropertyChangeEvent e)1670     public void propertyChange(PropertyChangeEvent e) {
1671             if (viewType == -1) {
1672                 setViewType(VIEWTYPE_LIST);
1673             }
1674 
1675         String s = e.getPropertyName();
1676         if (s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
1677             doSelectedFileChanged(e);
1678         } else if (s.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {
1679             doSelectedFilesChanged(e);
1680         } else if (s.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {
1681             doDirectoryChanged(e);
1682         } else if (s.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {
1683             doFilterChanged(e);
1684         } else if (s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
1685             doFileSelectionModeChanged(e);
1686         } else if (s.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
1687             doMultiSelectionChanged(e);
1688         } else if (s.equals(JFileChooser.CANCEL_SELECTION)) {
1689             applyEdit();
1690         } else if (s.equals("busy")) {
1691             setCursor((Boolean)e.getNewValue() ? waitCursor : null);
1692         } else if (s.equals("componentOrientation")) {
1693             ComponentOrientation o = (ComponentOrientation)e.getNewValue();
1694             JFileChooser cc = (JFileChooser)e.getSource();
1695             if (o != e.getOldValue()) {
1696                 cc.applyComponentOrientation(o);
1697             }
1698             if (detailsTable != null) {
1699                 detailsTable.setComponentOrientation(o);
1700                 detailsTable.getParent().getParent().setComponentOrientation(o);
1701             }
1702         }
1703     }
1704 
ensureIndexIsVisible(int i)1705     private void ensureIndexIsVisible(int i) {
1706         if (i >= 0) {
1707             if (list != null) {
1708                 list.ensureIndexIsVisible(i);
1709             }
1710             if (detailsTable != null) {
1711                 detailsTable.scrollRectToVisible(detailsTable.getCellRect(i, COLUMN_FILENAME, true));
1712             }
1713         }
1714     }
1715 
ensureFileIsVisible(JFileChooser fc, File f)1716     public void ensureFileIsVisible(JFileChooser fc, File f) {
1717         int modelIndex = getModel().indexOf(f);
1718         if (modelIndex >= 0) {
1719             ensureIndexIsVisible(getRowSorter().convertRowIndexToView(modelIndex));
1720         }
1721     }
1722 
rescanCurrentDirectory()1723     public void rescanCurrentDirectory() {
1724         getModel().validateFileCache();
1725     }
1726 
clearSelection()1727     public void clearSelection() {
1728         if (listSelectionModel != null) {
1729             listSelectionModel.clearSelection();
1730             if (listSelectionModel instanceof DefaultListSelectionModel) {
1731                 ((DefaultListSelectionModel)listSelectionModel).moveLeadSelectionIndex(0);
1732                 listSelectionModel.setAnchorSelectionIndex(0);
1733             }
1734         }
1735     }
1736 
getViewMenu()1737     public JMenu getViewMenu() {
1738         if (viewMenu == null) {
1739             viewMenu = new JMenu(viewMenuLabelText);
1740             ButtonGroup viewButtonGroup = new ButtonGroup();
1741 
1742             for (int i = 0; i < VIEWTYPE_COUNT; i++) {
1743                 JRadioButtonMenuItem mi =
1744                     new JRadioButtonMenuItem(new ViewTypeAction(i));
1745                 viewButtonGroup.add(mi);
1746                 viewMenu.add(mi);
1747             }
1748             updateViewMenu();
1749         }
1750         return viewMenu;
1751     }
1752 
updateViewMenu()1753     private void updateViewMenu() {
1754         if (viewMenu != null) {
1755             Component[] comps = viewMenu.getMenuComponents();
1756             for (Component comp : comps) {
1757                 if (comp instanceof JRadioButtonMenuItem) {
1758                     JRadioButtonMenuItem mi = (JRadioButtonMenuItem) comp;
1759                     if (((ViewTypeAction)mi.getAction()).viewType == viewType) {
1760                         mi.setSelected(true);
1761                     }
1762                 }
1763             }
1764         }
1765     }
1766 
getComponentPopupMenu()1767     public JPopupMenu getComponentPopupMenu() {
1768         JPopupMenu popupMenu = getFileChooser().getComponentPopupMenu();
1769         if (popupMenu != null) {
1770             return popupMenu;
1771         }
1772 
1773         JMenu viewMenu = getViewMenu();
1774         if (contextMenu == null) {
1775             contextMenu = new JPopupMenu();
1776             if (viewMenu != null) {
1777                 contextMenu.add(viewMenu);
1778                 if (listViewWindowsStyle) {
1779                     contextMenu.addSeparator();
1780                 }
1781             }
1782             ActionMap actionMap = getActionMap();
1783             Action refreshAction   = actionMap.get(ACTION_REFRESH);
1784             Action newFolderAction = actionMap.get(ACTION_NEW_FOLDER);
1785             if (refreshAction != null) {
1786                 contextMenu.add(refreshAction);
1787                 if (listViewWindowsStyle && newFolderAction != null) {
1788                     contextMenu.addSeparator();
1789                 }
1790             }
1791             if (newFolderAction != null) {
1792                 contextMenu.add(newFolderAction);
1793             }
1794         }
1795         if (viewMenu != null) {
1796             viewMenu.getPopupMenu().setInvoker(viewMenu);
1797         }
1798         return contextMenu;
1799     }
1800 
1801 
1802     private Handler handler;
1803 
getMouseHandler()1804     protected Handler getMouseHandler() {
1805         if (handler == null) {
1806             handler = new Handler();
1807         }
1808         return handler;
1809     }
1810 
1811     private class Handler implements MouseListener {
1812         private MouseListener doubleClickListener;
1813 
mouseClicked(MouseEvent evt)1814         public void mouseClicked(MouseEvent evt) {
1815             JComponent source = (JComponent)evt.getSource();
1816 
1817             int index;
1818             if (source instanceof JList) {
1819                 index = SwingUtilities2.loc2IndexFileList(list, evt.getPoint());
1820             } else if (source instanceof JTable) {
1821                 JTable table = (JTable)source;
1822                 Point p = evt.getPoint();
1823                 index = table.rowAtPoint(p);
1824 
1825                 boolean pointOutsidePrefSize =
1826                         SwingUtilities2.pointOutsidePrefSize(
1827                             table, index, table.columnAtPoint(p), p);
1828 
1829                 if (pointOutsidePrefSize && !fullRowSelection) {
1830                     return;
1831                 }
1832 
1833                 // Translate point from table to list
1834                 if (index >= 0 && list != null &&
1835                     listSelectionModel.isSelectedIndex(index)) {
1836 
1837                     // Make a new event with the list as source, placing the
1838                     // click in the corresponding list cell.
1839                     Rectangle r = list.getCellBounds(index, index);
1840                     MouseEvent newEvent = new MouseEvent(list, evt.getID(),
1841                                          evt.getWhen(), evt.getModifiers(),
1842                                          r.x + 1, r.y + r.height/2,
1843                                          evt.getXOnScreen(),
1844                                          evt.getYOnScreen(),
1845                                          evt.getClickCount(), evt.isPopupTrigger(),
1846                                          evt.getButton());
1847                     MouseEventAccessor meAccessor = AWTAccessor.getMouseEventAccessor();
1848                     meAccessor.setCausedByTouchEvent(newEvent,
1849                         meAccessor.isCausedByTouchEvent(evt));
1850                     evt = newEvent;
1851                 }
1852             } else {
1853                 return;
1854             }
1855 
1856             if (index >= 0 && SwingUtilities.isLeftMouseButton(evt)) {
1857                 JFileChooser fc = getFileChooser();
1858 
1859                 // For single click, we handle editing file name
1860                 if (evt.getClickCount() == 1 && source instanceof JList) {
1861                     if ((!fc.isMultiSelectionEnabled() || fc.getSelectedFiles().length <= 1)
1862                         && index >= 0 && listSelectionModel.isSelectedIndex(index)
1863                         && getEditIndex() == index && editFile == null) {
1864 
1865                         editFileName(index);
1866                     } else {
1867                         if (index >= 0) {
1868                             setEditIndex(index);
1869                         } else {
1870                             resetEditIndex();
1871                         }
1872                     }
1873                 } else if (evt.getClickCount() == 2) {
1874                     // on double click (open or drill down one directory) be
1875                     // sure to clear the edit index
1876                     resetEditIndex();
1877                 }
1878             }
1879 
1880             // Forward event to Basic
1881             if (getDoubleClickListener() != null) {
1882                 getDoubleClickListener().mouseClicked(evt);
1883             }
1884         }
1885 
mouseEntered(MouseEvent evt)1886         public void mouseEntered(MouseEvent evt) {
1887             JComponent source = (JComponent)evt.getSource();
1888             if (source instanceof JTable) {
1889                 JTable table = (JTable)evt.getSource();
1890 
1891                 TransferHandler th1 = getFileChooser().getTransferHandler();
1892                 TransferHandler th2 = table.getTransferHandler();
1893                 if (th1 != th2) {
1894                     table.setTransferHandler(th1);
1895                 }
1896 
1897                 boolean dragEnabled = getFileChooser().getDragEnabled();
1898                 if (dragEnabled != table.getDragEnabled()) {
1899                     table.setDragEnabled(dragEnabled);
1900                 }
1901             } else if (source instanceof JList) {
1902                 // Forward event to Basic
1903                 if (getDoubleClickListener() != null) {
1904                     getDoubleClickListener().mouseEntered(evt);
1905                 }
1906             }
1907         }
1908 
mouseExited(MouseEvent evt)1909         public void mouseExited(MouseEvent evt) {
1910             if (evt.getSource() instanceof JList) {
1911                 // Forward event to Basic
1912                 if (getDoubleClickListener() != null) {
1913                     getDoubleClickListener().mouseExited(evt);
1914                 }
1915             }
1916         }
1917 
mousePressed(MouseEvent evt)1918         public void mousePressed(MouseEvent evt) {
1919             if (evt.getSource() instanceof JList) {
1920                 // Forward event to Basic
1921                 if (getDoubleClickListener() != null) {
1922                     getDoubleClickListener().mousePressed(evt);
1923                 }
1924             }
1925         }
1926 
mouseReleased(MouseEvent evt)1927         public void mouseReleased(MouseEvent evt) {
1928             if (evt.getSource() instanceof JList) {
1929                 // Forward event to Basic
1930                 if (getDoubleClickListener() != null) {
1931                     getDoubleClickListener().mouseReleased(evt);
1932                 }
1933             }
1934         }
1935 
getDoubleClickListener()1936         private MouseListener getDoubleClickListener() {
1937             // Lazy creation of Basic's listener
1938             if (doubleClickListener == null && list != null) {
1939                 doubleClickListener =
1940                     fileChooserUIAccessor.createDoubleClickListener(list);
1941             }
1942             return doubleClickListener;
1943         }
1944     }
1945 
1946     /**
1947      * Property to remember whether a directory is currently selected in the UI.
1948      *
1949      * @return <code>true</code> iff a directory is currently selected.
1950      */
isDirectorySelected()1951     protected boolean isDirectorySelected() {
1952         return fileChooserUIAccessor.isDirectorySelected();
1953     }
1954 
1955 
1956     /**
1957      * Property to remember the directory that is currently selected in the UI.
1958      *
1959      * @return the value of the <code>directory</code> property
1960      * @see javax.swing.plaf.basic.BasicFileChooserUI#setDirectory
1961      */
getDirectory()1962     protected File getDirectory() {
1963         return fileChooserUIAccessor.getDirectory();
1964     }
1965 
findChildComponent(Container container, Class cls)1966     private Component findChildComponent(Container container, Class cls) {
1967         int n = container.getComponentCount();
1968         for (int i = 0; i < n; i++) {
1969             Component comp = container.getComponent(i);
1970             if (cls.isInstance(comp)) {
1971                 return comp;
1972             } else if (comp instanceof Container) {
1973                 Component c = findChildComponent((Container)comp, cls);
1974                 if (c != null) {
1975                     return c;
1976                 }
1977             }
1978         }
1979         return null;
1980     }
1981 
canWrite(File f)1982     public boolean canWrite(File f) {
1983         // Return false for non FileSystem files or if file doesn't exist.
1984         if (!f.exists()) {
1985             return false;
1986         }
1987 
1988         try {
1989             if (f instanceof ShellFolder) {
1990                 return f.canWrite();
1991             } else {
1992                 if (usesShellFolder(getFileChooser())) {
1993                     try {
1994                         return ShellFolder.getShellFolder(f).canWrite();
1995                     } catch (FileNotFoundException ex) {
1996                         // File doesn't exist
1997                         return false;
1998                     }
1999                 } else {
2000                     // Ordinary file
2001                     return f.canWrite();
2002                 }
2003             }
2004         } catch (SecurityException e) {
2005             return false;
2006         }
2007     }
2008 
2009     /**
2010      * Returns true if specified FileChooser should use ShellFolder
2011      */
usesShellFolder(JFileChooser chooser)2012     public static boolean usesShellFolder(JFileChooser chooser) {
2013         Boolean prop = (Boolean) chooser.getClientProperty("FileChooser.useShellFolder");
2014 
2015         return prop == null ? chooser.getFileSystemView().equals(FileSystemView.getFileSystemView())
2016                 : prop.booleanValue();
2017     }
2018 
2019     // This interface is used to access methods in the FileChooserUI
2020     // that are not public.
2021     public interface FileChooserUIAccessor {
getFileChooser()2022         public JFileChooser getFileChooser();
getModel()2023         public BasicDirectoryModel getModel();
createList()2024         public JPanel createList();
createDetailsView()2025         public JPanel createDetailsView();
isDirectorySelected()2026         public boolean isDirectorySelected();
getDirectory()2027         public File getDirectory();
getApproveSelectionAction()2028         public Action getApproveSelectionAction();
getChangeToParentDirectoryAction()2029         public Action getChangeToParentDirectoryAction();
getNewFolderAction()2030         public Action getNewFolderAction();
createDoubleClickListener(JList list)2031         public MouseListener createDoubleClickListener(JList list);
createListSelectionListener()2032         public ListSelectionListener createListSelectionListener();
2033     }
2034 }
2035