History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/swing/FilePane.java (1 of 18352 files). Show all

Unified · Split

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