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