blob: 3489cd3e4eae06cc5a348580d49fb9f8d708fd5b [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.swing.plaf.metal;
27
28import javax.swing.*;
29import javax.swing.border.EmptyBorder;
30import javax.swing.filechooser.*;
31import javax.swing.event.*;
32import javax.swing.plaf.*;
33import javax.swing.plaf.basic.*;
34import java.awt.*;
35import java.awt.event.*;
36import java.beans.*;
37import java.io.File;
38import java.io.FileNotFoundException;
39import java.io.IOException;
40import java.util.*;
41import javax.accessibility.*;
42
43import sun.awt.shell.ShellFolder;
44import sun.swing.*;
45
46/**
47 * Metal L&F implementation of a FileChooser.
48 *
49 * @author Jeff Dinkins
50 */
51public class MetalFileChooserUI extends BasicFileChooserUI {
52
53 // Much of the Metal UI for JFilechooser is just a copy of
54 // the windows implementation, but using Metal themed buttons, lists,
55 // icons, etc. We are planning a complete rewrite, and hence we've
56 // made most things in this class private.
57
58 private JLabel lookInLabel;
59 private JComboBox directoryComboBox;
60 private DirectoryComboBoxModel directoryComboBoxModel;
61 private Action directoryComboBoxAction = new DirectoryComboBoxAction();
62
63 private FilterComboBoxModel filterComboBoxModel;
64
65 private JTextField fileNameTextField;
66
67 private FilePane filePane;
68 private JToggleButton listViewButton;
69 private JToggleButton detailsViewButton;
70
71 private boolean useShellFolder;
72
73 private JButton approveButton;
74 private JButton cancelButton;
75
76 private JPanel buttonPanel;
77 private JPanel bottomPanel;
78
79 private JComboBox filterComboBox;
80
81 private static final Dimension hstrut5 = new Dimension(5, 1);
82 private static final Dimension hstrut11 = new Dimension(11, 1);
83
84 private static final Dimension vstrut5 = new Dimension(1, 5);
85
86 private static final Insets shrinkwrap = new Insets(0,0,0,0);
87
88 // Preferred and Minimum sizes for the dialog box
89 private static int PREF_WIDTH = 500;
90 private static int PREF_HEIGHT = 326;
91 private static Dimension PREF_SIZE = new Dimension(PREF_WIDTH, PREF_HEIGHT);
92
93 private static int MIN_WIDTH = 500;
94 private static int MIN_HEIGHT = 326;
95 private static Dimension MIN_SIZE = new Dimension(MIN_WIDTH, MIN_HEIGHT);
96
97 private static int LIST_PREF_WIDTH = 405;
98 private static int LIST_PREF_HEIGHT = 135;
99 private static Dimension LIST_PREF_SIZE = new Dimension(LIST_PREF_WIDTH, LIST_PREF_HEIGHT);
100
101 // Labels, mnemonics, and tooltips (oh my!)
102 private int lookInLabelMnemonic = 0;
103 private String lookInLabelText = null;
104 private String saveInLabelText = null;
105
106 private int fileNameLabelMnemonic = 0;
107 private String fileNameLabelText = null;
108 private int folderNameLabelMnemonic = 0;
109 private String folderNameLabelText = null;
110
111 private int filesOfTypeLabelMnemonic = 0;
112 private String filesOfTypeLabelText = null;
113
114 private String upFolderToolTipText = null;
115 private String upFolderAccessibleName = null;
116
117 private String homeFolderToolTipText = null;
118 private String homeFolderAccessibleName = null;
119
120 private String newFolderToolTipText = null;
121 private String newFolderAccessibleName = null;
122
123 private String listViewButtonToolTipText = null;
124 private String listViewButtonAccessibleName = null;
125
126 private String detailsViewButtonToolTipText = null;
127 private String detailsViewButtonAccessibleName = null;
128
129 private AlignedLabel fileNameLabel;
130
131 private void populateFileNameLabel() {
132 if (getFileChooser().getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) {
133 fileNameLabel.setText(folderNameLabelText);
134 fileNameLabel.setDisplayedMnemonic(folderNameLabelMnemonic);
135 } else {
136 fileNameLabel.setText(fileNameLabelText);
137 fileNameLabel.setDisplayedMnemonic(fileNameLabelMnemonic);
138 }
139 }
140
141 //
142 // ComponentUI Interface Implementation methods
143 //
144 public static ComponentUI createUI(JComponent c) {
145 return new MetalFileChooserUI((JFileChooser) c);
146 }
147
148 public MetalFileChooserUI(JFileChooser filechooser) {
149 super(filechooser);
150 }
151
152 public void installUI(JComponent c) {
153 super.installUI(c);
154 }
155
156 public void uninstallComponents(JFileChooser fc) {
157 fc.removeAll();
158 bottomPanel = null;
159 buttonPanel = null;
160 }
161
162 private class MetalFileChooserUIAccessor implements FilePane.FileChooserUIAccessor {
163 public JFileChooser getFileChooser() {
164 return MetalFileChooserUI.this.getFileChooser();
165 }
166
167 public BasicDirectoryModel getModel() {
168 return MetalFileChooserUI.this.getModel();
169 }
170
171 public JPanel createList() {
172 return MetalFileChooserUI.this.createList(getFileChooser());
173 }
174
175 public JPanel createDetailsView() {
176 return MetalFileChooserUI.this.createDetailsView(getFileChooser());
177 }
178
179 public boolean isDirectorySelected() {
180 return MetalFileChooserUI.this.isDirectorySelected();
181 }
182
183 public File getDirectory() {
184 return MetalFileChooserUI.this.getDirectory();
185 }
186
187 public Action getChangeToParentDirectoryAction() {
188 return MetalFileChooserUI.this.getChangeToParentDirectoryAction();
189 }
190
191 public Action getApproveSelectionAction() {
192 return MetalFileChooserUI.this.getApproveSelectionAction();
193 }
194
195 public Action getNewFolderAction() {
196 return MetalFileChooserUI.this.getNewFolderAction();
197 }
198
199 public MouseListener createDoubleClickListener(JList list) {
200 return MetalFileChooserUI.this.createDoubleClickListener(getFileChooser(),
201 list);
202 }
203
204 public ListSelectionListener createListSelectionListener() {
205 return MetalFileChooserUI.this.createListSelectionListener(getFileChooser());
206 }
207
208 public boolean usesShellFolder() {
209 return useShellFolder;
210 }
211 }
212
213 public void installComponents(JFileChooser fc) {
214 FileSystemView fsv = fc.getFileSystemView();
215
216 fc.setBorder(new EmptyBorder(12, 12, 11, 11));
217 fc.setLayout(new BorderLayout(0, 11));
218
219 filePane = new FilePane(new MetalFileChooserUIAccessor());
220 fc.addPropertyChangeListener(filePane);
221
222 updateUseShellFolder();
223
224 // ********************************* //
225 // **** Construct the top panel **** //
226 // ********************************* //
227
228 // Directory manipulation buttons
229 JPanel topPanel = new JPanel(new BorderLayout(11, 0));
230 JPanel topButtonPanel = new JPanel();
231 topButtonPanel.setLayout(new BoxLayout(topButtonPanel, BoxLayout.LINE_AXIS));
232 topPanel.add(topButtonPanel, BorderLayout.AFTER_LINE_ENDS);
233
234 // Add the top panel to the fileChooser
235 fc.add(topPanel, BorderLayout.NORTH);
236
237 // ComboBox Label
238 lookInLabel = new JLabel(lookInLabelText);
239 lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic);
240 topPanel.add(lookInLabel, BorderLayout.BEFORE_LINE_BEGINS);
241
242 // CurrentDir ComboBox
243 directoryComboBox = new JComboBox() {
244 public Dimension getPreferredSize() {
245 Dimension d = super.getPreferredSize();
246 // Must be small enough to not affect total width.
247 d.width = 150;
248 return d;
249 }
250 };
251 directoryComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY,
252 lookInLabelText);
253 directoryComboBox.putClientProperty( "JComboBox.isTableCellEditor", Boolean.TRUE );
254 lookInLabel.setLabelFor(directoryComboBox);
255 directoryComboBoxModel = createDirectoryComboBoxModel(fc);
256 directoryComboBox.setModel(directoryComboBoxModel);
257 directoryComboBox.addActionListener(directoryComboBoxAction);
258 directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
259 directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT);
260 directoryComboBox.setAlignmentY(JComponent.TOP_ALIGNMENT);
261 directoryComboBox.setMaximumRowCount(8);
262
263 topPanel.add(directoryComboBox, BorderLayout.CENTER);
264
265 // Up Button
266 JButton upFolderButton = new JButton(getChangeToParentDirectoryAction());
267 upFolderButton.setText(null);
268 upFolderButton.setIcon(upFolderIcon);
269 upFolderButton.setToolTipText(upFolderToolTipText);
270 upFolderButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
271 upFolderAccessibleName);
272 upFolderButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
273 upFolderButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
274 upFolderButton.setMargin(shrinkwrap);
275
276 topButtonPanel.add(upFolderButton);
277 topButtonPanel.add(Box.createRigidArea(hstrut5));
278
279 // Home Button
280 File homeDir = fsv.getHomeDirectory();
281 String toolTipText = homeFolderToolTipText;
282 if (fsv.isRoot(homeDir)) {
283 toolTipText = getFileView(fc).getName(homeDir); // Probably "Desktop".
284 }
285
286
287
288
289 JButton b = new JButton(homeFolderIcon);
290 b.setToolTipText(toolTipText);
291 b.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
292 homeFolderAccessibleName);
293 b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
294 b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
295 b.setMargin(shrinkwrap);
296
297 b.addActionListener(getGoHomeAction());
298 topButtonPanel.add(b);
299 topButtonPanel.add(Box.createRigidArea(hstrut5));
300
301 // New Directory Button
302 if (!UIManager.getBoolean("FileChooser.readOnly")) {
303 b = new JButton(filePane.getNewFolderAction());
304 b.setText(null);
305 b.setIcon(newFolderIcon);
306 b.setToolTipText(newFolderToolTipText);
307 b.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
308 newFolderAccessibleName);
309 b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
310 b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
311 b.setMargin(shrinkwrap);
312 }
313 topButtonPanel.add(b);
314 topButtonPanel.add(Box.createRigidArea(hstrut5));
315
316 // View button group
317 ButtonGroup viewButtonGroup = new ButtonGroup();
318
319 // List Button
320 listViewButton = new JToggleButton(listViewIcon);
321 listViewButton.setToolTipText(listViewButtonToolTipText);
322 listViewButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
323 listViewButtonAccessibleName);
324 listViewButton.setSelected(true);
325 listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
326 listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
327 listViewButton.setMargin(shrinkwrap);
328 listViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_LIST));
329 topButtonPanel.add(listViewButton);
330 viewButtonGroup.add(listViewButton);
331
332 // Details Button
333 detailsViewButton = new JToggleButton(detailsViewIcon);
334 detailsViewButton.setToolTipText(detailsViewButtonToolTipText);
335 detailsViewButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
336 detailsViewButtonAccessibleName);
337 detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
338 detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
339 detailsViewButton.setMargin(shrinkwrap);
340 detailsViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_DETAILS));
341 topButtonPanel.add(detailsViewButton);
342 viewButtonGroup.add(detailsViewButton);
343
344 filePane.addPropertyChangeListener(new PropertyChangeListener() {
345 public void propertyChange(PropertyChangeEvent e) {
346 if ("viewType".equals(e.getPropertyName())) {
347 int viewType = filePane.getViewType();
348 switch (viewType) {
349 case FilePane.VIEWTYPE_LIST:
350 listViewButton.setSelected(true);
351 break;
352
353 case FilePane.VIEWTYPE_DETAILS:
354 detailsViewButton.setSelected(true);
355 break;
356 }
357 }
358 }
359 });
360
361 // ************************************** //
362 // ******* Add the directory pane ******* //
363 // ************************************** //
364 fc.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS);
365 JComponent accessory = fc.getAccessory();
366 if(accessory != null) {
367 getAccessoryPanel().add(accessory);
368 }
369 filePane.setPreferredSize(LIST_PREF_SIZE);
370 fc.add(filePane, BorderLayout.CENTER);
371
372 // ********************************** //
373 // **** Construct the bottom panel ** //
374 // ********************************** //
375 JPanel bottomPanel = getBottomPanel();
376 bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS));
377 fc.add(bottomPanel, BorderLayout.SOUTH);
378
379 // FileName label and textfield
380 JPanel fileNamePanel = new JPanel();
381 fileNamePanel.setLayout(new BoxLayout(fileNamePanel, BoxLayout.LINE_AXIS));
382 bottomPanel.add(fileNamePanel);
383 bottomPanel.add(Box.createRigidArea(vstrut5));
384
385 fileNameLabel = new AlignedLabel();
386 populateFileNameLabel();
387 fileNamePanel.add(fileNameLabel);
388
389 fileNameTextField = new JTextField(35) {
390 public Dimension getMaximumSize() {
391 return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height);
392 }
393 };
394 fileNamePanel.add(fileNameTextField);
395 fileNameLabel.setLabelFor(fileNameTextField);
396 fileNameTextField.addFocusListener(
397 new FocusAdapter() {
398 public void focusGained(FocusEvent e) {
399 if (!getFileChooser().isMultiSelectionEnabled()) {
400 filePane.clearSelection();
401 }
402 }
403 }
404 );
405 if (fc.isMultiSelectionEnabled()) {
406 setFileName(fileNameString(fc.getSelectedFiles()));
407 } else {
408 setFileName(fileNameString(fc.getSelectedFile()));
409 }
410
411
412 // Filetype label and combobox
413 JPanel filesOfTypePanel = new JPanel();
414 filesOfTypePanel.setLayout(new BoxLayout(filesOfTypePanel, BoxLayout.LINE_AXIS));
415 bottomPanel.add(filesOfTypePanel);
416
417 AlignedLabel filesOfTypeLabel = new AlignedLabel(filesOfTypeLabelText);
418 filesOfTypeLabel.setDisplayedMnemonic(filesOfTypeLabelMnemonic);
419 filesOfTypePanel.add(filesOfTypeLabel);
420
421 filterComboBoxModel = createFilterComboBoxModel();
422 fc.addPropertyChangeListener(filterComboBoxModel);
423 filterComboBox = new JComboBox(filterComboBoxModel);
424 filterComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY,
425 filesOfTypeLabelText);
426 filesOfTypeLabel.setLabelFor(filterComboBox);
427 filterComboBox.setRenderer(createFilterComboBoxRenderer());
428 filesOfTypePanel.add(filterComboBox);
429
430 // buttons
431 getButtonPanel().setLayout(new ButtonAreaLayout());
432
433 approveButton = new JButton(getApproveButtonText(fc));
434 // Note: Metal does not use mnemonics for approve and cancel
435 approveButton.addActionListener(getApproveSelectionAction());
436 approveButton.setToolTipText(getApproveButtonToolTipText(fc));
437 getButtonPanel().add(approveButton);
438
439 cancelButton = new JButton(cancelButtonText);
440 cancelButton.setToolTipText(cancelButtonToolTipText);
441 cancelButton.addActionListener(getCancelSelectionAction());
442 getButtonPanel().add(cancelButton);
443
444 if(fc.getControlButtonsAreShown()) {
445 addControlButtons();
446 }
447
448 groupLabels(new AlignedLabel[] { fileNameLabel, filesOfTypeLabel });
449 }
450
451 private void updateUseShellFolder() {
452 // Decide whether to use the ShellFolder class to populate shortcut
453 // panel and combobox.
454 JFileChooser fc = getFileChooser();
455 Boolean prop =
456 (Boolean)fc.getClientProperty("FileChooser.useShellFolder");
457 if (prop != null) {
458 useShellFolder = prop.booleanValue();
459 } else {
460 useShellFolder = fc.getFileSystemView().equals(FileSystemView.getFileSystemView());
461 }
462 }
463
464 protected JPanel getButtonPanel() {
465 if (buttonPanel == null) {
466 buttonPanel = new JPanel();
467 }
468 return buttonPanel;
469 }
470
471 protected JPanel getBottomPanel() {
472 if(bottomPanel == null) {
473 bottomPanel = new JPanel();
474 }
475 return bottomPanel;
476 }
477
478 protected void installStrings(JFileChooser fc) {
479 super.installStrings(fc);
480
481 Locale l = fc.getLocale();
482
483 lookInLabelMnemonic = UIManager.getInt("FileChooser.lookInLabelMnemonic");
484 lookInLabelText = UIManager.getString("FileChooser.lookInLabelText",l);
485 saveInLabelText = UIManager.getString("FileChooser.saveInLabelText",l);
486
487 fileNameLabelMnemonic = UIManager.getInt("FileChooser.fileNameLabelMnemonic");
488 fileNameLabelText = UIManager.getString("FileChooser.fileNameLabelText",l);
489 folderNameLabelMnemonic = UIManager.getInt("FileChooser.folderNameLabelMnemonic");
490 folderNameLabelText = UIManager.getString("FileChooser.folderNameLabelText",l);
491
492 filesOfTypeLabelMnemonic = UIManager.getInt("FileChooser.filesOfTypeLabelMnemonic");
493 filesOfTypeLabelText = UIManager.getString("FileChooser.filesOfTypeLabelText",l);
494
495 upFolderToolTipText = UIManager.getString("FileChooser.upFolderToolTipText",l);
496 upFolderAccessibleName = UIManager.getString("FileChooser.upFolderAccessibleName",l);
497
498 homeFolderToolTipText = UIManager.getString("FileChooser.homeFolderToolTipText",l);
499 homeFolderAccessibleName = UIManager.getString("FileChooser.homeFolderAccessibleName",l);
500
501 newFolderToolTipText = UIManager.getString("FileChooser.newFolderToolTipText",l);
502 newFolderAccessibleName = UIManager.getString("FileChooser.newFolderAccessibleName",l);
503
504 listViewButtonToolTipText = UIManager.getString("FileChooser.listViewButtonToolTipText",l);
505 listViewButtonAccessibleName = UIManager.getString("FileChooser.listViewButtonAccessibleName",l);
506
507 detailsViewButtonToolTipText = UIManager.getString("FileChooser.detailsViewButtonToolTipText",l);
508 detailsViewButtonAccessibleName = UIManager.getString("FileChooser.detailsViewButtonAccessibleName",l);
509 }
510
511 protected void installListeners(JFileChooser fc) {
512 super.installListeners(fc);
513 ActionMap actionMap = getActionMap();
514 SwingUtilities.replaceUIActionMap(fc, actionMap);
515 }
516
517 protected ActionMap getActionMap() {
518 return createActionMap();
519 }
520
521 protected ActionMap createActionMap() {
522 ActionMap map = new ActionMapUIResource();
523 FilePane.addActionsToMap(map, filePane.getActions());
524 return map;
525 }
526
527 protected JPanel createList(JFileChooser fc) {
528 return filePane.createList();
529 }
530
531 protected JPanel createDetailsView(JFileChooser fc) {
532 return filePane.createDetailsView();
533 }
534
535 /**
536 * Creates a selection listener for the list of files and directories.
537 *
538 * @param fc a <code>JFileChooser</code>
539 * @return a <code>ListSelectionListener</code>
540 */
541 public ListSelectionListener createListSelectionListener(JFileChooser fc) {
542 return super.createListSelectionListener(fc);
543 }
544
545 // Obsolete class, not used in this version.
546 protected class SingleClickListener extends MouseAdapter {
547 public SingleClickListener(JList list) {
548 }
549 }
550
551 // Obsolete class, not used in this version.
552 protected class FileRenderer extends DefaultListCellRenderer {
553 }
554
555 public void uninstallUI(JComponent c) {
556 // Remove listeners
557 c.removePropertyChangeListener(filterComboBoxModel);
558 c.removePropertyChangeListener(filePane);
559 cancelButton.removeActionListener(getCancelSelectionAction());
560 approveButton.removeActionListener(getApproveSelectionAction());
561 fileNameTextField.removeActionListener(getApproveSelectionAction());
562
563 if (filePane != null) {
564 filePane.uninstallUI();
565 filePane = null;
566 }
567
568 super.uninstallUI(c);
569 }
570
571 /**
572 * Returns the preferred size of the specified
573 * <code>JFileChooser</code>.
574 * The preferred size is at least as large,
575 * in both height and width,
576 * as the preferred size recommended
577 * by the file chooser's layout manager.
578 *
579 * @param c a <code>JFileChooser</code>
580 * @return a <code>Dimension</code> specifying the preferred
581 * width and height of the file chooser
582 */
583 public Dimension getPreferredSize(JComponent c) {
584 int prefWidth = PREF_SIZE.width;
585 Dimension d = c.getLayout().preferredLayoutSize(c);
586 if (d != null) {
587 return new Dimension(d.width < prefWidth ? prefWidth : d.width,
588 d.height < PREF_SIZE.height ? PREF_SIZE.height : d.height);
589 } else {
590 return new Dimension(prefWidth, PREF_SIZE.height);
591 }
592 }
593
594 /**
595 * Returns the minimum size of the <code>JFileChooser</code>.
596 *
597 * @param c a <code>JFileChooser</code>
598 * @return a <code>Dimension</code> specifying the minimum
599 * width and height of the file chooser
600 */
601 public Dimension getMinimumSize(JComponent c) {
602 return MIN_SIZE;
603 }
604
605 /**
606 * Returns the maximum size of the <code>JFileChooser</code>.
607 *
608 * @param c a <code>JFileChooser</code>
609 * @return a <code>Dimension</code> specifying the maximum
610 * width and height of the file chooser
611 */
612 public Dimension getMaximumSize(JComponent c) {
613 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
614 }
615
616 private String fileNameString(File file) {
617 if (file == null) {
618 return null;
619 } else {
620 JFileChooser fc = getFileChooser();
621 if ((fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) ||
622 (fc.isDirectorySelectionEnabled() && fc.isFileSelectionEnabled() && fc.getFileSystemView().isFileSystemRoot(file))) {
623 return file.getPath();
624 } else {
625 return file.getName();
626 }
627 }
628 }
629
630 private String fileNameString(File[] files) {
631 StringBuffer buf = new StringBuffer();
632 for (int i = 0; files != null && i < files.length; i++) {
633 if (i > 0) {
634 buf.append(" ");
635 }
636 if (files.length > 1) {
637 buf.append("\"");
638 }
639 buf.append(fileNameString(files[i]));
640 if (files.length > 1) {
641 buf.append("\"");
642 }
643 }
644 return buf.toString();
645 }
646
647 /* The following methods are used by the PropertyChange Listener */
648
649 private void doSelectedFileChanged(PropertyChangeEvent e) {
650 File f = (File) e.getNewValue();
651 JFileChooser fc = getFileChooser();
652 if (f != null
653 && ((fc.isFileSelectionEnabled() && !f.isDirectory())
654 || (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {
655
656 setFileName(fileNameString(f));
657 }
658 }
659
660 private void doSelectedFilesChanged(PropertyChangeEvent e) {
661 File[] files = (File[]) e.getNewValue();
662 JFileChooser fc = getFileChooser();
663 if (files != null
664 && files.length > 0
665 && (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) {
666 setFileName(fileNameString(files));
667 }
668 }
669
670 private void doDirectoryChanged(PropertyChangeEvent e) {
671 JFileChooser fc = getFileChooser();
672 FileSystemView fsv = fc.getFileSystemView();
673
674 clearIconCache();
675 File currentDirectory = fc.getCurrentDirectory();
676 if(currentDirectory != null) {
677 directoryComboBoxModel.addItem(currentDirectory);
678
679 if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
680 if (fsv.isFileSystem(currentDirectory)) {
681 setFileName(currentDirectory.getPath());
682 } else {
683 setFileName(null);
684 }
685 }
686 }
687 }
688
689 private void doFilterChanged(PropertyChangeEvent e) {
690 clearIconCache();
691 }
692
693 private void doFileSelectionModeChanged(PropertyChangeEvent e) {
694 if (fileNameLabel != null) {
695 populateFileNameLabel();
696 }
697 clearIconCache();
698
699 JFileChooser fc = getFileChooser();
700 File currentDirectory = fc.getCurrentDirectory();
701 if (currentDirectory != null
702 && fc.isDirectorySelectionEnabled()
703 && !fc.isFileSelectionEnabled()
704 && fc.getFileSystemView().isFileSystem(currentDirectory)) {
705
706 setFileName(currentDirectory.getPath());
707 } else {
708 setFileName(null);
709 }
710 }
711
712 private void doAccessoryChanged(PropertyChangeEvent e) {
713 if(getAccessoryPanel() != null) {
714 if(e.getOldValue() != null) {
715 getAccessoryPanel().remove((JComponent) e.getOldValue());
716 }
717 JComponent accessory = (JComponent) e.getNewValue();
718 if(accessory != null) {
719 getAccessoryPanel().add(accessory, BorderLayout.CENTER);
720 }
721 }
722 }
723
724 private void doApproveButtonTextChanged(PropertyChangeEvent e) {
725 JFileChooser chooser = getFileChooser();
726 approveButton.setText(getApproveButtonText(chooser));
727 approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
728 }
729
730 private void doDialogTypeChanged(PropertyChangeEvent e) {
731 JFileChooser chooser = getFileChooser();
732 approveButton.setText(getApproveButtonText(chooser));
733 approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
734 if (chooser.getDialogType() == JFileChooser.SAVE_DIALOG) {
735 lookInLabel.setText(saveInLabelText);
736 } else {
737 lookInLabel.setText(lookInLabelText);
738 }
739 }
740
741 private void doApproveButtonMnemonicChanged(PropertyChangeEvent e) {
742 // Note: Metal does not use mnemonics for approve and cancel
743 }
744
745 private void doControlButtonsChanged(PropertyChangeEvent e) {
746 if(getFileChooser().getControlButtonsAreShown()) {
747 addControlButtons();
748 } else {
749 removeControlButtons();
750 }
751 }
752
753 /*
754 * Listen for filechooser property changes, such as
755 * the selected file changing, or the type of the dialog changing.
756 */
757 public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) {
758 return new PropertyChangeListener() {
759 public void propertyChange(PropertyChangeEvent e) {
760 String s = e.getPropertyName();
761 if(s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
762 doSelectedFileChanged(e);
763 } else if (s.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {
764 doSelectedFilesChanged(e);
765 } else if(s.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {
766 doDirectoryChanged(e);
767 } else if(s.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {
768 doFilterChanged(e);
769 } else if(s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
770 doFileSelectionModeChanged(e);
771 } else if(s.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) {
772 doAccessoryChanged(e);
773 } else if (s.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY) ||
774 s.equals(JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) {
775 doApproveButtonTextChanged(e);
776 } else if(s.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY)) {
777 doDialogTypeChanged(e);
778 } else if(s.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) {
779 doApproveButtonMnemonicChanged(e);
780 } else if(s.equals(JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) {
781 doControlButtonsChanged(e);
782 } else if (s.equals("componentOrientation")) {
783 ComponentOrientation o = (ComponentOrientation)e.getNewValue();
784 JFileChooser cc = (JFileChooser)e.getSource();
785 if (o != (ComponentOrientation)e.getOldValue()) {
786 cc.applyComponentOrientation(o);
787 }
788 } else if (s == "FileChooser.useShellFolder") {
789 updateUseShellFolder();
790 doDirectoryChanged(e);
791 } else if (s.equals("ancestor")) {
792 if (e.getOldValue() == null && e.getNewValue() != null) {
793 // Ancestor was added, set initial focus
794 fileNameTextField.selectAll();
795 fileNameTextField.requestFocus();
796 }
797 }
798 }
799 };
800 }
801
802
803 protected void removeControlButtons() {
804 getBottomPanel().remove(getButtonPanel());
805 }
806
807 protected void addControlButtons() {
808 getBottomPanel().add(getButtonPanel());
809 }
810
811 public void ensureFileIsVisible(JFileChooser fc, File f) {
812 filePane.ensureFileIsVisible(fc, f);
813 }
814
815 public void rescanCurrentDirectory(JFileChooser fc) {
816 filePane.rescanCurrentDirectory();
817 }
818
819 public String getFileName() {
820 if (fileNameTextField != null) {
821 return fileNameTextField.getText();
822 } else {
823 return null;
824 }
825 }
826
827 public void setFileName(String filename) {
828 if (fileNameTextField != null) {
829 fileNameTextField.setText(filename);
830 }
831 }
832
833 /**
834 * Property to remember whether a directory is currently selected in the UI.
835 * This is normally called by the UI on a selection event.
836 *
837 * @param directorySelected if a directory is currently selected.
838 * @since 1.4
839 */
840 protected void setDirectorySelected(boolean directorySelected) {
841 super.setDirectorySelected(directorySelected);
842 JFileChooser chooser = getFileChooser();
843 if(directorySelected) {
844 if (approveButton != null) {
845 approveButton.setText(directoryOpenButtonText);
846 approveButton.setToolTipText(directoryOpenButtonToolTipText);
847 }
848 } else {
849 if (approveButton != null) {
850 approveButton.setText(getApproveButtonText(chooser));
851 approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
852 }
853 }
854 }
855
856 public String getDirectoryName() {
857 // PENDING(jeff) - get the name from the directory combobox
858 return null;
859 }
860
861 public void setDirectoryName(String dirname) {
862 // PENDING(jeff) - set the name in the directory combobox
863 }
864
865 protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer(JFileChooser fc) {
866 return new DirectoryComboBoxRenderer();
867 }
868
869 //
870 // Renderer for DirectoryComboBox
871 //
872 class DirectoryComboBoxRenderer extends DefaultListCellRenderer {
873 IndentIcon ii = new IndentIcon();
874 public Component getListCellRendererComponent(JList list, Object value,
875 int index, boolean isSelected,
876 boolean cellHasFocus) {
877
878 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
879
880 if (value == null) {
881 setText("");
882 return this;
883 }
884 File directory = (File)value;
885 setText(getFileChooser().getName(directory));
886 Icon icon = getFileChooser().getIcon(directory);
887 ii.icon = icon;
888 ii.depth = directoryComboBoxModel.getDepth(index);
889 setIcon(ii);
890
891 return this;
892 }
893 }
894
895 final static int space = 10;
896 class IndentIcon implements Icon {
897
898 Icon icon = null;
899 int depth = 0;
900
901 public void paintIcon(Component c, Graphics g, int x, int y) {
902 if (c.getComponentOrientation().isLeftToRight()) {
903 icon.paintIcon(c, g, x+depth*space, y);
904 } else {
905 icon.paintIcon(c, g, x, y);
906 }
907 }
908
909 public int getIconWidth() {
910 return icon.getIconWidth() + depth*space;
911 }
912
913 public int getIconHeight() {
914 return icon.getIconHeight();
915 }
916
917 }
918
919 //
920 // DataModel for DirectoryComboxbox
921 //
922 protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) {
923 return new DirectoryComboBoxModel();
924 }
925
926 /**
927 * Data model for a type-face selection combo-box.
928 */
929 protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {
930 Vector directories = new Vector();
931 int[] depths = null;
932 File selectedDirectory = null;
933 JFileChooser chooser = getFileChooser();
934 FileSystemView fsv = chooser.getFileSystemView();
935
936 public DirectoryComboBoxModel() {
937 // Add the current directory to the model, and make it the
938 // selectedDirectory
939 File dir = getFileChooser().getCurrentDirectory();
940 if(dir != null) {
941 addItem(dir);
942 }
943 }
944
945 /**
946 * Adds the directory to the model and sets it to be selected,
947 * additionally clears out the previous selected directory and
948 * the paths leading up to it, if any.
949 */
950 private void addItem(File directory) {
951
952 if(directory == null) {
953 return;
954 }
955
956 directories.clear();
957
958 File[] baseFolders;
959 if (useShellFolder) {
960 baseFolders = (File[])ShellFolder.get("fileChooserComboBoxFolders");
961 } else {
962 baseFolders = fsv.getRoots();
963 }
964 directories.addAll(Arrays.asList(baseFolders));
965
966 // Get the canonical (full) path. This has the side
967 // benefit of removing extraneous chars from the path,
968 // for example /foo/bar/ becomes /foo/bar
969 File canonical = null;
970 try {
971 canonical = ShellFolder.getNormalizedFile(directory);
972 } catch (IOException e) {
973 // Maybe drive is not ready. Can't abort here.
974 canonical = directory;
975 }
976
977 // create File instances of each directory leading up to the top
978 try {
979 File sf = useShellFolder ? ShellFolder.getShellFolder(canonical)
980 : canonical;
981 File f = sf;
982 Vector path = new Vector(10);
983 do {
984 path.addElement(f);
985 } while ((f = f.getParentFile()) != null);
986
987 int pathCount = path.size();
988 // Insert chain at appropriate place in vector
989 for (int i = 0; i < pathCount; i++) {
990 f = (File)path.get(i);
991 if (directories.contains(f)) {
992 int topIndex = directories.indexOf(f);
993 for (int j = i-1; j >= 0; j--) {
994 directories.insertElementAt(path.get(j), topIndex+i-j);
995 }
996 break;
997 }
998 }
999 calculateDepths();
1000 setSelectedItem(sf);
1001 } catch (FileNotFoundException ex) {
1002 calculateDepths();
1003 }
1004 }
1005
1006 private void calculateDepths() {
1007 depths = new int[directories.size()];
1008 for (int i = 0; i < depths.length; i++) {
1009 File dir = (File)directories.get(i);
1010 File parent = dir.getParentFile();
1011 depths[i] = 0;
1012 if (parent != null) {
1013 for (int j = i-1; j >= 0; j--) {
1014 if (parent.equals((File)directories.get(j))) {
1015 depths[i] = depths[j] + 1;
1016 break;
1017 }
1018 }
1019 }
1020 }
1021 }
1022
1023 public int getDepth(int i) {
1024 return (depths != null && i >= 0 && i < depths.length) ? depths[i] : 0;
1025 }
1026
1027 public void setSelectedItem(Object selectedDirectory) {
1028 this.selectedDirectory = (File)selectedDirectory;
1029 fireContentsChanged(this, -1, -1);
1030 }
1031
1032 public Object getSelectedItem() {
1033 return selectedDirectory;
1034 }
1035
1036 public int getSize() {
1037 return directories.size();
1038 }
1039
1040 public Object getElementAt(int index) {
1041 return directories.elementAt(index);
1042 }
1043 }
1044
1045 //
1046 // Renderer for Types ComboBox
1047 //
1048 protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
1049 return new FilterComboBoxRenderer();
1050 }
1051
1052 /**
1053 * Render different type sizes and styles.
1054 */
1055 public class FilterComboBoxRenderer extends DefaultListCellRenderer {
1056 public Component getListCellRendererComponent(JList list,
1057 Object value, int index, boolean isSelected,
1058 boolean cellHasFocus) {
1059
1060 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1061
1062 if (value != null && value instanceof FileFilter) {
1063 setText(((FileFilter)value).getDescription());
1064 }
1065
1066 return this;
1067 }
1068 }
1069
1070 //
1071 // DataModel for Types Comboxbox
1072 //
1073 protected FilterComboBoxModel createFilterComboBoxModel() {
1074 return new FilterComboBoxModel();
1075 }
1076
1077 /**
1078 * Data model for a type-face selection combo-box.
1079 */
1080 protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener {
1081 protected FileFilter[] filters;
1082 protected FilterComboBoxModel() {
1083 super();
1084 filters = getFileChooser().getChoosableFileFilters();
1085 }
1086
1087 public void propertyChange(PropertyChangeEvent e) {
1088 String prop = e.getPropertyName();
1089 if(prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) {
1090 filters = (FileFilter[]) e.getNewValue();
1091 fireContentsChanged(this, -1, -1);
1092 } else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) {
1093 fireContentsChanged(this, -1, -1);
1094 }
1095 }
1096
1097 public void setSelectedItem(Object filter) {
1098 if(filter != null) {
1099 getFileChooser().setFileFilter((FileFilter) filter);
1100 fireContentsChanged(this, -1, -1);
1101 }
1102 }
1103
1104 public Object getSelectedItem() {
1105 // Ensure that the current filter is in the list.
1106 // NOTE: we shouldnt' have to do this, since JFileChooser adds
1107 // the filter to the choosable filters list when the filter
1108 // is set. Lets be paranoid just in case someone overrides
1109 // setFileFilter in JFileChooser.
1110 FileFilter currentFilter = getFileChooser().getFileFilter();
1111 boolean found = false;
1112 if(currentFilter != null) {
1113 for(int i=0; i < filters.length; i++) {
1114 if(filters[i] == currentFilter) {
1115 found = true;
1116 }
1117 }
1118 if(found == false) {
1119 getFileChooser().addChoosableFileFilter(currentFilter);
1120 }
1121 }
1122 return getFileChooser().getFileFilter();
1123 }
1124
1125 public int getSize() {
1126 if(filters != null) {
1127 return filters.length;
1128 } else {
1129 return 0;
1130 }
1131 }
1132
1133 public Object getElementAt(int index) {
1134 if(index > getSize() - 1) {
1135 // This shouldn't happen. Try to recover gracefully.
1136 return getFileChooser().getFileFilter();
1137 }
1138 if(filters != null) {
1139 return filters[index];
1140 } else {
1141 return null;
1142 }
1143 }
1144 }
1145
1146 public void valueChanged(ListSelectionEvent e) {
1147 JFileChooser fc = getFileChooser();
1148 File f = fc.getSelectedFile();
1149 if (!e.getValueIsAdjusting() && f != null && !getFileChooser().isTraversable(f)) {
1150 setFileName(fileNameString(f));
1151 }
1152 }
1153
1154 /**
1155 * Acts when DirectoryComboBox has changed the selected item.
1156 */
1157 protected class DirectoryComboBoxAction extends AbstractAction {
1158 protected DirectoryComboBoxAction() {
1159 super("DirectoryComboBoxAction");
1160 }
1161
1162 public void actionPerformed(ActionEvent e) {
1163 directoryComboBox.hidePopup();
1164 File f = (File)directoryComboBox.getSelectedItem();
1165 if (!getFileChooser().getCurrentDirectory().equals(f)) {
1166 getFileChooser().setCurrentDirectory(f);
1167 }
1168 }
1169 }
1170
1171 protected JButton getApproveButton(JFileChooser fc) {
1172 return approveButton;
1173 }
1174
1175
1176 /**
1177 * <code>ButtonAreaLayout</code> behaves in a similar manner to
1178 * <code>FlowLayout</code>. It lays out all components from left to
1179 * right, flushed right. The widths of all components will be set
1180 * to the largest preferred size width.
1181 */
1182 private static class ButtonAreaLayout implements LayoutManager {
1183 private int hGap = 5;
1184 private int topMargin = 17;
1185
1186 public void addLayoutComponent(String string, Component comp) {
1187 }
1188
1189 public void layoutContainer(Container container) {
1190 Component[] children = container.getComponents();
1191
1192 if (children != null && children.length > 0) {
1193 int numChildren = children.length;
1194 Dimension[] sizes = new Dimension[numChildren];
1195 Insets insets = container.getInsets();
1196 int yLocation = insets.top + topMargin;
1197 int maxWidth = 0;
1198
1199 for (int counter = 0; counter < numChildren; counter++) {
1200 sizes[counter] = children[counter].getPreferredSize();
1201 maxWidth = Math.max(maxWidth, sizes[counter].width);
1202 }
1203 int xLocation, xOffset;
1204 if (container.getComponentOrientation().isLeftToRight()) {
1205 xLocation = container.getSize().width - insets.left - maxWidth;
1206 xOffset = hGap + maxWidth;
1207 } else {
1208 xLocation = insets.left;
1209 xOffset = -(hGap + maxWidth);
1210 }
1211 for (int counter = numChildren - 1; counter >= 0; counter--) {
1212 children[counter].setBounds(xLocation, yLocation,
1213 maxWidth, sizes[counter].height);
1214 xLocation -= xOffset;
1215 }
1216 }
1217 }
1218
1219 public Dimension minimumLayoutSize(Container c) {
1220 if (c != null) {
1221 Component[] children = c.getComponents();
1222
1223 if (children != null && children.length > 0) {
1224 int numChildren = children.length;
1225 int height = 0;
1226 Insets cInsets = c.getInsets();
1227 int extraHeight = topMargin + cInsets.top + cInsets.bottom;
1228 int extraWidth = cInsets.left + cInsets.right;
1229 int maxWidth = 0;
1230
1231 for (int counter = 0; counter < numChildren; counter++) {
1232 Dimension aSize = children[counter].getPreferredSize();
1233 height = Math.max(height, aSize.height);
1234 maxWidth = Math.max(maxWidth, aSize.width);
1235 }
1236 return new Dimension(extraWidth + numChildren * maxWidth +
1237 (numChildren - 1) * hGap,
1238 extraHeight + height);
1239 }
1240 }
1241 return new Dimension(0, 0);
1242 }
1243
1244 public Dimension preferredLayoutSize(Container c) {
1245 return minimumLayoutSize(c);
1246 }
1247
1248 public void removeLayoutComponent(Component c) { }
1249 }
1250
1251 private static void groupLabels(AlignedLabel[] group) {
1252 for (int i = 0; i < group.length; i++) {
1253 group[i].group = group;
1254 }
1255 }
1256
1257 private class AlignedLabel extends JLabel {
1258 private AlignedLabel[] group;
1259 private int maxWidth = 0;
1260
1261 AlignedLabel() {
1262 super();
1263 setAlignmentX(JComponent.LEFT_ALIGNMENT);
1264 }
1265
1266
1267 AlignedLabel(String text) {
1268 super(text);
1269 setAlignmentX(JComponent.LEFT_ALIGNMENT);
1270 }
1271
1272 public Dimension getPreferredSize() {
1273 Dimension d = super.getPreferredSize();
1274 // Align the width with all other labels in group.
1275 return new Dimension(getMaxWidth() + 11, d.height);
1276 }
1277
1278 private int getMaxWidth() {
1279 if (maxWidth == 0 && group != null) {
1280 int max = 0;
1281 for (int i = 0; i < group.length; i++) {
1282 max = Math.max(group[i].getSuperPreferredWidth(), max);
1283 }
1284 for (int i = 0; i < group.length; i++) {
1285 group[i].maxWidth = max;
1286 }
1287 }
1288 return maxWidth;
1289 }
1290
1291 private int getSuperPreferredWidth() {
1292 return super.getPreferredSize().width;
1293 }
1294 }
1295}