Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/macosx/classes/com/apple/laf/AquaFileChooserUI.java
41154 views
1
/*
2
* Copyright (c) 2011, 2021, 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 com.apple.laf;
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.Dimension;
33
import java.awt.FlowLayout;
34
import java.awt.Font;
35
import java.awt.FontMetrics;
36
import java.awt.Graphics;
37
import java.awt.Insets;
38
import java.awt.Point;
39
import java.awt.Rectangle;
40
import java.awt.Toolkit;
41
import java.awt.datatransfer.DataFlavor;
42
import java.awt.datatransfer.Transferable;
43
import java.awt.dnd.DnDConstants;
44
import java.awt.dnd.DropTarget;
45
import java.awt.dnd.DropTargetAdapter;
46
import java.awt.dnd.DropTargetDragEvent;
47
import java.awt.dnd.DropTargetDropEvent;
48
import java.awt.event.ActionEvent;
49
import java.awt.event.FocusEvent;
50
import java.awt.event.FocusListener;
51
import java.awt.event.KeyEvent;
52
import java.awt.event.MouseAdapter;
53
import java.awt.event.MouseEvent;
54
import java.awt.event.MouseListener;
55
import java.beans.PropertyChangeEvent;
56
import java.beans.PropertyChangeListener;
57
import java.io.File;
58
import java.net.URI;
59
import java.text.DateFormat;
60
import java.util.Date;
61
import java.util.Locale;
62
import java.util.Objects;
63
import java.util.Vector;
64
65
import javax.swing.AbstractAction;
66
import javax.swing.AbstractListModel;
67
import javax.swing.Action;
68
import javax.swing.Box;
69
import javax.swing.BoxLayout;
70
import javax.swing.ComboBoxModel;
71
import javax.swing.DefaultListSelectionModel;
72
import javax.swing.Icon;
73
import javax.swing.JButton;
74
import javax.swing.JComboBox;
75
import javax.swing.JComponent;
76
import javax.swing.JDialog;
77
import javax.swing.JFileChooser;
78
import javax.swing.JLabel;
79
import javax.swing.JList;
80
import javax.swing.JOptionPane;
81
import javax.swing.JPanel;
82
import javax.swing.JRootPane;
83
import javax.swing.JScrollPane;
84
import javax.swing.JSeparator;
85
import javax.swing.JTable;
86
import javax.swing.JTextField;
87
import javax.swing.KeyStroke;
88
import javax.swing.ListCellRenderer;
89
import javax.swing.ListSelectionModel;
90
import javax.swing.ScrollPaneConstants;
91
import javax.swing.SwingConstants;
92
import javax.swing.SwingUtilities;
93
import javax.swing.UIManager;
94
import javax.swing.border.Border;
95
import javax.swing.event.AncestorEvent;
96
import javax.swing.event.AncestorListener;
97
import javax.swing.event.DocumentEvent;
98
import javax.swing.event.DocumentListener;
99
import javax.swing.event.ListSelectionEvent;
100
import javax.swing.event.ListSelectionListener;
101
import javax.swing.filechooser.FileFilter;
102
import javax.swing.filechooser.FileSystemView;
103
import javax.swing.filechooser.FileView;
104
import javax.swing.plaf.ComponentUI;
105
import javax.swing.plaf.FileChooserUI;
106
import javax.swing.plaf.UIResource;
107
import javax.swing.table.DefaultTableCellRenderer;
108
import javax.swing.table.JTableHeader;
109
import javax.swing.table.TableCellRenderer;
110
import javax.swing.table.TableColumn;
111
import javax.swing.table.TableColumnModel;
112
113
import sun.swing.SwingUtilities2;
114
115
public class AquaFileChooserUI extends FileChooserUI {
116
/* FileView icons */
117
protected Icon directoryIcon = null;
118
protected Icon fileIcon = null;
119
protected Icon computerIcon = null;
120
protected Icon hardDriveIcon = null;
121
protected Icon floppyDriveIcon = null;
122
123
protected Icon upFolderIcon = null;
124
protected Icon homeFolderIcon = null;
125
protected Icon listViewIcon = null;
126
protected Icon detailsViewIcon = null;
127
128
protected int saveButtonMnemonic = 0;
129
protected int openButtonMnemonic = 0;
130
protected int cancelButtonMnemonic = 0;
131
protected int updateButtonMnemonic = 0;
132
protected int helpButtonMnemonic = 0;
133
protected int chooseButtonMnemonic = 0;
134
135
private String saveTitleText = null;
136
private String openTitleText = null;
137
String newFolderTitleText = null;
138
139
protected String saveButtonText = null;
140
protected String openButtonText = null;
141
protected String cancelButtonText = null;
142
protected String updateButtonText = null;
143
protected String helpButtonText = null;
144
protected String newFolderButtonText = null;
145
protected String chooseButtonText = null;
146
147
//private String newFolderErrorSeparator = null;
148
String newFolderErrorText = null;
149
String newFolderExistsErrorText = null;
150
protected String fileDescriptionText = null;
151
protected String directoryDescriptionText = null;
152
153
protected String saveButtonToolTipText = null;
154
protected String openButtonToolTipText = null;
155
protected String cancelButtonToolTipText = null;
156
protected String updateButtonToolTipText = null;
157
protected String helpButtonToolTipText = null;
158
protected String chooseItemButtonToolTipText = null; // Choose anything
159
protected String chooseFolderButtonToolTipText = null; // Choose folder
160
protected String directoryComboBoxToolTipText = null;
161
protected String filenameTextFieldToolTipText = null;
162
protected String filterComboBoxToolTipText = null;
163
protected String openDirectoryButtonToolTipText = null;
164
165
protected String cancelOpenButtonToolTipText = null;
166
protected String cancelSaveButtonToolTipText = null;
167
protected String cancelChooseButtonToolTipText = null;
168
protected String cancelNewFolderButtonToolTipText = null;
169
170
protected String desktopName = null;
171
String newFolderDialogPrompt = null;
172
String newFolderDefaultName = null;
173
private String newFileDefaultName = null;
174
String createButtonText = null;
175
176
JFileChooser filechooser = null;
177
178
private MouseListener doubleClickListener = null;
179
private PropertyChangeListener propertyChangeListener = null;
180
private AncestorListener ancestorListener = null;
181
private DropTarget dragAndDropTarget = null;
182
183
private static final AcceptAllFileFilter acceptAllFileFilter = new AcceptAllFileFilter();
184
185
private AquaFileSystemModel model;
186
187
final AquaFileView fileView = new AquaFileView(this);
188
189
boolean selectionInProgress = false;
190
191
// The accessoryPanel is a container to place the JFileChooser accessory component
192
private JPanel accessoryPanel = null;
193
194
//
195
// ComponentUI Interface Implementation methods
196
//
197
public static ComponentUI createUI(final JComponent c) {
198
return new AquaFileChooserUI((JFileChooser)c);
199
}
200
201
public AquaFileChooserUI(final JFileChooser filechooser) {
202
super();
203
}
204
205
public void installUI(final JComponent c) {
206
accessoryPanel = new JPanel(new BorderLayout());
207
filechooser = (JFileChooser)c;
208
209
createModel();
210
211
installDefaults(filechooser);
212
installComponents(filechooser);
213
installListeners(filechooser);
214
215
AquaUtils.enforceComponentOrientation(filechooser, ComponentOrientation.getOrientation(Locale.getDefault()));
216
}
217
218
public void uninstallUI(final JComponent c) {
219
uninstallListeners(filechooser);
220
uninstallComponents(filechooser);
221
uninstallDefaults(filechooser);
222
223
if (accessoryPanel != null) {
224
accessoryPanel.removeAll();
225
}
226
227
accessoryPanel = null;
228
getFileChooser().removeAll();
229
}
230
231
protected void installListeners(final JFileChooser fc) {
232
doubleClickListener = createDoubleClickListener(fc, fFileList);
233
fFileList.addMouseListener(doubleClickListener);
234
235
propertyChangeListener = createPropertyChangeListener(fc);
236
if (propertyChangeListener != null) {
237
fc.addPropertyChangeListener(propertyChangeListener);
238
}
239
240
ancestorListener = new AncestorListener(){
241
public void ancestorAdded(final AncestorEvent e) {
242
// Request defaultness for the appropriate button based on mode
243
setFocusForMode(getFileChooser());
244
// Request defaultness for the appropriate button based on mode
245
setDefaultButtonForMode(getFileChooser());
246
}
247
248
public void ancestorRemoved(final AncestorEvent e) {
249
}
250
251
public void ancestorMoved(final AncestorEvent e) {
252
}
253
};
254
fc.addAncestorListener(ancestorListener);
255
256
fc.registerKeyboardAction(new CancelSelectionAction(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
257
dragAndDropTarget = new DropTarget(fc, DnDConstants.ACTION_COPY, new DnDHandler(), true);
258
fc.setDropTarget(dragAndDropTarget);
259
}
260
261
protected void uninstallListeners(final JFileChooser fc) {
262
if (propertyChangeListener != null) {
263
fc.removePropertyChangeListener(propertyChangeListener);
264
}
265
fFileList.removeMouseListener(doubleClickListener);
266
fc.removePropertyChangeListener(filterComboBoxModel);
267
fc.removePropertyChangeListener(model);
268
fc.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
269
fc.removeAncestorListener(ancestorListener);
270
fc.setDropTarget(null);
271
ancestorListener = null;
272
}
273
274
protected void installDefaults(final JFileChooser fc) {
275
installIcons(fc);
276
installStrings(fc);
277
setPackageIsTraversable(fc.getClientProperty(PACKAGE_TRAVERSABLE_PROPERTY));
278
setApplicationIsTraversable(fc.getClientProperty(APPLICATION_TRAVERSABLE_PROPERTY));
279
}
280
281
protected void installIcons(final JFileChooser fc) {
282
directoryIcon = UIManager.getIcon("FileView.directoryIcon");
283
fileIcon = UIManager.getIcon("FileView.fileIcon");
284
computerIcon = UIManager.getIcon("FileView.computerIcon");
285
hardDriveIcon = UIManager.getIcon("FileView.hardDriveIcon");
286
}
287
288
String getString(final String uiKey, final String fallback) {
289
final String result = UIManager.getString(uiKey);
290
return (result == null ? fallback : result);
291
}
292
293
protected void installStrings(final JFileChooser fc) {
294
// Exist in basic.properties (though we might want to override)
295
fileDescriptionText = UIManager.getString("FileChooser.fileDescriptionText");
296
directoryDescriptionText = UIManager.getString("FileChooser.directoryDescriptionText");
297
newFolderErrorText = getString("FileChooser.newFolderErrorText", "Error occurred during folder creation");
298
299
saveButtonText = UIManager.getString("FileChooser.saveButtonText");
300
openButtonText = UIManager.getString("FileChooser.openButtonText");
301
cancelButtonText = UIManager.getString("FileChooser.cancelButtonText");
302
updateButtonText = UIManager.getString("FileChooser.updateButtonText");
303
helpButtonText = UIManager.getString("FileChooser.helpButtonText");
304
305
saveButtonMnemonic = UIManager.getInt("FileChooser.saveButtonMnemonic");
306
openButtonMnemonic = UIManager.getInt("FileChooser.openButtonMnemonic");
307
cancelButtonMnemonic = UIManager.getInt("FileChooser.cancelButtonMnemonic");
308
updateButtonMnemonic = UIManager.getInt("FileChooser.updateButtonMnemonic");
309
helpButtonMnemonic = UIManager.getInt("FileChooser.helpButtonMnemonic");
310
chooseButtonMnemonic = UIManager.getInt("FileChooser.chooseButtonMnemonic");
311
312
saveButtonToolTipText = UIManager.getString("FileChooser.saveButtonToolTipText");
313
openButtonToolTipText = UIManager.getString("FileChooser.openButtonToolTipText");
314
cancelButtonToolTipText = UIManager.getString("FileChooser.cancelButtonToolTipText");
315
updateButtonToolTipText = UIManager.getString("FileChooser.updateButtonToolTipText");
316
helpButtonToolTipText = UIManager.getString("FileChooser.helpButtonToolTipText");
317
318
// Mac-specific, but fallback to basic if it's missing
319
saveTitleText = getString("FileChooser.saveTitleText", saveButtonText);
320
openTitleText = getString("FileChooser.openTitleText", openButtonText);
321
322
// Mac-specific, required
323
newFolderExistsErrorText = getString("FileChooser.newFolderExistsErrorText", "That name is already taken");
324
chooseButtonText = getString("FileChooser.chooseButtonText", "Choose");
325
newFolderButtonText = getString("FileChooser.newFolderButtonText", "New");
326
newFolderTitleText = getString("FileChooser.newFolderTitleText", "New Folder");
327
328
if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) {
329
fileNameLabelText = getString("FileChooser.saveDialogFileNameLabelText", "Save As:");
330
} else {
331
fileNameLabelText = getString("FileChooser.fileNameLabelText", "Name:");
332
}
333
334
filesOfTypeLabelText = getString("FileChooser.filesOfTypeLabelText", "Format:");
335
336
desktopName = getString("FileChooser.desktopName", "Desktop");
337
newFolderDialogPrompt = getString("FileChooser.newFolderPromptText", "Name of new folder:");
338
newFolderDefaultName = getString("FileChooser.untitledFolderName", "untitled folder");
339
newFileDefaultName = getString("FileChooser.untitledFileName", "untitled");
340
createButtonText = getString("FileChooser.createButtonText", "Create");
341
342
fColumnNames[1] = getString("FileChooser.byDateText", "Date Modified");
343
fColumnNames[0] = getString("FileChooser.byNameText", "Name");
344
345
// Mac-specific, optional
346
chooseItemButtonToolTipText = UIManager.getString("FileChooser.chooseItemButtonToolTipText");
347
chooseFolderButtonToolTipText = UIManager.getString("FileChooser.chooseFolderButtonToolTipText");
348
openDirectoryButtonToolTipText = UIManager.getString("FileChooser.openDirectoryButtonToolTipText");
349
350
directoryComboBoxToolTipText = UIManager.getString("FileChooser.directoryComboBoxToolTipText");
351
filenameTextFieldToolTipText = UIManager.getString("FileChooser.filenameTextFieldToolTipText");
352
filterComboBoxToolTipText = UIManager.getString("FileChooser.filterComboBoxToolTipText");
353
354
cancelOpenButtonToolTipText = UIManager.getString("FileChooser.cancelOpenButtonToolTipText");
355
cancelSaveButtonToolTipText = UIManager.getString("FileChooser.cancelSaveButtonToolTipText");
356
cancelChooseButtonToolTipText = UIManager.getString("FileChooser.cancelChooseButtonToolTipText");
357
cancelNewFolderButtonToolTipText = UIManager.getString("FileChooser.cancelNewFolderButtonToolTipText");
358
359
newFolderTitleText = UIManager.getString("FileChooser.newFolderTitleText");
360
newFolderToolTipText = UIManager.getString("FileChooser.newFolderToolTipText");
361
newFolderAccessibleName = getString("FileChooser.newFolderAccessibleName", newFolderTitleText);
362
}
363
364
protected void uninstallDefaults(final JFileChooser fc) {
365
uninstallIcons(fc);
366
uninstallStrings(fc);
367
}
368
369
protected void uninstallIcons(final JFileChooser fc) {
370
directoryIcon = null;
371
fileIcon = null;
372
computerIcon = null;
373
hardDriveIcon = null;
374
floppyDriveIcon = null;
375
376
upFolderIcon = null;
377
homeFolderIcon = null;
378
detailsViewIcon = null;
379
listViewIcon = null;
380
}
381
382
protected void uninstallStrings(final JFileChooser fc) {
383
saveTitleText = null;
384
openTitleText = null;
385
newFolderTitleText = null;
386
387
saveButtonText = null;
388
openButtonText = null;
389
cancelButtonText = null;
390
updateButtonText = null;
391
helpButtonText = null;
392
newFolderButtonText = null;
393
chooseButtonText = null;
394
395
cancelOpenButtonToolTipText = null;
396
cancelSaveButtonToolTipText = null;
397
cancelChooseButtonToolTipText = null;
398
cancelNewFolderButtonToolTipText = null;
399
400
saveButtonToolTipText = null;
401
openButtonToolTipText = null;
402
cancelButtonToolTipText = null;
403
updateButtonToolTipText = null;
404
helpButtonToolTipText = null;
405
chooseItemButtonToolTipText = null;
406
chooseFolderButtonToolTipText = null;
407
openDirectoryButtonToolTipText = null;
408
directoryComboBoxToolTipText = null;
409
filenameTextFieldToolTipText = null;
410
filterComboBoxToolTipText = null;
411
412
newFolderDefaultName = null;
413
newFileDefaultName = null;
414
415
desktopName = null;
416
}
417
418
protected void createModel() {
419
}
420
421
AquaFileSystemModel getModel() {
422
return model;
423
}
424
425
/*
426
* Listen for filechooser property changes, such as
427
* the selected file changing, or the type of the dialog changing.
428
*/
429
// Taken almost verbatim from Metal
430
protected PropertyChangeListener createPropertyChangeListener(final JFileChooser fc) {
431
return new PropertyChangeListener(){
432
public void propertyChange(final PropertyChangeEvent e) {
433
final String prop = e.getPropertyName();
434
if (prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
435
final File f = (File)e.getNewValue();
436
if (f != null) {
437
// Select the file in the list if the selected file didn't change as
438
// a result of a list click.
439
if (!selectionInProgress && getModel().contains(f)) {
440
fFileList.setSelectedIndex(getModel().indexOf(f));
441
}
442
443
// [3643835] Need to populate the text field here. No-op on Open dialogs
444
// Note that this was removed for 3514735, but should not have been.
445
if (!f.isDirectory()) {
446
setFileName(getFileChooser().getName(f));
447
}
448
}
449
updateButtonState(getFileChooser());
450
} else if (prop.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {
451
JFileChooser fileChooser = getFileChooser();
452
if (!fileChooser.isDirectorySelectionEnabled()) {
453
final File[] files = (File[]) e.getNewValue();
454
if (files != null) {
455
for (int selectedRow : fFileList.getSelectedRows()) {
456
File file = (File) fFileList.getValueAt(selectedRow, 0);
457
if (fileChooser.isTraversable(file)) {
458
fFileList.removeSelectedIndex(selectedRow);
459
}
460
}
461
}
462
}
463
} else if (prop.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {
464
fFileList.clearSelection();
465
final File currentDirectory = getFileChooser().getCurrentDirectory();
466
if (currentDirectory != null) {
467
fDirectoryComboBoxModel.addItem(currentDirectory);
468
// Enable the newFolder action if the current directory
469
// is writable.
470
// PENDING(jeff) - broken - fix
471
getAction(kNewFolder).setEnabled(currentDirectory.canWrite());
472
}
473
updateButtonState(getFileChooser());
474
} else if (prop.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
475
fFileList.clearSelection();
476
setBottomPanelForMode(getFileChooser()); // Also updates approve button
477
} else if (prop == JFileChooser.ACCESSORY_CHANGED_PROPERTY) {
478
if (getAccessoryPanel() != null) {
479
if (e.getOldValue() != null) {
480
getAccessoryPanel().remove((JComponent)e.getOldValue());
481
}
482
final JComponent accessory = (JComponent)e.getNewValue();
483
if (accessory != null) {
484
getAccessoryPanel().add(accessory, BorderLayout.CENTER);
485
}
486
}
487
} else if (prop == JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY) {
488
updateApproveButton(getFileChooser());
489
getFileChooser().invalidate();
490
} else if (prop == JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY) {
491
if (getFileChooser().getDialogType() == JFileChooser.SAVE_DIALOG) {
492
fileNameLabelText = getString("FileChooser.saveDialogFileNameLabelText", "Save As:");
493
} else {
494
fileNameLabelText = getString("FileChooser.fileNameLabelText", "Name:");
495
}
496
fTextFieldLabel.setText(fileNameLabelText);
497
498
// Mac doesn't show the text field or "new folder" button in 'Open' dialogs
499
setBottomPanelForMode(getFileChooser()); // Also updates approve button
500
} else if (prop.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) {
501
getApproveButton(getFileChooser()).setMnemonic(getApproveButtonMnemonic(getFileChooser()));
502
} else if (prop.equals(PACKAGE_TRAVERSABLE_PROPERTY)) {
503
setPackageIsTraversable(e.getNewValue());
504
} else if (prop.equals(APPLICATION_TRAVERSABLE_PROPERTY)) {
505
setApplicationIsTraversable(e.getNewValue());
506
} else if (prop.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
507
if (getFileChooser().isMultiSelectionEnabled()) {
508
fFileList.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
509
} else {
510
fFileList.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
511
}
512
} else if (prop.equals(JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) {
513
doControlButtonsChanged(e);
514
}
515
}
516
};
517
}
518
519
void setPackageIsTraversable(final Object o) {
520
int newProp = -1;
521
if (o != null && o instanceof String) newProp = parseTraversableProperty((String)o);
522
if (newProp != -1) fPackageIsTraversable = newProp;
523
else fPackageIsTraversable = sGlobalPackageIsTraversable;
524
}
525
526
void setApplicationIsTraversable(final Object o) {
527
int newProp = -1;
528
if (o != null && o instanceof String) newProp = parseTraversableProperty((String)o);
529
if (newProp != -1) fApplicationIsTraversable = newProp;
530
else fApplicationIsTraversable = sGlobalApplicationIsTraversable;
531
}
532
533
void doControlButtonsChanged(final PropertyChangeEvent e) {
534
if (getFileChooser().getControlButtonsAreShown()) {
535
fBottomPanel.add(fDirectoryPanelSpacer);
536
fBottomPanel.add(fDirectoryPanel);
537
} else {
538
fBottomPanel.remove(fDirectoryPanelSpacer);
539
fBottomPanel.remove(fDirectoryPanel);
540
}
541
}
542
543
public String getFileName() {
544
if (filenameTextField != null) { return filenameTextField.getText(); }
545
return null;
546
}
547
548
public String getDirectoryName() {
549
// PENDING(jeff) - get the name from the directory combobox
550
return null;
551
}
552
553
public void setFileName(final String filename) {
554
if (filenameTextField != null) {
555
filenameTextField.setText(filename);
556
}
557
}
558
559
public void setDirectoryName(final String dirname) {
560
// PENDING(jeff) - set the name in the directory combobox
561
}
562
563
public void rescanCurrentDirectory(final JFileChooser fc) {
564
getModel().invalidateFileCache();
565
getModel().validateFileCache();
566
}
567
568
public void ensureFileIsVisible(final JFileChooser fc, final File f) {
569
if (f == null) {
570
fFileList.requestFocusInWindow();
571
fFileList.ensureIndexIsVisible(-1);
572
return;
573
}
574
575
getModel().runWhenDone(new Runnable() {
576
public void run() {
577
fFileList.requestFocusInWindow();
578
fFileList.ensureIndexIsVisible(getModel().indexOf(f));
579
}
580
});
581
}
582
583
public JFileChooser getFileChooser() {
584
return filechooser;
585
}
586
587
public JPanel getAccessoryPanel() {
588
return accessoryPanel;
589
}
590
591
protected JButton getApproveButton(final JFileChooser fc) {
592
return fApproveButton;
593
}
594
595
public int getApproveButtonMnemonic(final JFileChooser fc) {
596
return fSubPanel.getApproveButtonMnemonic(fc);
597
}
598
599
public String getApproveButtonToolTipText(final JFileChooser fc) {
600
return fSubPanel.getApproveButtonToolTipText(fc);
601
}
602
603
public String getApproveButtonText(final JFileChooser fc) {
604
return fSubPanel.getApproveButtonText(fc);
605
}
606
607
protected String getCancelButtonToolTipText(final JFileChooser fc) {
608
return fSubPanel.getCancelButtonToolTipText(fc);
609
}
610
611
// If the item's not selectable, it'll be visible but disabled in the list
612
boolean isSelectableInList(final File f) {
613
return fSubPanel.isSelectableInList(getFileChooser(), f);
614
}
615
616
// Is this a file that the JFileChooser wants?
617
// Directories can be selected in the list regardless of mode
618
boolean isSelectableForMode(final JFileChooser fc, final File f) {
619
if (f == null) return false;
620
final int mode = fc.getFileSelectionMode();
621
if (mode == JFileChooser.FILES_AND_DIRECTORIES) return true;
622
boolean traversable = fc.isTraversable(f);
623
if (mode == JFileChooser.DIRECTORIES_ONLY) return traversable;
624
return !traversable;
625
}
626
627
// ********************************************
628
// ************ Create Listeners **************
629
// ********************************************
630
631
// From Basic
632
public ListSelectionListener createListSelectionListener(final JFileChooser fc) {
633
return new SelectionListener();
634
}
635
636
protected class SelectionListener implements ListSelectionListener {
637
public void valueChanged(final ListSelectionEvent e) {
638
if (e.getValueIsAdjusting()) return;
639
640
File f = null;
641
final int selectedRow = fFileList.getSelectedRow();
642
final JFileChooser chooser = getFileChooser();
643
boolean isSave = (chooser.getDialogType() == JFileChooser.SAVE_DIALOG);
644
if (selectedRow >= 0) {
645
f = (File)fFileList.getValueAt(selectedRow, 0);
646
}
647
648
// Save dialog lists can't be multi select, because all we're selecting is the next folder to open
649
selectionInProgress = true;
650
if (!isSave && chooser.isMultiSelectionEnabled()) {
651
final int[] rows = fFileList.getSelectedRows();
652
int selectableCount = 0;
653
// Double-check that all the list selections are valid for this mode
654
// Directories can be selected in the list regardless of mode
655
if (rows.length > 0) {
656
for (final int element : rows) {
657
if (isSelectableForMode(chooser, (File)fFileList.getValueAt(element, 0))) selectableCount++;
658
}
659
}
660
if (selectableCount > 0) {
661
final File[] files = new File[selectableCount];
662
for (int i = 0, si = 0; i < rows.length; i++) {
663
f = (File)fFileList.getValueAt(rows[i], 0);
664
if (isSelectableForMode(chooser, f)) {
665
if (fileView.isAlias(f)) {
666
f = fileView.resolveAlias(f);
667
}
668
files[si++] = f;
669
}
670
}
671
chooser.setSelectedFiles(files);
672
} else {
673
chooser.setSelectedFiles(null);
674
}
675
} else {
676
chooser.setSelectedFiles(null);
677
chooser.setSelectedFile(f);
678
}
679
selectionInProgress = false;
680
}
681
}
682
683
// When the Save textfield has the focus, the button should say "Save"
684
// Otherwise, it depends on the list selection
685
protected class SaveTextFocusListener implements FocusListener {
686
public void focusGained(final FocusEvent e) {
687
updateButtonState(getFileChooser());
688
}
689
690
// Do nothing, we might be losing focus due to window deactivation
691
public void focusLost(final FocusEvent e) {
692
693
}
694
}
695
696
// When the Save textfield is empty and the button says "Save", it should be disabled
697
// Otherwise, it depends on the list selection
698
protected class SaveTextDocumentListener implements DocumentListener {
699
public void insertUpdate(final DocumentEvent e) {
700
textChanged();
701
}
702
703
public void removeUpdate(final DocumentEvent e) {
704
textChanged();
705
}
706
707
public void changedUpdate(final DocumentEvent e) {
708
709
}
710
711
void textChanged() {
712
updateButtonState(getFileChooser());
713
}
714
}
715
716
// Opens the File object if it's a traversable directory
717
protected boolean openDirectory(final File f) {
718
if (getFileChooser().isTraversable(f)) {
719
fFileList.clearSelection();
720
// Resolve any aliases
721
final File original = fileView.resolveAlias(f);
722
getFileChooser().setCurrentDirectory(original);
723
updateButtonState(getFileChooser());
724
return true;
725
}
726
return false;
727
}
728
729
// From Basic
730
protected class DoubleClickListener extends MouseAdapter {
731
JTableExtension list;
732
733
public DoubleClickListener(final JTableExtension list) {
734
this.list = list;
735
}
736
737
public void mouseClicked(final MouseEvent e) {
738
if (e.getClickCount() != 2) return;
739
740
final int index = list.locationToIndex(e.getPoint());
741
if (index < 0) return;
742
743
final File f = (File)((AquaFileSystemModel)list.getModel()).getElementAt(index);
744
if (openDirectory(f)) return;
745
746
if (!isSelectableInList(f)) return;
747
getFileChooser().approveSelection();
748
}
749
}
750
751
protected MouseListener createDoubleClickListener(final JFileChooser fc, final JTableExtension list) {
752
return new DoubleClickListener(list);
753
}
754
755
// listens for drag events onto the JFileChooser and sets the selected file or directory
756
class DnDHandler extends DropTargetAdapter {
757
public void dragEnter(final DropTargetDragEvent dtde) {
758
tryToAcceptDrag(dtde);
759
}
760
761
public void dragOver(final DropTargetDragEvent dtde) {
762
tryToAcceptDrag(dtde);
763
}
764
765
public void dropActionChanged(final DropTargetDragEvent dtde) {
766
tryToAcceptDrag(dtde);
767
}
768
769
public void drop(final DropTargetDropEvent dtde) {
770
if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
771
handleFileDropEvent(dtde);
772
return;
773
}
774
775
if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor)) {
776
handleStringDropEvent(dtde);
777
return;
778
}
779
}
780
781
protected void tryToAcceptDrag(final DropTargetDragEvent dtde) {
782
if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor) || dtde.isDataFlavorSupported(DataFlavor.stringFlavor)) {
783
dtde.acceptDrag(DnDConstants.ACTION_COPY);
784
return;
785
}
786
787
dtde.rejectDrag();
788
}
789
790
protected void handleFileDropEvent(final DropTargetDropEvent dtde) {
791
dtde.acceptDrop(dtde.getDropAction());
792
final Transferable transferable = dtde.getTransferable();
793
794
try {
795
@SuppressWarnings("unchecked")
796
final java.util.List<File> fileList = (java.util.List<File>)transferable.getTransferData(DataFlavor.javaFileListFlavor);
797
dropFiles(fileList.toArray(new File[fileList.size()]));
798
dtde.dropComplete(true);
799
} catch (final Exception e) {
800
dtde.dropComplete(false);
801
}
802
}
803
804
protected void handleStringDropEvent(final DropTargetDropEvent dtde) {
805
dtde.acceptDrop(dtde.getDropAction());
806
final Transferable transferable = dtde.getTransferable();
807
808
final String stringData;
809
try {
810
stringData = (String)transferable.getTransferData(DataFlavor.stringFlavor);
811
} catch (final Exception e) {
812
dtde.dropComplete(false);
813
return;
814
}
815
816
try {
817
final File fileAsPath = new File(stringData);
818
if (fileAsPath.exists()) {
819
dropFiles(new File[] {fileAsPath});
820
dtde.dropComplete(true);
821
return;
822
}
823
} catch (final Exception e) {
824
// try again
825
}
826
827
try {
828
final File fileAsURI = new File(new URI(stringData));
829
if (fileAsURI.exists()) {
830
dropFiles(new File[] {fileAsURI});
831
dtde.dropComplete(true);
832
return;
833
}
834
} catch (final Exception e) {
835
// nothing more to do
836
}
837
838
dtde.dropComplete(false);
839
}
840
841
protected void dropFiles(final File[] files) {
842
final JFileChooser jfc = getFileChooser();
843
844
if (files.length == 1) {
845
if (files[0].isDirectory()) {
846
jfc.setCurrentDirectory(files[0]);
847
return;
848
}
849
850
if (!isSelectableForMode(jfc, files[0])) {
851
return;
852
}
853
}
854
855
jfc.setSelectedFiles(files);
856
for (final File file : files) {
857
jfc.ensureFileIsVisible(file);
858
}
859
getModel().runWhenDone(new Runnable() {
860
public void run() {
861
final AquaFileSystemModel fileSystemModel = getModel();
862
for (final File element : files) {
863
final int index = fileSystemModel.indexOf(element);
864
if (index >= 0) fFileList.addRowSelectionInterval(index, index);
865
}
866
}
867
});
868
}
869
}
870
871
// FileChooser UI PLAF methods
872
873
/**
874
* Returns the default accept all file filter
875
*/
876
public FileFilter getAcceptAllFileFilter(final JFileChooser fc) {
877
return acceptAllFileFilter;
878
}
879
880
public FileView getFileView(final JFileChooser fc) {
881
return fileView;
882
}
883
884
/**
885
* Returns the title of this dialog
886
*/
887
public String getDialogTitle(final JFileChooser fc) {
888
if (fc.getDialogTitle() == null) {
889
if (getFileChooser().getDialogType() == JFileChooser.OPEN_DIALOG) {
890
return openTitleText;
891
} else if (getFileChooser().getDialogType() == JFileChooser.SAVE_DIALOG) { return saveTitleText; }
892
}
893
return fc.getDialogTitle();
894
}
895
896
// Utility to get the first selected item regardless of whether we're single or multi select
897
File getFirstSelectedItem() {
898
// Get the selected item
899
File selectedFile = null;
900
final int index = fFileList.getSelectedRow();
901
if (index >= 0) {
902
selectedFile = (File)((AquaFileSystemModel)fFileList.getModel()).getElementAt(index);
903
}
904
return selectedFile;
905
}
906
907
// Make a file from the filename
908
File makeFile(final JFileChooser fc, final String filename) {
909
File selectedFile = null;
910
// whitespace is legal on Macs, even on beginning and end of filename
911
if (filename != null && !filename.isEmpty()) {
912
final FileSystemView fs = fc.getFileSystemView();
913
selectedFile = fs.createFileObject(filename);
914
if (!selectedFile.isAbsolute()) {
915
selectedFile = fs.createFileObject(fc.getCurrentDirectory(), filename);
916
}
917
}
918
return selectedFile;
919
}
920
921
// Utility to tell if the textfield has anything in it
922
boolean textfieldIsValid() {
923
final String s = getFileName();
924
return (s != null && !s.isEmpty());
925
}
926
927
// Action to attach to the file list so we can override the default action
928
// of the table for the return key, which is to select the next line.
929
@SuppressWarnings("serial") // Superclass is not serializable across versions
930
protected class DefaultButtonAction extends AbstractAction {
931
public void actionPerformed(final ActionEvent e) {
932
final JRootPane root = AquaFileChooserUI.this.getFileChooser().getRootPane();
933
final JFileChooser fc = AquaFileChooserUI.this.getFileChooser();
934
final JButton owner = root.getDefaultButton();
935
if (owner != null && SwingUtilities.getRootPane(owner) == root && owner.isEnabled()) {
936
owner.doClick(20);
937
} else if (!fc.getControlButtonsAreShown()) {
938
final JButton defaultButton = AquaFileChooserUI.this.fSubPanel.getDefaultButton(fc);
939
940
if (defaultButton != null) {
941
defaultButton.doClick(20);
942
}
943
} else {
944
Toolkit.getDefaultToolkit().beep();
945
}
946
}
947
948
public boolean isEnabled() {
949
return true;
950
}
951
}
952
953
/**
954
* Creates a new folder.
955
*/
956
@SuppressWarnings("serial") // Superclass is not serializable across versions
957
protected class NewFolderAction extends AbstractAction {
958
protected NewFolderAction() {
959
super(newFolderAccessibleName);
960
}
961
962
// Muchlike showInputDialog, but we give it options instead of selectionValues
963
private Object showNewFolderDialog(final Component parentComponent, final Object message, final String title, final int messageType, final Icon icon, final Object[] options, final Object initialSelectionValue) {
964
final JOptionPane pane = new JOptionPane(message, messageType, JOptionPane.OK_CANCEL_OPTION, icon, options, null);
965
966
pane.setWantsInput(true);
967
pane.setInitialSelectionValue(initialSelectionValue);
968
969
final JDialog dialog = pane.createDialog(parentComponent, title);
970
971
pane.selectInitialValue();
972
dialog.setVisible(true);
973
dialog.dispose();
974
975
final Object value = pane.getValue();
976
977
if (value == null || value.equals(cancelButtonText)
978
|| value.equals(JOptionPane.CLOSED_OPTION)) {
979
return null;
980
}
981
return pane.getInputValue();
982
}
983
984
public void actionPerformed(final ActionEvent e) {
985
final JFileChooser fc = getFileChooser();
986
final File currentDirectory = fc.getCurrentDirectory();
987
File newFolder = null;
988
final String[] options = {createButtonText, cancelButtonText};
989
final String filename = (String)showNewFolderDialog(fc, //parentComponent
990
newFolderDialogPrompt, // message
991
newFolderTitleText, // title
992
JOptionPane.PLAIN_MESSAGE, // messageType
993
null, // icon
994
options, // selectionValues
995
newFolderDefaultName); // initialSelectionValue
996
997
if (filename != null) {
998
try {
999
newFolder = fc.getFileSystemView().createFileObject(currentDirectory, filename);
1000
if (newFolder.exists()) {
1001
JOptionPane.showMessageDialog(fc, newFolderExistsErrorText, "", JOptionPane.ERROR_MESSAGE);
1002
return;
1003
}
1004
1005
newFolder.mkdirs();
1006
} catch(final Exception exc) {
1007
JOptionPane.showMessageDialog(fc, newFolderErrorText, "", JOptionPane.ERROR_MESSAGE);
1008
return;
1009
}
1010
1011
openDirectory(newFolder);
1012
}
1013
}
1014
}
1015
1016
/**
1017
* Responds to an Open, Save, or Choose request
1018
*/
1019
@SuppressWarnings("serial") // Superclass is not serializable across versions
1020
protected class ApproveSelectionAction extends AbstractAction {
1021
public void actionPerformed(final ActionEvent e) {
1022
fSubPanel.approveSelection(getFileChooser());
1023
}
1024
}
1025
1026
/**
1027
* Responds to an OpenDirectory request
1028
*/
1029
@SuppressWarnings("serial") // Superclass is not serializable across versions
1030
protected class OpenSelectionAction extends AbstractAction {
1031
public void actionPerformed(final ActionEvent e) {
1032
final int index = fFileList.getSelectedRow();
1033
if (index >= 0) {
1034
final File selectedFile = (File)((AquaFileSystemModel)fFileList.getModel()).getElementAt(index);
1035
if (selectedFile != null) openDirectory(selectedFile);
1036
}
1037
}
1038
}
1039
1040
/**
1041
* Responds to a cancel request.
1042
*/
1043
@SuppressWarnings("serial") // Superclass is not serializable across versions
1044
protected class CancelSelectionAction extends AbstractAction {
1045
public void actionPerformed(final ActionEvent e) {
1046
getFileChooser().cancelSelection();
1047
}
1048
1049
public boolean isEnabled() {
1050
return getFileChooser().isEnabled();
1051
}
1052
}
1053
1054
/**
1055
* Rescans the files in the current directory
1056
*/
1057
@SuppressWarnings("serial") // Superclass is not serializable across versions
1058
protected class UpdateAction extends AbstractAction {
1059
public void actionPerformed(final ActionEvent e) {
1060
final JFileChooser fc = getFileChooser();
1061
fc.setCurrentDirectory(fc.getFileSystemView().createFileObject(getDirectoryName()));
1062
fc.rescanCurrentDirectory();
1063
}
1064
}
1065
1066
// *****************************************
1067
// ***** default AcceptAll file filter *****
1068
// *****************************************
1069
private static class AcceptAllFileFilter extends FileFilter {
1070
public AcceptAllFileFilter() {
1071
}
1072
1073
public boolean accept(final File f) {
1074
return true;
1075
}
1076
1077
public String getDescription() {
1078
return UIManager.getString("FileChooser.acceptAllFileFilterText");
1079
}
1080
}
1081
1082
// Penultimate superclass is JLabel
1083
@SuppressWarnings("serial") // Superclass is not serializable across versions
1084
protected class MacFCTableCellRenderer extends DefaultTableCellRenderer {
1085
boolean fIsSelected = false;
1086
1087
public MacFCTableCellRenderer(final Font f) {
1088
super();
1089
setFont(f);
1090
setIconTextGap(10);
1091
}
1092
1093
public Component getTableCellRendererComponent(final JTable list, final Object value, final boolean isSelected, final boolean cellHasFocus, final int index, final int col) {
1094
super.getTableCellRendererComponent(list, value, isSelected, false, index, col); // No focus border, thanks
1095
fIsSelected = isSelected;
1096
return this;
1097
}
1098
1099
public boolean isSelected() {
1100
return fIsSelected && isEnabled();
1101
}
1102
1103
protected String layoutCL(final JLabel label, final FontMetrics fontMetrics, final String text, final Icon icon, final Rectangle viewR, final Rectangle iconR, final Rectangle textR) {
1104
return SwingUtilities.layoutCompoundLabel(label, fontMetrics, text, icon, label.getVerticalAlignment(), label.getHorizontalAlignment(), label.getVerticalTextPosition(), label.getHorizontalTextPosition(), viewR, iconR, textR, label.getIconTextGap());
1105
}
1106
1107
protected void paintComponent(final Graphics g) {
1108
final String text = getText();
1109
Icon icon = getIcon();
1110
if (icon != null && !isEnabled()) {
1111
final Icon disabledIcon = getDisabledIcon();
1112
if (disabledIcon != null) icon = disabledIcon;
1113
}
1114
1115
if ((icon == null) && (text == null)) { return; }
1116
1117
// from ComponentUI update
1118
g.setColor(getBackground());
1119
g.fillRect(0, 0, getWidth(), getHeight());
1120
1121
// from BasicLabelUI paint
1122
final FontMetrics fm = g.getFontMetrics();
1123
Insets paintViewInsets = getInsets(null);
1124
paintViewInsets.left += 10;
1125
1126
Rectangle paintViewR = new Rectangle(paintViewInsets.left, paintViewInsets.top, getWidth() - (paintViewInsets.left + paintViewInsets.right), getHeight() - (paintViewInsets.top + paintViewInsets.bottom));
1127
1128
Rectangle paintIconR = new Rectangle();
1129
Rectangle paintTextR = new Rectangle();
1130
1131
final String clippedText = layoutCL(this, fm, text, icon, paintViewR, paintIconR, paintTextR);
1132
1133
if (icon != null) {
1134
icon.paintIcon(this, g, paintIconR.x + 5, paintIconR.y);
1135
}
1136
1137
if (text != null) {
1138
final int textX = paintTextR.x;
1139
final int textY = paintTextR.y + fm.getAscent() + 1;
1140
if (isEnabled()) {
1141
// Color background = fIsSelected ? getForeground() : getBackground();
1142
final Color background = getBackground();
1143
1144
g.setColor(background);
1145
g.fillRect(textX - 1, paintTextR.y, paintTextR.width + 2, fm.getAscent() + 2);
1146
1147
g.setColor(getForeground());
1148
SwingUtilities2.drawString(filechooser, g, clippedText, textX, textY);
1149
} else {
1150
final Color background = getBackground();
1151
g.setColor(background);
1152
g.fillRect(textX - 1, paintTextR.y, paintTextR.width + 2, fm.getAscent() + 2);
1153
1154
g.setColor(background.brighter());
1155
SwingUtilities2.drawString(filechooser, g, clippedText, textX, textY);
1156
g.setColor(background.darker());
1157
SwingUtilities2.drawString(filechooser, g, clippedText, textX + 1, textY + 1);
1158
}
1159
}
1160
}
1161
1162
}
1163
1164
@SuppressWarnings("serial") // Superclass is not serializable across versions
1165
protected class FileRenderer extends MacFCTableCellRenderer {
1166
public FileRenderer(final Font f) {
1167
super(f);
1168
}
1169
1170
public Component getTableCellRendererComponent(final JTable list,
1171
final Object value,
1172
final boolean isSelected,
1173
final boolean cellHasFocus,
1174
final int index,
1175
final int col) {
1176
super.getTableCellRendererComponent(list, value, isSelected, false,
1177
index,
1178
col); // No focus border, thanks
1179
final File file = (File)value;
1180
final JFileChooser fc = getFileChooser();
1181
setText(fc.getName(file));
1182
setIcon(fc.getIcon(file));
1183
setEnabled(isSelectableInList(file));
1184
return this;
1185
}
1186
}
1187
1188
@SuppressWarnings("serial") // Superclass is not serializable across versions
1189
protected class DateRenderer extends MacFCTableCellRenderer {
1190
public DateRenderer(final Font f) {
1191
super(f);
1192
}
1193
1194
public Component getTableCellRendererComponent(final JTable list,
1195
final Object value,
1196
final boolean isSelected,
1197
final boolean cellHasFocus,
1198
final int index,
1199
final int col) {
1200
super.getTableCellRendererComponent(list, value, isSelected, false,
1201
index, col);
1202
final File file = (File)fFileList.getValueAt(index, 0);
1203
setEnabled(isSelectableInList(file));
1204
final DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.SHORT);
1205
final Date date = (Date)value;
1206
1207
if (date != null) {
1208
setText(formatter.format(date));
1209
} else {
1210
setText("");
1211
}
1212
1213
return this;
1214
}
1215
}
1216
1217
@Override
1218
public Dimension getPreferredSize(final JComponent c) {
1219
return new Dimension(PREF_WIDTH, PREF_HEIGHT);
1220
}
1221
1222
@Override
1223
public Dimension getMinimumSize(final JComponent c) {
1224
return new Dimension(MIN_WIDTH, MIN_HEIGHT);
1225
}
1226
1227
@Override
1228
public Dimension getMaximumSize(final JComponent c) {
1229
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1230
}
1231
1232
@SuppressWarnings("serial") // anonymous class
1233
protected ListCellRenderer<File> createDirectoryComboBoxRenderer(final JFileChooser fc) {
1234
return new AquaComboBoxRendererInternal<File>(directoryComboBox) {
1235
public Component getListCellRendererComponent(final JList<? extends File> list,
1236
final File directory,
1237
final int index,
1238
final boolean isSelected,
1239
final boolean cellHasFocus) {
1240
super.getListCellRendererComponent(list, directory, index, isSelected, cellHasFocus);
1241
if (directory == null) {
1242
setText("");
1243
return this;
1244
}
1245
1246
final JFileChooser chooser = getFileChooser();
1247
setText(chooser.getName(directory));
1248
setIcon(chooser.getIcon(directory));
1249
return this;
1250
}
1251
};
1252
}
1253
1254
//
1255
// DataModel for DirectoryComboxbox
1256
//
1257
protected DirectoryComboBoxModel createDirectoryComboBoxModel(final JFileChooser fc) {
1258
return new DirectoryComboBoxModel();
1259
}
1260
1261
/**
1262
* Data model for a type-face selection combo-box.
1263
*/
1264
@SuppressWarnings("serial") // Superclass is not serializable across versions
1265
protected class DirectoryComboBoxModel extends AbstractListModel<File> implements ComboBoxModel<File> {
1266
Vector<File> fDirectories = new Vector<File>();
1267
int topIndex = -1;
1268
int fPathCount = 0;
1269
1270
File fSelectedDirectory = null;
1271
1272
public DirectoryComboBoxModel() {
1273
super();
1274
// Add the current directory to the model, and make it the
1275
// selectedDirectory
1276
addItem(getFileChooser().getCurrentDirectory());
1277
}
1278
1279
/**
1280
* Removes the selected directory, and clears out the
1281
* path file entries leading up to that directory.
1282
*/
1283
private void removeSelectedDirectory() {
1284
fDirectories.removeAllElements();
1285
fPathCount = 0;
1286
fSelectedDirectory = null;
1287
// dump();
1288
}
1289
1290
/**
1291
* Adds the directory to the model and sets it to be selected,
1292
* additionally clears out the previous selected directory and
1293
* the paths leading up to it, if any.
1294
*/
1295
void addItem(final File directory) {
1296
if (directory == null) { return; }
1297
if (fSelectedDirectory != null) {
1298
removeSelectedDirectory();
1299
}
1300
1301
// create File instances of each directory leading up to the top
1302
File f = directory.getAbsoluteFile();
1303
final Vector<File> path = new Vector<File>(10);
1304
while (f.getParent() != null) {
1305
path.addElement(f);
1306
f = getFileChooser().getFileSystemView().createFileObject(f.getParent());
1307
};
1308
1309
// Add root file (the desktop) to the model
1310
final File[] roots = getFileChooser().getFileSystemView().getRoots();
1311
for (final File element : roots) {
1312
path.addElement(element);
1313
}
1314
fPathCount = path.size();
1315
1316
// insert all the path fDirectories leading up to the
1317
// selected directory in reverse order (current directory at top)
1318
for (int i = 0; i < path.size(); i++) {
1319
fDirectories.addElement(path.elementAt(i));
1320
}
1321
1322
setSelectedItem(fDirectories.elementAt(0));
1323
1324
// dump();
1325
}
1326
1327
public void setSelectedItem(final Object selectedDirectory) {
1328
this.fSelectedDirectory = (File)selectedDirectory;
1329
fireContentsChanged(this, -1, -1);
1330
}
1331
1332
public Object getSelectedItem() {
1333
return fSelectedDirectory;
1334
}
1335
1336
public int getSize() {
1337
return fDirectories.size();
1338
}
1339
1340
public File getElementAt(final int index) {
1341
return fDirectories.elementAt(index);
1342
}
1343
}
1344
1345
//
1346
// Renderer for Types ComboBox
1347
//
1348
@SuppressWarnings("serial") // anonymous class
1349
protected ListCellRenderer<FileFilter> createFilterComboBoxRenderer() {
1350
return new AquaComboBoxRendererInternal<FileFilter>(filterComboBox) {
1351
public Component getListCellRendererComponent(final JList<? extends FileFilter> list,
1352
final FileFilter filter,
1353
final int index,
1354
final boolean isSelected,
1355
final boolean cellHasFocus) {
1356
super.getListCellRendererComponent(list, filter, index, isSelected, cellHasFocus);
1357
if (filter != null) setText(filter.getDescription());
1358
return this;
1359
}
1360
};
1361
}
1362
1363
//
1364
// DataModel for Types Comboxbox
1365
//
1366
protected FilterComboBoxModel createFilterComboBoxModel() {
1367
return new FilterComboBoxModel();
1368
}
1369
1370
/**
1371
* Data model for a type-face selection combo-box.
1372
*/
1373
@SuppressWarnings("serial") // Superclass is not serializable across versions
1374
protected class FilterComboBoxModel extends AbstractListModel<FileFilter> implements ComboBoxModel<FileFilter>,
1375
PropertyChangeListener {
1376
protected FileFilter[] filters;
1377
Object oldFileFilter = getFileChooser().getFileFilter();
1378
1379
protected FilterComboBoxModel() {
1380
super();
1381
filters = getFileChooser().getChoosableFileFilters();
1382
}
1383
1384
public void propertyChange(PropertyChangeEvent e) {
1385
String prop = e.getPropertyName();
1386
if(prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) {
1387
filters = (FileFilter[]) e.getNewValue();
1388
fireContentsChanged(this, -1, -1);
1389
} else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) {
1390
setSelectedItem(e.getNewValue());
1391
}
1392
}
1393
1394
public void setSelectedItem(Object filter) {
1395
if (filter != null && !isSelectedFileFilterInModel(filter)) {
1396
oldFileFilter = filter;
1397
getFileChooser().setFileFilter((FileFilter) filter);
1398
fireContentsChanged(this, -1, -1);
1399
}
1400
}
1401
1402
private boolean isSelectedFileFilterInModel(Object filter) {
1403
return Objects.equals(filter, oldFileFilter);
1404
}
1405
1406
public Object getSelectedItem() {
1407
// Ensure that the current filter is in the list.
1408
// NOTE: we shouldnt' have to do this, since JFileChooser adds
1409
// the filter to the choosable filters list when the filter
1410
// is set. Lets be paranoid just in case someone overrides
1411
// setFileFilter in JFileChooser.
1412
FileFilter currentFilter = getFileChooser().getFileFilter();
1413
boolean found = false;
1414
if(currentFilter != null) {
1415
for (FileFilter filter : filters) {
1416
if (filter == currentFilter) {
1417
found = true;
1418
}
1419
}
1420
if(found == false) {
1421
getFileChooser().addChoosableFileFilter(currentFilter);
1422
}
1423
}
1424
return getFileChooser().getFileFilter();
1425
}
1426
1427
public int getSize() {
1428
if(filters != null) {
1429
return filters.length;
1430
} else {
1431
return 0;
1432
}
1433
}
1434
1435
public FileFilter getElementAt(int index) {
1436
if(index > getSize() - 1) {
1437
// This shouldn't happen. Try to recover gracefully.
1438
return getFileChooser().getFileFilter();
1439
}
1440
if(filters != null) {
1441
return filters[index];
1442
} else {
1443
return null;
1444
}
1445
}
1446
}
1447
1448
private boolean containsFileFilter(Object fileFilter) {
1449
return Objects.equals(fileFilter, getFileChooser().getFileFilter());
1450
}
1451
1452
/**
1453
* Acts when FilterComboBox has changed the selected item.
1454
*/
1455
@SuppressWarnings("serial") // Superclass is not serializable across versions
1456
protected class FilterComboBoxAction extends AbstractAction {
1457
protected FilterComboBoxAction() {
1458
super("FilterComboBoxAction");
1459
}
1460
1461
public void actionPerformed(final ActionEvent e) {
1462
Object selectedFilter = filterComboBox.getSelectedItem();
1463
if (!containsFileFilter(selectedFilter)) {
1464
getFileChooser().setFileFilter((FileFilter) selectedFilter);
1465
}
1466
}
1467
}
1468
1469
/**
1470
* Acts when DirectoryComboBox has changed the selected item.
1471
*/
1472
@SuppressWarnings("serial") // Superclass is not serializable across versions
1473
protected class DirectoryComboBoxAction extends AbstractAction {
1474
protected DirectoryComboBoxAction() {
1475
super("DirectoryComboBoxAction");
1476
}
1477
1478
public void actionPerformed(final ActionEvent e) {
1479
getFileChooser().setCurrentDirectory((File)directoryComboBox.getSelectedItem());
1480
}
1481
}
1482
1483
// Sorting Table operations
1484
@SuppressWarnings("serial") // Superclass is not serializable across versions
1485
class JSortingTableHeader extends JTableHeader {
1486
public JSortingTableHeader(final TableColumnModel cm) {
1487
super(cm);
1488
setReorderingAllowed(true); // This causes mousePress to call setDraggedColumn
1489
}
1490
1491
// One sort state for each column. Both are ascending by default
1492
final boolean[] fSortAscending = {true, true};
1493
1494
// Instead of dragging, it selects which one to sort by
1495
public void setDraggedColumn(final TableColumn aColumn) {
1496
if (aColumn != null) {
1497
final int colIndex = aColumn.getModelIndex();
1498
if (colIndex != fSortColumn) {
1499
filechooser.firePropertyChange(AquaFileSystemModel.SORT_BY_CHANGED, fSortColumn, colIndex);
1500
fSortColumn = colIndex;
1501
} else {
1502
fSortAscending[colIndex] = !fSortAscending[colIndex];
1503
filechooser.firePropertyChange(AquaFileSystemModel.SORT_ASCENDING_CHANGED, !fSortAscending[colIndex], fSortAscending[colIndex]);
1504
}
1505
// Need to repaint the highlighted column.
1506
repaint();
1507
}
1508
}
1509
1510
// This stops mouseDrags from moving the column
1511
public TableColumn getDraggedColumn() {
1512
return null;
1513
}
1514
1515
protected TableCellRenderer createDefaultRenderer() {
1516
final DefaultTableCellRenderer label = new AquaTableCellRenderer();
1517
label.setHorizontalAlignment(SwingConstants.LEFT);
1518
return label;
1519
}
1520
1521
@SuppressWarnings("serial") // Superclass is not serializable across versions
1522
class AquaTableCellRenderer extends DefaultTableCellRenderer implements UIResource {
1523
public Component getTableCellRendererComponent(final JTable localTable, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
1524
if (localTable != null) {
1525
final JTableHeader header = localTable.getTableHeader();
1526
if (header != null) {
1527
setForeground(header.getForeground());
1528
setBackground(header.getBackground());
1529
setFont(UIManager.getFont("TableHeader.font"));
1530
}
1531
}
1532
1533
setText((value == null) ? "" : value.toString());
1534
1535
// Modify the table "border" to draw smaller, and with the titles in the right position
1536
// and sort indicators, just like an NSSave/Open panel.
1537
final AquaTableHeaderBorder cellBorder = AquaTableHeaderBorder.getListHeaderBorder();
1538
cellBorder.setSelected(column == fSortColumn);
1539
final int horizontalShift = (column == 0 ? 35 : 10);
1540
cellBorder.setHorizontalShift(horizontalShift);
1541
1542
if (column == fSortColumn) {
1543
cellBorder.setSortOrder(fSortAscending[column] ? AquaTableHeaderBorder.SORT_ASCENDING : AquaTableHeaderBorder.SORT_DECENDING);
1544
} else {
1545
cellBorder.setSortOrder(AquaTableHeaderBorder.SORT_NONE);
1546
}
1547
setBorder(cellBorder);
1548
return this;
1549
}
1550
}
1551
}
1552
1553
public void installComponents(final JFileChooser fc) {
1554
JPanel tPanel; // temp panel
1555
// set to a Y BoxLayout. The chooser will be laid out top to bottom.
1556
fc.setLayout(new BoxLayout(fc, BoxLayout.Y_AXIS));
1557
fc.add(Box.createRigidArea(vstrut10));
1558
1559
// construct the top panel
1560
1561
final JPanel topPanel = new JPanel();
1562
topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
1563
fc.add(topPanel);
1564
fc.add(Box.createRigidArea(vstrut10));
1565
1566
// Add the textfield pane
1567
1568
fTextfieldPanel = new JPanel();
1569
fTextfieldPanel.setLayout(new BorderLayout());
1570
// setBottomPanelForMode will make this visible if we need it
1571
fTextfieldPanel.setVisible(false);
1572
topPanel.add(fTextfieldPanel);
1573
1574
tPanel = new JPanel();
1575
tPanel.setLayout(new BoxLayout(tPanel, BoxLayout.Y_AXIS));
1576
final JPanel labelArea = new JPanel();
1577
labelArea.setLayout(new FlowLayout(FlowLayout.CENTER));
1578
fTextFieldLabel = new JLabel(fileNameLabelText);
1579
labelArea.add(fTextFieldLabel);
1580
1581
// text field
1582
filenameTextField = new JTextField();
1583
fTextFieldLabel.setLabelFor(filenameTextField);
1584
filenameTextField.addActionListener(getAction(kOpen));
1585
filenameTextField.addFocusListener(new SaveTextFocusListener());
1586
final Dimension minSize = filenameTextField.getMinimumSize();
1587
Dimension d = new Dimension(250, (int)minSize.getHeight());
1588
filenameTextField.setPreferredSize(d);
1589
filenameTextField.setMaximumSize(d);
1590
labelArea.add(filenameTextField);
1591
final File f = fc.getSelectedFile();
1592
if (f != null) {
1593
setFileName(fc.getName(f));
1594
} else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) {
1595
setFileName(newFileDefaultName);
1596
}
1597
1598
tPanel.add(labelArea);
1599
// separator line
1600
@SuppressWarnings("serial") // anonymous class
1601
final JSeparator sep = new JSeparator(){
1602
public Dimension getPreferredSize() {
1603
return new Dimension(((JComponent)getParent()).getWidth(), 3);
1604
}
1605
};
1606
tPanel.add(Box.createRigidArea(new Dimension(1, 8)));
1607
tPanel.add(sep);
1608
tPanel.add(Box.createRigidArea(new Dimension(1, 7)));
1609
fTextfieldPanel.add(tPanel, BorderLayout.CENTER);
1610
1611
// DirectoryComboBox, left-justified, 200x20 not including drop shadow
1612
directoryComboBox = new JComboBox<>();
1613
directoryComboBox.putClientProperty("JComboBox.lightweightKeyboardNavigation", "Lightweight");
1614
fDirectoryComboBoxModel = createDirectoryComboBoxModel(fc);
1615
directoryComboBox.setModel(fDirectoryComboBoxModel);
1616
directoryComboBox.addActionListener(directoryComboBoxAction);
1617
directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
1618
directoryComboBox.setToolTipText(directoryComboBoxToolTipText);
1619
d = new Dimension(250, (int)directoryComboBox.getMinimumSize().getHeight());
1620
directoryComboBox.setPreferredSize(d);
1621
directoryComboBox.setMaximumSize(d);
1622
topPanel.add(directoryComboBox);
1623
1624
// ************************************** //
1625
// ** Add the directory/Accessory pane ** //
1626
// ************************************** //
1627
final JPanel centerPanel = new JPanel(new BorderLayout());
1628
fc.add(centerPanel);
1629
1630
// Accessory pane (equiv to Preview pane in NavServices)
1631
final JComponent accessory = fc.getAccessory();
1632
if (accessory != null) {
1633
getAccessoryPanel().add(accessory);
1634
}
1635
centerPanel.add(getAccessoryPanel(), BorderLayout.LINE_START);
1636
1637
// Directory list(table), right-justified, resizable
1638
final JPanel p = createList(fc);
1639
p.setMinimumSize(LIST_MIN_SIZE);
1640
centerPanel.add(p, BorderLayout.CENTER);
1641
1642
// ********************************** //
1643
// **** Construct the bottom panel ** //
1644
// ********************************** //
1645
fBottomPanel = new JPanel();
1646
fBottomPanel.setLayout(new BoxLayout(fBottomPanel, BoxLayout.Y_AXIS));
1647
fc.add(fBottomPanel);
1648
1649
// Filter label and combobox.
1650
// I know it's unMaclike, but the filter goes on Directory_only too.
1651
tPanel = new JPanel();
1652
tPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
1653
tPanel.setBorder(AquaGroupBorder.getTitlelessBorder());
1654
final JLabel formatLabel = new JLabel(filesOfTypeLabelText);
1655
tPanel.add(formatLabel);
1656
1657
// Combobox
1658
filterComboBoxModel = createFilterComboBoxModel();
1659
fc.addPropertyChangeListener(filterComboBoxModel);
1660
filterComboBox = new JComboBox<>(filterComboBoxModel);
1661
formatLabel.setLabelFor(filterComboBox);
1662
filterComboBox.setRenderer(createFilterComboBoxRenderer());
1663
d = new Dimension(220, (int)filterComboBox.getMinimumSize().getHeight());
1664
filterComboBox.setPreferredSize(d);
1665
filterComboBox.setMaximumSize(d);
1666
filterComboBox.addActionListener(filterComboBoxAction);
1667
filterComboBox.setOpaque(false);
1668
tPanel.add(filterComboBox);
1669
1670
fBottomPanel.add(tPanel);
1671
1672
// fDirectoryPanel: New, Open, Cancel, Approve buttons, right-justified, 82x22
1673
// (sometimes the NewFolder and OpenFolder buttons are invisible)
1674
fDirectoryPanel = new JPanel();
1675
fDirectoryPanel.setLayout(new BoxLayout(fDirectoryPanel, BoxLayout.PAGE_AXIS));
1676
JPanel directoryPanel = new JPanel(new BorderLayout());
1677
JPanel newFolderButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
1678
newFolderButtonPanel.add(Box.createHorizontalStrut(20));
1679
fNewFolderButton = createNewFolderButton(); // Because we hide it depending on style
1680
newFolderButtonPanel.add(fNewFolderButton);
1681
directoryPanel.add(newFolderButtonPanel, BorderLayout.LINE_START);
1682
JPanel approveCancelButtonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 0, 0));
1683
fOpenButton = createButton(kOpenDirectory, openButtonText);
1684
approveCancelButtonPanel.add(fOpenButton);
1685
approveCancelButtonPanel.add(Box.createHorizontalStrut(8));
1686
fCancelButton = createButton(kCancel, null);
1687
approveCancelButtonPanel.add(fCancelButton);
1688
approveCancelButtonPanel.add(Box.createHorizontalStrut(8));
1689
// The ApproveSelection button
1690
fApproveButton = new JButton();
1691
fApproveButton.addActionListener(fApproveSelectionAction);
1692
approveCancelButtonPanel.add(fApproveButton);
1693
approveCancelButtonPanel.add(Box.createHorizontalStrut(20));
1694
directoryPanel.add(approveCancelButtonPanel, BorderLayout.LINE_END);
1695
fDirectoryPanel.add(Box.createVerticalStrut(5));
1696
fDirectoryPanel.add(directoryPanel);
1697
fDirectoryPanel.add(Box.createVerticalStrut(12));
1698
fDirectoryPanelSpacer = Box.createRigidArea(hstrut10);
1699
1700
if (fc.getControlButtonsAreShown()) {
1701
fBottomPanel.add(fDirectoryPanelSpacer);
1702
fBottomPanel.add(fDirectoryPanel);
1703
}
1704
1705
setBottomPanelForMode(fc); // updates ApproveButtonText etc
1706
1707
// don't create til after the FCSubpanel and buttons are made
1708
filenameTextField.getDocument().addDocumentListener(new SaveTextDocumentListener());
1709
}
1710
1711
void setDefaultButtonForMode(final JFileChooser fc) {
1712
final JButton defaultButton = fSubPanel.getDefaultButton(fc);
1713
final JRootPane root = defaultButton.getRootPane();
1714
if (root != null) {
1715
root.setDefaultButton(defaultButton);
1716
}
1717
}
1718
1719
// Macs start with their focus in text areas if they have them,
1720
// lists otherwise (the other plafs start with the focus on approveButton)
1721
void setFocusForMode(final JFileChooser fc) {
1722
final JComponent focusComponent = fSubPanel.getFocusComponent(fc);
1723
if (focusComponent != null) {
1724
focusComponent.requestFocus();
1725
}
1726
}
1727
1728
// Enable/disable buttons as needed for the current selection/focus state
1729
void updateButtonState(final JFileChooser fc) {
1730
fSubPanel.updateButtonState(fc, getFirstSelectedItem());
1731
updateApproveButton(fc);
1732
}
1733
1734
void updateApproveButton(final JFileChooser chooser) {
1735
fApproveButton.setText(getApproveButtonText(chooser));
1736
fApproveButton.setToolTipText(getApproveButtonToolTipText(chooser));
1737
fApproveButton.setMnemonic(getApproveButtonMnemonic(chooser));
1738
fCancelButton.setToolTipText(getCancelButtonToolTipText(chooser));
1739
}
1740
1741
// Lazy-init the subpanels
1742
synchronized FCSubpanel getSaveFilePanel() {
1743
if (fSaveFilePanel == null) fSaveFilePanel = new SaveFilePanel();
1744
return fSaveFilePanel;
1745
}
1746
1747
synchronized FCSubpanel getOpenFilePanel() {
1748
if (fOpenFilePanel == null) fOpenFilePanel = new OpenFilePanel();
1749
return fOpenFilePanel;
1750
}
1751
1752
synchronized FCSubpanel getOpenDirOrAnyPanel() {
1753
if (fOpenDirOrAnyPanel == null) fOpenDirOrAnyPanel = new OpenDirOrAnyPanel();
1754
return fOpenDirOrAnyPanel;
1755
}
1756
1757
synchronized FCSubpanel getCustomFilePanel() {
1758
if (fCustomFilePanel == null) fCustomFilePanel = new CustomFilePanel();
1759
return fCustomFilePanel;
1760
}
1761
1762
synchronized FCSubpanel getCustomDirOrAnyPanel() {
1763
if (fCustomDirOrAnyPanel == null) fCustomDirOrAnyPanel = new CustomDirOrAnyPanel();
1764
return fCustomDirOrAnyPanel;
1765
}
1766
1767
void setBottomPanelForMode(final JFileChooser fc) {
1768
if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) fSubPanel = getSaveFilePanel();
1769
else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) {
1770
if (fc.getFileSelectionMode() == JFileChooser.FILES_ONLY) fSubPanel = getOpenFilePanel();
1771
else fSubPanel = getOpenDirOrAnyPanel();
1772
} else if (fc.getDialogType() == JFileChooser.CUSTOM_DIALOG) {
1773
if (fc.getFileSelectionMode() == JFileChooser.FILES_ONLY) fSubPanel = getCustomFilePanel();
1774
else fSubPanel = getCustomDirOrAnyPanel();
1775
}
1776
1777
fSubPanel.installPanel(fc, true);
1778
updateApproveButton(fc);
1779
updateButtonState(fc);
1780
setDefaultButtonForMode(fc);
1781
setFocusForMode(fc);
1782
fc.invalidate();
1783
}
1784
1785
// fTextfieldPanel and fDirectoryPanel both have NewFolder buttons; only one should be visible at a time
1786
JButton createNewFolderButton() {
1787
final JButton b = new JButton(newFolderButtonText);
1788
b.setToolTipText(newFolderToolTipText);
1789
b.getAccessibleContext().setAccessibleName(newFolderAccessibleName);
1790
b.setHorizontalTextPosition(SwingConstants.LEFT);
1791
b.setAlignmentX(Component.LEFT_ALIGNMENT);
1792
b.setAlignmentY(Component.CENTER_ALIGNMENT);
1793
b.addActionListener(getAction(kNewFolder));
1794
return b;
1795
}
1796
1797
JButton createButton(final int which, String label) {
1798
if (label == null) label = UIManager.getString(sDataPrefix + sButtonKinds[which] + sButtonData[0]);
1799
final int mnemonic = UIManager.getInt(sDataPrefix + sButtonKinds[which] + sButtonData[1]);
1800
final String tipText = UIManager.getString(sDataPrefix + sButtonKinds[which] + sButtonData[2]);
1801
final JButton b = new JButton(label);
1802
b.setMnemonic(mnemonic);
1803
b.setToolTipText(tipText);
1804
b.addActionListener(getAction(which));
1805
return b;
1806
}
1807
1808
AbstractAction getAction(final int which) {
1809
return fButtonActions[which];
1810
}
1811
1812
public void uninstallComponents(final JFileChooser fc) {
1813
// AquaButtonUI install some listeners to all parents, which means that
1814
// we need to uninstall UI here to remove those listeners, because after
1815
// we remove them from FileChooser we lost the latest reference to them,
1816
// and our standard uninstallUI machinery will not call them.
1817
fApproveButton.getUI().uninstallUI(fApproveButton);
1818
fOpenButton.getUI().uninstallUI(fOpenButton);
1819
fNewFolderButton.getUI().uninstallUI(fNewFolderButton);
1820
fCancelButton.getUI().uninstallUI(fCancelButton);
1821
directoryComboBox.getUI().uninstallUI(directoryComboBox);
1822
filterComboBox.getUI().uninstallUI(filterComboBox);
1823
}
1824
1825
// Consistent with the AppKit NSSavePanel, clicks on a file (not a directory) should populate the text field
1826
// with that file's display name.
1827
protected class FileListMouseListener extends MouseAdapter {
1828
public void mouseClicked(final MouseEvent e) {
1829
final Point p = e.getPoint();
1830
final int row = fFileList.rowAtPoint(p);
1831
final int column = fFileList.columnAtPoint(p);
1832
1833
// The autoscroller can generate drag events outside the Table's range.
1834
if ((column == -1) || (row == -1)) { return; }
1835
1836
final File clickedFile = (File)(fFileList.getValueAt(row, 0));
1837
1838
// rdar://problem/3734130 -- don't populate the text field if this file isn't selectable in this mode.
1839
if (isSelectableForMode(getFileChooser(), clickedFile)) {
1840
// [3188387] Populate the file name field with the selected file name
1841
// [3484163] It should also use the display name, not the actual name.
1842
setFileName(fileView.getName(clickedFile));
1843
}
1844
}
1845
}
1846
1847
protected JPanel createList(final JFileChooser fc) {
1848
// The first part is similar to MetalFileChooserUI.createList - same kind of listeners
1849
final JPanel p = new JPanel(new BorderLayout());
1850
fFileList = new JTableExtension();
1851
fFileList.setToolTipText(null); // Workaround for 2487689
1852
fFileList.addMouseListener(new FileListMouseListener());
1853
model = new AquaFileSystemModel(fc, fFileList, fColumnNames);
1854
final MacListSelectionModel listSelectionModel = new MacListSelectionModel(model);
1855
1856
if (getFileChooser().isMultiSelectionEnabled()) {
1857
listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
1858
} else {
1859
listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1860
}
1861
1862
fFileList.setModel(model);
1863
fFileList.setSelectionModel(listSelectionModel);
1864
fFileList.getSelectionModel().addListSelectionListener(createListSelectionListener(fc));
1865
1866
// Now we're different, because we're a table, not a list
1867
fc.addPropertyChangeListener(model);
1868
fFileList.addFocusListener(new SaveTextFocusListener());
1869
final JTableHeader th = new JSortingTableHeader(fFileList.getColumnModel());
1870
fFileList.setTableHeader(th);
1871
fFileList.setRowMargin(0);
1872
fFileList.setIntercellSpacing(new Dimension(0, 1));
1873
fFileList.setShowVerticalLines(false);
1874
fFileList.setShowHorizontalLines(false);
1875
final Font f = fFileList.getFont(); //ThemeFont.GetThemeFont(AppearanceConstants.kThemeViewsFont);
1876
//fc.setFont(f);
1877
//fFileList.setFont(f);
1878
fFileList.setDefaultRenderer(File.class, new FileRenderer(f));
1879
fFileList.setDefaultRenderer(Date.class, new DateRenderer(f));
1880
final FontMetrics fm = fFileList.getFontMetrics(f);
1881
1882
// Row height isn't based on the renderers. It defaults to 16 so we have to set it
1883
fFileList.setRowHeight(Math.max(fm.getHeight(), fileIcon.getIconHeight() + 2));
1884
1885
// Add a binding for the file list that triggers return and escape
1886
fFileList.registerKeyboardAction(new CancelSelectionAction(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_FOCUSED);
1887
// Add a binding for the file list that triggers the default button (see DefaultButtonAction)
1888
fFileList.registerKeyboardAction(new DefaultButtonAction(), KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_FOCUSED);
1889
fFileList.setDropTarget(dragAndDropTarget);
1890
1891
final JScrollPane scrollpane = new JScrollPane(fFileList, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
1892
scrollpane.setComponentOrientation(ComponentOrientation.getOrientation(Locale.getDefault()));
1893
scrollpane.setCorner(ScrollPaneConstants.UPPER_TRAILING_CORNER, new ScrollPaneCornerPanel());
1894
p.add(scrollpane, BorderLayout.CENTER);
1895
return p;
1896
}
1897
1898
@SuppressWarnings("serial") // Superclass is not serializable across versions
1899
protected class ScrollPaneCornerPanel extends JPanel {
1900
final Border border = UIManager.getBorder("TableHeader.cellBorder");
1901
1902
protected void paintComponent(final Graphics g) {
1903
border.paintBorder(this, g, 0, 0, getWidth() + 1, getHeight());
1904
}
1905
}
1906
1907
JComboBox<File> directoryComboBox;
1908
DirectoryComboBoxModel fDirectoryComboBoxModel;
1909
private final Action directoryComboBoxAction = new DirectoryComboBoxAction();
1910
1911
JTextField filenameTextField;
1912
1913
JTableExtension fFileList;
1914
1915
private FilterComboBoxModel filterComboBoxModel;
1916
JComboBox<FileFilter> filterComboBox;
1917
private final Action filterComboBoxAction = new FilterComboBoxAction();
1918
1919
private static final Dimension hstrut10 = new Dimension(10, 1);
1920
private static final Dimension vstrut10 = new Dimension(1, 10);
1921
1922
private static final int PREF_WIDTH = 550;
1923
private static final int PREF_HEIGHT = 400;
1924
private static final int MIN_WIDTH = 400;
1925
private static final int MIN_HEIGHT = 250;
1926
private static final int LIST_MIN_WIDTH = 400;
1927
private static final int LIST_MIN_HEIGHT = 100;
1928
private static final Dimension LIST_MIN_SIZE = new Dimension(LIST_MIN_WIDTH, LIST_MIN_HEIGHT);
1929
1930
static String fileNameLabelText = null;
1931
JLabel fTextFieldLabel = null;
1932
1933
private static String filesOfTypeLabelText = null;
1934
1935
private static String newFolderToolTipText = null;
1936
static String newFolderAccessibleName = null;
1937
1938
private static final String[] fColumnNames = new String[2];
1939
1940
JPanel fTextfieldPanel; // Filename textfield for Save or Custom
1941
private JPanel fDirectoryPanel; // NewFolder/OpenFolder/Cancel/Approve buttons
1942
private Component fDirectoryPanelSpacer;
1943
private JPanel fBottomPanel; // The panel that holds fDirectoryPanel and filterComboBox
1944
1945
private FCSubpanel fSaveFilePanel = null;
1946
private FCSubpanel fOpenFilePanel = null;
1947
private FCSubpanel fOpenDirOrAnyPanel = null;
1948
private FCSubpanel fCustomFilePanel = null;
1949
private FCSubpanel fCustomDirOrAnyPanel = null;
1950
1951
FCSubpanel fSubPanel = null; // Current FCSubpanel
1952
1953
JButton fApproveButton; // mode-specific behavior is managed by FCSubpanel.approveSelection
1954
JButton fOpenButton; // for Directories
1955
JButton fNewFolderButton; // for fDirectoryPanel
1956
1957
// ToolTip text varies with type of dialog
1958
private JButton fCancelButton;
1959
1960
private final ApproveSelectionAction fApproveSelectionAction = new ApproveSelectionAction();
1961
protected int fSortColumn = 0;
1962
protected int fPackageIsTraversable = -1;
1963
protected int fApplicationIsTraversable = -1;
1964
1965
protected static final int sGlobalPackageIsTraversable;
1966
protected static final int sGlobalApplicationIsTraversable;
1967
1968
protected static final String PACKAGE_TRAVERSABLE_PROPERTY = "JFileChooser.packageIsTraversable";
1969
protected static final String APPLICATION_TRAVERSABLE_PROPERTY = "JFileChooser.appBundleIsTraversable";
1970
protected static final String[] sTraversableProperties = {"always", // Bundle is always traversable
1971
"never", // Bundle is never traversable
1972
"conditional"}; // Bundle is traversable on command click
1973
protected static final int kOpenAlways = 0, kOpenNever = 1, kOpenConditional = 2;
1974
1975
AbstractAction[] fButtonActions = {fApproveSelectionAction, fApproveSelectionAction, new CancelSelectionAction(), new OpenSelectionAction(), null, new NewFolderAction()};
1976
1977
static int parseTraversableProperty(final String s) {
1978
if (s == null) return -1;
1979
for (int i = 0; i < sTraversableProperties.length; i++) {
1980
if (s.equals(sTraversableProperties[i])) return i;
1981
}
1982
return -1;
1983
}
1984
1985
static {
1986
Object o = UIManager.get(PACKAGE_TRAVERSABLE_PROPERTY);
1987
if (o != null && o instanceof String) sGlobalPackageIsTraversable = parseTraversableProperty((String)o);
1988
else sGlobalPackageIsTraversable = kOpenConditional;
1989
1990
o = UIManager.get(APPLICATION_TRAVERSABLE_PROPERTY);
1991
if (o != null && o instanceof String) sGlobalApplicationIsTraversable = parseTraversableProperty((String)o);
1992
else sGlobalApplicationIsTraversable = kOpenConditional;
1993
}
1994
static final String sDataPrefix = "FileChooser.";
1995
static final String[] sButtonKinds = {"openButton", "saveButton", "cancelButton", "openDirectoryButton", "helpButton", "newFolderButton"};
1996
static final String[] sButtonData = {"Text", "Mnemonic", "ToolTipText"};
1997
static final int kOpen = 0, kSave = 1, kCancel = 2, kOpenDirectory = 3, kHelp = 4, kNewFolder = 5;
1998
1999
/*-------
2000
2001
Possible states: Save, {Open, Custom}x{Files, File and Directory, Directory}
2002
--------- */
2003
2004
// This class returns the values for the Custom type, to avoid duplicating code in the two Custom subclasses
2005
abstract class FCSubpanel {
2006
// Install the appropriate panels for this mode
2007
abstract void installPanel(JFileChooser fc, boolean controlButtonsAreShown);
2008
2009
abstract void updateButtonState(JFileChooser fc, File f);
2010
2011
// Can this item be selected?
2012
// if not, it's disabled in the list
2013
boolean isSelectableInList(final JFileChooser fc, final File f) {
2014
if (f == null) return false;
2015
if (fc.getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) return fc.isTraversable(f);
2016
return fc.accept(f);
2017
}
2018
2019
void approveSelection(final JFileChooser fc) {
2020
fc.approveSelection();
2021
}
2022
2023
JButton getDefaultButton(final JFileChooser fc) {
2024
return fApproveButton;
2025
}
2026
2027
// Default to the textfield, panels without one should subclass
2028
JComponent getFocusComponent(final JFileChooser fc) {
2029
return filenameTextField;
2030
}
2031
2032
String getApproveButtonText(final JFileChooser fc) {
2033
// Fallback to "choose"
2034
return this.getApproveButtonText(fc, chooseButtonText);
2035
}
2036
2037
// Try to get the custom text. If none, use the fallback
2038
String getApproveButtonText(final JFileChooser fc, final String fallbackText) {
2039
final String buttonText = fc.getApproveButtonText();
2040
return buttonText != null
2041
? buttonText
2042
: fallbackText;
2043
}
2044
2045
int getApproveButtonMnemonic(final JFileChooser fc) {
2046
// Don't use a default
2047
return fc.getApproveButtonMnemonic();
2048
}
2049
2050
// No fallback
2051
String getApproveButtonToolTipText(final JFileChooser fc) {
2052
return getApproveButtonToolTipText(fc, null);
2053
}
2054
2055
String getApproveButtonToolTipText(final JFileChooser fc, final String fallbackText) {
2056
final String tooltipText = fc.getApproveButtonToolTipText();
2057
return tooltipText != null
2058
? tooltipText
2059
: fallbackText;
2060
}
2061
2062
String getCancelButtonToolTipText(final JFileChooser fc) {
2063
return cancelChooseButtonToolTipText;
2064
}
2065
}
2066
2067
// Custom FILES_ONLY dialog
2068
/*
2069
NavServices Save appearance with Open behavior
2070
Approve button label = Open when list has focus and a directory is selected, Custom otherwise
2071
No OpenDirectory button - Approve button is overloaded
2072
Default button / double click = Approve
2073
Has text field
2074
List - everything is enabled
2075
*/
2076
class CustomFilePanel extends FCSubpanel {
2077
void installPanel(final JFileChooser fc, final boolean controlButtonsAreShown) {
2078
fTextfieldPanel.setVisible(true); // do we really want one in multi-select? It's confusing
2079
fOpenButton.setVisible(false);
2080
fNewFolderButton.setVisible(true);
2081
}
2082
2083
// If the list has focus, the mode depends on the selection
2084
// - directory = open, file = approve
2085
// If something else has focus and we have text, it's approve
2086
// otherwise, it depends on selection again.
2087
boolean inOpenDirectoryMode(final JFileChooser fc, final File f) {
2088
final boolean selectionIsDirectory = (f != null && fc.isTraversable(f));
2089
if (fFileList.hasFocus()) return selectionIsDirectory;
2090
else if (textfieldIsValid()) return false;
2091
return selectionIsDirectory;
2092
}
2093
2094
// The approve button is overloaded to mean OpenDirectory or Save
2095
void approveSelection(final JFileChooser fc) {
2096
File f = getFirstSelectedItem();
2097
if (inOpenDirectoryMode(fc, f)) {
2098
openDirectory(f);
2099
} else {
2100
f = makeFile(fc, getFileName());
2101
if (f != null) {
2102
selectionInProgress = true;
2103
getFileChooser().setSelectedFile(f);
2104
selectionInProgress = false;
2105
}
2106
getFileChooser().approveSelection();
2107
}
2108
}
2109
2110
// The approve button should be enabled
2111
// - if something in the list can be opened
2112
// - if the textfield has something in it
2113
void updateButtonState(final JFileChooser fc, final File f) {
2114
boolean enabled = true;
2115
if (!inOpenDirectoryMode(fc, f)) {
2116
enabled = (f != null) || textfieldIsValid();
2117
}
2118
getApproveButton(fc).setEnabled(enabled);
2119
2120
// The OpenDirectory button should be disabled if there's no directory selected
2121
fOpenButton.setEnabled(f != null && fc.isTraversable(f));
2122
2123
// Update the default button, since we may have disabled the current default.
2124
setDefaultButtonForMode(fc);
2125
}
2126
2127
// everything's enabled, because we don't know what they're doing with them
2128
boolean isSelectableInList(final JFileChooser fc, final File f) {
2129
if (f == null) return false;
2130
return fc.accept(f);
2131
}
2132
2133
String getApproveButtonToolTipText(final JFileChooser fc) {
2134
// The approve Button should have openDirectoryButtonToolTipText when the selection is a folder...
2135
if (inOpenDirectoryMode(fc, getFirstSelectedItem())) return openDirectoryButtonToolTipText;
2136
return super.getApproveButtonToolTipText(fc);
2137
}
2138
}
2139
2140
// All Save dialogs
2141
/*
2142
NavServices Save
2143
Approve button label = Open when list has focus and a directory is selected, Save otherwise
2144
No OpenDirectory button - Approve button is overloaded
2145
Default button / double click = Approve
2146
Has text field
2147
Has NewFolder button (by text field)
2148
List - only traversables are enabled
2149
List is always SINGLE_SELECT
2150
*/
2151
// Subclasses CustomFilePanel because they look alike and have some common behavior
2152
class SaveFilePanel extends CustomFilePanel {
2153
void installPanel(final JFileChooser fc, final boolean controlButtonsAreShown) {
2154
fTextfieldPanel.setVisible(true);
2155
fOpenButton.setVisible(false);
2156
fNewFolderButton.setVisible(true);
2157
}
2158
2159
// only traversables are enabled, regardless of mode
2160
// because all you can do is select the next folder to open
2161
boolean isSelectableInList(final JFileChooser fc, final File f) {
2162
return fc.accept(f) && fc.isTraversable(f);
2163
}
2164
2165
// The approve button means 'approve the file name in the text field.'
2166
void approveSelection(final JFileChooser fc) {
2167
final File f = makeFile(fc, getFileName());
2168
if (f != null) {
2169
selectionInProgress = true;
2170
getFileChooser().setSelectedFile(f);
2171
selectionInProgress = false;
2172
getFileChooser().approveSelection();
2173
}
2174
}
2175
2176
// The approve button should be enabled if the textfield has something in it
2177
void updateButtonState(final JFileChooser fc, final File f) {
2178
final boolean enabled = textfieldIsValid();
2179
getApproveButton(fc).setEnabled(enabled);
2180
}
2181
2182
String getApproveButtonText(final JFileChooser fc) {
2183
// Get the custom text, or fallback to "Save"
2184
return this.getApproveButtonText(fc, saveButtonText);
2185
}
2186
2187
int getApproveButtonMnemonic(final JFileChooser fc) {
2188
return saveButtonMnemonic;
2189
}
2190
2191
String getApproveButtonToolTipText(final JFileChooser fc) {
2192
// The approve Button should have openDirectoryButtonToolTipText when the selection is a folder...
2193
if (inOpenDirectoryMode(fc, getFirstSelectedItem())) return openDirectoryButtonToolTipText;
2194
return this.getApproveButtonToolTipText(fc, saveButtonToolTipText);
2195
}
2196
2197
String getCancelButtonToolTipText(final JFileChooser fc) {
2198
return cancelSaveButtonToolTipText;
2199
}
2200
}
2201
2202
// Open FILES_ONLY
2203
/*
2204
NSOpenPanel-style
2205
Approve button label = Open
2206
Default button / double click = Approve
2207
No text field
2208
No NewFolder button
2209
List - all items are enabled
2210
*/
2211
class OpenFilePanel extends FCSubpanel {
2212
void installPanel(final JFileChooser fc, final boolean controlButtonsAreShown) {
2213
fTextfieldPanel.setVisible(false);
2214
fOpenButton.setVisible(false);
2215
fNewFolderButton.setVisible(false);
2216
setDefaultButtonForMode(fc);
2217
}
2218
2219
boolean inOpenDirectoryMode(final JFileChooser fc, final File f) {
2220
return (f != null && fc.isTraversable(f));
2221
}
2222
2223
// Default to the list
2224
JComponent getFocusComponent(final JFileChooser fc) {
2225
return fFileList;
2226
}
2227
2228
void updateButtonState(final JFileChooser fc, final File f) {
2229
// Button is disabled if there's nothing selected
2230
final boolean enabled = (f != null) && !fc.isTraversable(f);
2231
getApproveButton(fc).setEnabled(enabled);
2232
}
2233
2234
// all items are enabled
2235
boolean isSelectableInList(final JFileChooser fc, final File f) {
2236
return f != null && fc.accept(f);
2237
}
2238
2239
String getApproveButtonText(final JFileChooser fc) {
2240
// Get the custom text, or fallback to "Open"
2241
return this.getApproveButtonText(fc, openButtonText);
2242
}
2243
2244
int getApproveButtonMnemonic(final JFileChooser fc) {
2245
return openButtonMnemonic;
2246
}
2247
2248
String getApproveButtonToolTipText(final JFileChooser fc) {
2249
return this.getApproveButtonToolTipText(fc, openButtonToolTipText);
2250
}
2251
2252
String getCancelButtonToolTipText(final JFileChooser fc) {
2253
return cancelOpenButtonToolTipText;
2254
}
2255
}
2256
2257
// used by open and custom panels for Directory only or files and directories
2258
abstract class DirOrAnyPanel extends FCSubpanel {
2259
void installPanel(final JFileChooser fc, final boolean controlButtonsAreShown) {
2260
fOpenButton.setVisible(false);
2261
}
2262
2263
JButton getDefaultButton(final JFileChooser fc) {
2264
return getApproveButton(fc);
2265
}
2266
2267
void updateButtonState(final JFileChooser fc, final File f) {
2268
// Button is disabled if there's nothing selected
2269
// Approve button is handled by the subclasses
2270
// getApproveButton(fc).setEnabled(f != null);
2271
2272
// The OpenDirectory button should be disabled if there's no directory selected
2273
// - we only check the first item
2274
2275
fOpenButton.setEnabled(false);
2276
setDefaultButtonForMode(fc);
2277
}
2278
}
2279
2280
// Open FILES_AND_DIRECTORIES or DIRECTORIES_ONLY
2281
/*
2282
NavServices Choose
2283
Approve button label = Choose/Custom
2284
Has OpenDirectory button
2285
Default button / double click = OpenDirectory
2286
No text field
2287
List - files are disabled in DIRECTORIES_ONLY
2288
*/
2289
class OpenDirOrAnyPanel extends DirOrAnyPanel {
2290
void installPanel(final JFileChooser fc, final boolean controlButtonsAreShown) {
2291
super.installPanel(fc, controlButtonsAreShown);
2292
fTextfieldPanel.setVisible(false);
2293
fNewFolderButton.setVisible(false);
2294
}
2295
2296
// Default to the list
2297
JComponent getFocusComponent(final JFileChooser fc) {
2298
return fFileList;
2299
}
2300
2301
int getApproveButtonMnemonic(final JFileChooser fc) {
2302
return chooseButtonMnemonic;
2303
}
2304
2305
String getApproveButtonToolTipText(final JFileChooser fc) {
2306
String fallbackText;
2307
if (fc.getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) fallbackText = chooseFolderButtonToolTipText;
2308
else fallbackText = chooseItemButtonToolTipText;
2309
return this.getApproveButtonToolTipText(fc, fallbackText);
2310
}
2311
2312
void updateButtonState(final JFileChooser fc, final File f) {
2313
// Button is disabled if there's nothing selected
2314
getApproveButton(fc).setEnabled(f != null);
2315
super.updateButtonState(fc, f);
2316
}
2317
}
2318
2319
// Custom FILES_AND_DIRECTORIES or DIRECTORIES_ONLY
2320
/*
2321
No NavServices equivalent
2322
Approve button label = user defined or Choose
2323
Has OpenDirectory button
2324
Default button / double click = OpenDirectory
2325
Has text field
2326
Has NewFolder button (by text field)
2327
List - files are disabled in DIRECTORIES_ONLY
2328
*/
2329
class CustomDirOrAnyPanel extends DirOrAnyPanel {
2330
void installPanel(final JFileChooser fc, final boolean controlButtonsAreShown) {
2331
super.installPanel(fc, controlButtonsAreShown);
2332
fTextfieldPanel.setVisible(true);
2333
fNewFolderButton.setVisible(true);
2334
}
2335
2336
// If there's text, make a file and select it
2337
void approveSelection(final JFileChooser fc) {
2338
final File f = makeFile(fc, getFileName());
2339
if (f != null) {
2340
selectionInProgress = true;
2341
getFileChooser().setSelectedFile(f);
2342
selectionInProgress = false;
2343
}
2344
getFileChooser().approveSelection();
2345
}
2346
2347
void updateButtonState(final JFileChooser fc, final File f) {
2348
// Button is disabled if there's nothing selected
2349
getApproveButton(fc).setEnabled(f != null || textfieldIsValid());
2350
super.updateButtonState(fc, f);
2351
}
2352
}
2353
2354
// See FileRenderer - documents in Save dialogs draw disabled, so they shouldn't be selected
2355
@SuppressWarnings("serial") // Superclass is not serializable across versions
2356
class MacListSelectionModel extends DefaultListSelectionModel {
2357
AquaFileSystemModel fModel;
2358
2359
MacListSelectionModel(final AquaFileSystemModel model) {
2360
fModel = model;
2361
}
2362
2363
// Can the file be selected in this mode?
2364
// (files are visible even if they can't be selected)
2365
boolean isSelectableInListIndex(final int index) {
2366
final File file = (File)fModel.getValueAt(index, 0);
2367
return (file != null && isSelectableInList(file));
2368
}
2369
2370
// Make sure everything in the selection interval is valid
2371
void verifySelectionInterval(int index0, int index1, boolean isSetSelection) {
2372
if (index0 > index1) {
2373
final int tmp = index1;
2374
index1 = index0;
2375
index0 = tmp;
2376
}
2377
int start = index0;
2378
int end;
2379
do {
2380
// Find the first selectable file in the range
2381
for (; start <= index1; start++) {
2382
if (isSelectableInListIndex(start)) break;
2383
}
2384
end = -1;
2385
// Find the last selectable file in the range
2386
for (int i = start; i <= index1; i++) {
2387
if (!isSelectableInListIndex(i)) {
2388
break;
2389
}
2390
end = i;
2391
}
2392
// Select the range
2393
if (end >= 0) {
2394
// If setting the selection, do "set" the first time to clear the old one
2395
// after that do "add" to extend it
2396
if (isSetSelection) {
2397
super.setSelectionInterval(start, end);
2398
isSetSelection = false;
2399
} else {
2400
super.addSelectionInterval(start, end);
2401
}
2402
start = end + 1;
2403
} else {
2404
break;
2405
}
2406
} while (start <= index1);
2407
}
2408
2409
public void setAnchorSelectionIndex(final int anchorIndex) {
2410
if (isSelectableInListIndex(anchorIndex)) super.setAnchorSelectionIndex(anchorIndex);
2411
}
2412
2413
public void setLeadSelectionIndex(final int leadIndex) {
2414
if (isSelectableInListIndex(leadIndex)) super.setLeadSelectionIndex(leadIndex);
2415
}
2416
2417
public void setSelectionInterval(final int index0, final int index1) {
2418
if (index0 == -1 || index1 == -1) { return; }
2419
2420
if ((getSelectionMode() == SINGLE_SELECTION) || (index0 == index1)) {
2421
if (isSelectableInListIndex(index1)) super.setSelectionInterval(index1, index1);
2422
} else {
2423
verifySelectionInterval(index0, index1, true);
2424
}
2425
}
2426
2427
public void addSelectionInterval(final int index0, final int index1) {
2428
if (index0 == -1 || index1 == -1) { return; }
2429
2430
if (index0 == index1) {
2431
if (isSelectableInListIndex(index1)) super.addSelectionInterval(index1, index1);
2432
return;
2433
}
2434
2435
if (getSelectionMode() != MULTIPLE_INTERVAL_SELECTION) {
2436
setSelectionInterval(index0, index1);
2437
return;
2438
}
2439
2440
verifySelectionInterval(index0, index1, false);
2441
}
2442
}
2443
2444
// Convenience, to translate from the JList directory view to the Mac-style JTable
2445
// & minimize diffs between this and BasicFileChooserUI
2446
@SuppressWarnings("serial") // Superclass is not serializable across versions
2447
class JTableExtension extends JTable {
2448
public void setSelectedIndex(final int index) {
2449
getSelectionModel().setSelectionInterval(index, index);
2450
}
2451
2452
public void removeSelectedIndex(final int index) {
2453
getSelectionModel().removeSelectionInterval(index, index);
2454
}
2455
2456
public void ensureIndexIsVisible(final int index) {
2457
final Rectangle cellBounds = getCellRect(index, 0, false);
2458
if (cellBounds != null) {
2459
scrollRectToVisible(cellBounds);
2460
}
2461
}
2462
2463
public int locationToIndex(final Point location) {
2464
return rowAtPoint(location);
2465
}
2466
}
2467
}
2468
2469