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