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