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