       1 /*
       2  * Copyright (c) 2003, 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 package sun.swing.plaf.synth;
      26
      27 import java.awt.*;
      28 import java.awt.event.*;
      29 import java.beans.*;
      30 import java.io.*;
      31 import java.util.*;
      32 import java.security.AccessController;
      33 import java.security.PrivilegedAction;
      34
      35 import javax.swing.*;
      36 import javax.swing.event.*;
      37 import javax.swing.filechooser.*;
      38 import javax.swing.filechooser.FileFilter;
      39 import javax.swing.plaf.basic.*;
      40 import javax.swing.plaf.synth.*;
      41 import javax.swing.plaf.ActionMapUIResource;
      42
      43 import sun.awt.shell.ShellFolder;
      44 import sun.swing.*;
      45
      46 /**
      47  * Synth FileChooserUI implementation.
      48  * <p>
      49  * Note that the classes in the com.sun.java.swing.plaf.synth
      50  * package are not
      51  * part of the core Java APIs. They are a part of Sun's JDK and JRE
      52  * distributions. Although other licensees may choose to distribute
      53  * these classes, developers cannot depend on their availability in
      54  * non-Sun implementations. Additionally this API may change in
      55  * incompatible ways between releases. While this class is public, it
      56  * shoud be considered an implementation detail, and subject to change.
      57  *
      58  * @author Leif Samuelsson
      59  * @author Jeff Dinkins
      60  */
      61 public class SynthFileChooserUIImpl extends SynthFileChooserUI {
      62     private JLabel lookInLabel;
      63     private JComboBox<File> directoryComboBox;
      64     private DirectoryComboBoxModel directoryComboBoxModel;
      65     private Action directoryComboBoxAction = new DirectoryComboBoxAction();
      66
      67     private FilterComboBoxModel filterComboBoxModel;
      68
      69     private JTextField fileNameTextField;
      70
      71     private FilePane filePane;
      72     private JToggleButton listViewButton;
      73     private JToggleButton detailsViewButton;
      74
      75     private boolean readOnly;
      76
      77     private JPanel buttonPanel;
      78     private JPanel bottomPanel;
      79
      80     private JComboBox<FileFilter> filterComboBox;
      81
      82     private static final Dimension hstrut5 = new Dimension(5, 1);
      83
      84     private static final Insets shrinkwrap = new Insets(0,0,0,0);
      85
      86     // Preferred and Minimum sizes for the dialog box
      87     private static Dimension LIST_PREF_SIZE = new Dimension(405, 135);
      88
      89     // Labels, mnemonics, and tooltips (oh my!)
      90     private int    lookInLabelMnemonic = 0;
      91     private String lookInLabelText = null;
      92     private String saveInLabelText = null;
      93
      94     private int    fileNameLabelMnemonic = 0;
      95     private String fileNameLabelText = null;
      96     private int    folderNameLabelMnemonic = 0;
      97     private String folderNameLabelText = null;
      98
      99     private int    filesOfTypeLabelMnemonic = 0;
     100     private String filesOfTypeLabelText = null;
     101
     102     private String upFolderToolTipText = null;
     103     private String upFolderAccessibleName = null;
     104
     105     private String homeFolderToolTipText = null;
     106     private String homeFolderAccessibleName = null;
     107
     108     private String newFolderToolTipText = null;
     109     private String newFolderAccessibleName = null;
     110
     111     private String listViewButtonToolTipText = null;
     112     private String listViewButtonAccessibleName = null;
     113
     114     private String detailsViewButtonToolTipText = null;
     115     private String detailsViewButtonAccessibleName = null;
     116
     117     private AlignedLabel fileNameLabel;
     118     private final PropertyChangeListener modeListener = new PropertyChangeListener() {
     119         public void propertyChange(PropertyChangeEvent event) {
     120             if (fileNameLabel != null) {
     121                 populateFileNameLabel();
     122             }
     123         }
     124     };
     125
     126     private void populateFileNameLabel() {
     127         if (getFileChooser().getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) {
     128             fileNameLabel.setText(folderNameLabelText);
     129             fileNameLabel.setDisplayedMnemonic(folderNameLabelMnemonic);
     130         } else {
     131             fileNameLabel.setText(fileNameLabelText);
     132             fileNameLabel.setDisplayedMnemonic(fileNameLabelMnemonic);
     133         }
     134     }
     135
     136     public SynthFileChooserUIImpl(JFileChooser b) {
     137         super(b);
     138     }
     139
     140
     141     private class SynthFileChooserUIAccessor implements FilePane.FileChooserUIAccessor {
     142         public JFileChooser getFileChooser() {
     143             return SynthFileChooserUIImpl.this.getFileChooser();
     144         }
     145
     146         public BasicDirectoryModel getModel() {
     147             return SynthFileChooserUIImpl.this.getModel();
     148         }
     149
     150         public JPanel createList() {
     151             return null;
     152         }
     153
     154         public JPanel createDetailsView() {
     155             return null;
     156         }
     157
     158         public boolean isDirectorySelected() {
     159             return SynthFileChooserUIImpl.this.isDirectorySelected();
     160         }
     161
     162         public File getDirectory() {
     163             return SynthFileChooserUIImpl.this.getDirectory();
     164         }
     165
     166         public Action getChangeToParentDirectoryAction() {
     167             return SynthFileChooserUIImpl.this.getChangeToParentDirectoryAction();
     168         }
     169
     170         public Action getApproveSelectionAction() {
     171             return SynthFileChooserUIImpl.this.getApproveSelectionAction();
     172         }
     173
     174         public Action getNewFolderAction() {
     175             return SynthFileChooserUIImpl.this.getNewFolderAction();
     176         }
     177
     178         public MouseListener createDoubleClickListener(JList<?> list) {
     179             return SynthFileChooserUIImpl.this.createDoubleClickListener(getFileChooser(),
     180                                                                      list);
     181         }
     182
     183         public ListSelectionListener createListSelectionListener() {
     184             return SynthFileChooserUIImpl.this.createListSelectionListener(getFileChooser());
     185         }
     186     }
     187
     188     protected void installDefaults(JFileChooser fc) {
     189         super.installDefaults(fc);
     190         readOnly = UIManager.getBoolean("FileChooser.readOnly");
     191     }
     192
     193     @SuppressWarnings("serial") // anonymous classes inside
     194     public void installComponents(JFileChooser fc) {
     195         super.installComponents(fc);
     196
     197         SynthContext context = getContext(fc, ENABLED);
     198
     199         fc.setLayout(new BorderLayout(0, 11));
     200
     201         // ********************************* //
     202         // **** Construct the top panel **** //
     203         // ********************************* //
     204
     205         // Directory manipulation buttons
     206         JPanel topPanel = new JPanel(new BorderLayout(11, 0));
     207     JPanel topButtonPanel = new JPanel();
     208     topButtonPanel.setLayout(new BoxLayout(topButtonPanel, BoxLayout.LINE_AXIS));
     209     topPanel.add(topButtonPanel, BorderLayout.AFTER_LINE_ENDS);
     210
     211         // Add the top panel to the fileChooser
     212         fc.add(topPanel, BorderLayout.NORTH);
     213
     214         // ComboBox Label
     215         lookInLabel = new JLabel(lookInLabelText);
     216         lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic);
     217         topPanel.add(lookInLabel, BorderLayout.BEFORE_LINE_BEGINS);
     218
     219         // CurrentDir ComboBox
     220         directoryComboBox = new JComboBox<File>();
     221         directoryComboBox.getAccessibleContext().setAccessibleDescription(lookInLabelText);
     222         directoryComboBox.putClientProperty( "JComboBox.isTableCellEditor", Boolean.TRUE );
     223         lookInLabel.setLabelFor(directoryComboBox);
     224         directoryComboBoxModel = createDirectoryComboBoxModel(fc);
     225         directoryComboBox.setModel(directoryComboBoxModel);
     226         directoryComboBox.addActionListener(directoryComboBoxAction);
     227         directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
     228         directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT);
     229         directoryComboBox.setAlignmentY(JComponent.TOP_ALIGNMENT);
     230         directoryComboBox.setMaximumRowCount(8);
     231         topPanel.add(directoryComboBox, BorderLayout.CENTER);
     232
     233         filePane = new FilePane(new SynthFileChooserUIAccessor());
     234         fc.addPropertyChangeListener(filePane);
     235
     236         // Add 'Go Up' to context menu, plus 'Go Home' if on Unix
     237         JPopupMenu contextMenu = filePane.getComponentPopupMenu();
     238         if (contextMenu != null) {
     239             contextMenu.insert(getChangeToParentDirectoryAction(), 0);
     240             if (File.separatorChar == '/') {
     241                 contextMenu.insert(getGoHomeAction(), 1);
     242             }
     243         }
     244
     245     FileSystemView fsv = fc.getFileSystemView();
     246
     247     // Up Button
     248     JButton upFolderButton = new JButton(getChangeToParentDirectoryAction());
     249     upFolderButton.setText(null);
     250     upFolderButton.setIcon(upFolderIcon);
     251     upFolderButton.setToolTipText(upFolderToolTipText);
     252     upFolderButton.getAccessibleContext().setAccessibleName(upFolderAccessibleName);
     253     upFolderButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
     254     upFolderButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
     255     upFolderButton.setMargin(shrinkwrap);
     256
     257     topButtonPanel.add(upFolderButton);
     258     topButtonPanel.add(Box.createRigidArea(hstrut5));
     259
     260     // Home Button
     261     File homeDir = fsv.getHomeDirectory();
     262     String toolTipText = homeFolderToolTipText;
     263
     264     JButton b = new JButton(homeFolderIcon);
     265     b.setToolTipText(toolTipText);
     266     b.getAccessibleContext().setAccessibleName(homeFolderAccessibleName);
     267     b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
     268     b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
     269     b.setMargin(shrinkwrap);
     270
     271     b.addActionListener(getGoHomeAction());
     272     topButtonPanel.add(b);
     273     topButtonPanel.add(Box.createRigidArea(hstrut5));
     274
     275     // New Directory Button
     276     if (!readOnly) {
     277         b = new JButton(filePane.getNewFolderAction());
     278         b.setText(null);
     279         b.setIcon(newFolderIcon);
     280         b.setToolTipText(newFolderToolTipText);
     281         b.getAccessibleContext().setAccessibleName(newFolderAccessibleName);
     282         b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
     283         b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
     284         b.setMargin(shrinkwrap);
     285         topButtonPanel.add(b);
     286         topButtonPanel.add(Box.createRigidArea(hstrut5));
     287     }
     288
     289     // View button group
     290     ButtonGroup viewButtonGroup = new ButtonGroup();
     291
     292     // List Button
     293     listViewButton = new JToggleButton(listViewIcon);
     294     listViewButton.setToolTipText(listViewButtonToolTipText);
     295     listViewButton.getAccessibleContext().setAccessibleName(listViewButtonAccessibleName);
     296     listViewButton.setSelected(true);
     297     listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
     298     listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
     299     listViewButton.setMargin(shrinkwrap);
     300     listViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_LIST));
     301     topButtonPanel.add(listViewButton);
     302     viewButtonGroup.add(listViewButton);
     303
     304     // Details Button
     305     detailsViewButton = new JToggleButton(detailsViewIcon);
     306     detailsViewButton.setToolTipText(detailsViewButtonToolTipText);
     307     detailsViewButton.getAccessibleContext().setAccessibleName(detailsViewButtonAccessibleName);
     308     detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
     309     detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
     310     detailsViewButton.setMargin(shrinkwrap);
     311     detailsViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_DETAILS));
     312     topButtonPanel.add(detailsViewButton);
     313     viewButtonGroup.add(detailsViewButton);
     314
     315     filePane.addPropertyChangeListener(new PropertyChangeListener() {
     316         public void propertyChange(PropertyChangeEvent e) {
     317             if ("viewType".equals(e.getPropertyName())) {
     318                 int viewType = filePane.getViewType();
     319                 switch (viewType) {
     320                     case FilePane.VIEWTYPE_LIST:
     321                         listViewButton.setSelected(true);
     322                         break;
     323                     case FilePane.VIEWTYPE_DETAILS:
     324                         detailsViewButton.setSelected(true);
     325                         break;
     326                 }
     327             }
     328         }
     329     });
     330
     331         // ************************************** //
     332         // ******* Add the directory pane ******* //
     333         // ************************************** //
     334         fc.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS);
     335         JComponent accessory = fc.getAccessory();
     336         if (accessory != null) {
     337             getAccessoryPanel().add(accessory);
     338         }
     339         filePane.setPreferredSize(LIST_PREF_SIZE);
     340         fc.add(filePane, BorderLayout.CENTER);
     341
     342
     343         // ********************************** //
     344         // **** Construct the bottom panel ** //
     345         // ********************************** //
     346         bottomPanel = new JPanel();
     347         bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS));
     348         fc.add(bottomPanel, BorderLayout.SOUTH);
     349
     350         // FileName label and textfield
     351         JPanel fileNamePanel = new JPanel();
     352         fileNamePanel.setLayout(new BoxLayout(fileNamePanel, BoxLayout.LINE_AXIS));
     353         bottomPanel.add(fileNamePanel);
     354         bottomPanel.add(Box.createRigidArea(new Dimension(1, 5)));
     355
     356         fileNameLabel = new AlignedLabel();
     357         populateFileNameLabel();
     358         fileNamePanel.add(fileNameLabel);
     359
     360         fileNameTextField = new JTextField(35) {
     361             public Dimension getMaximumSize() {
     362                 return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height);
     363             }
     364         };
     365         fileNamePanel.add(fileNameTextField);
     366         fileNameLabel.setLabelFor(fileNameTextField);
     367         fileNameTextField.addFocusListener(
     368             new FocusAdapter() {
     369                 public void focusGained(FocusEvent e) {
     370                     if (!getFileChooser().isMultiSelectionEnabled()) {
     371                         filePane.clearSelection();
     372                     }
     373                 }
     374             }
     375         );
     376         if (fc.isMultiSelectionEnabled()) {
     377             setFileName(fileNameString(fc.getSelectedFiles()));
     378         } else {
     379             setFileName(fileNameString(fc.getSelectedFile()));
     380         }
     381
     382
     383         // Filetype label and combobox
     384         JPanel filesOfTypePanel = new JPanel();
     385         filesOfTypePanel.setLayout(new BoxLayout(filesOfTypePanel, BoxLayout.LINE_AXIS));
     386         bottomPanel.add(filesOfTypePanel);
     387
     388         AlignedLabel filesOfTypeLabel = new AlignedLabel(filesOfTypeLabelText);
     389         filesOfTypeLabel.setDisplayedMnemonic(filesOfTypeLabelMnemonic);
     390         filesOfTypePanel.add(filesOfTypeLabel);
     391
     392         filterComboBoxModel = createFilterComboBoxModel();
     393         fc.addPropertyChangeListener(filterComboBoxModel);
     394         filterComboBox = new JComboBox<FileFilter>(filterComboBoxModel);
     395         filterComboBox.getAccessibleContext().setAccessibleDescription(filesOfTypeLabelText);
     396         filesOfTypeLabel.setLabelFor(filterComboBox);
     397         filterComboBox.setRenderer(createFilterComboBoxRenderer());
     398         filesOfTypePanel.add(filterComboBox);
     399
     400
     401         // buttons
     402         buttonPanel = new JPanel();
     403         buttonPanel.setLayout(new ButtonAreaLayout());
     404
     405         buttonPanel.add(getApproveButton(fc));
     406         buttonPanel.add(getCancelButton(fc));
     407
     408         if (fc.getControlButtonsAreShown()) {
     409             addControlButtons();
     410         }
     411
     412         groupLabels(new AlignedLabel[] { fileNameLabel, filesOfTypeLabel });
     413     }
     414
     415     protected void installListeners(JFileChooser fc) {
     416         super.installListeners(fc);
     417         fc.addPropertyChangeListener(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY, modeListener);
     418     }
     419
     420     protected void uninstallListeners(JFileChooser fc) {
     421         fc.removePropertyChangeListener(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY, modeListener);
     422         super.uninstallListeners(fc);
     423     }
     424
     425     private String fileNameString(File file) {
     426         if (file == null) {
     427             return null;
     428         } else {
     429             JFileChooser fc = getFileChooser();
     430             if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
     431                 return file.getPath();
     432             } else {
     433                 return file.getName();
     434             }
     435         }
     436     }
     437
     438     private String fileNameString(File[] files) {
     439         StringBuilder sb = new StringBuilder();
     440         for (int i = 0; files != null && i < files.length; i++) {
     441             if (i > 0) {
     442                 sb.append(" ");
     443             }
     444             if (files.length > 1) {
     445                 sb.append("\"");
     446             }
     447             sb.append(fileNameString(files[i]));
     448             if (files.length > 1) {
     449                 sb.append("\"");
     450             }
     451         }
     452         return sb.toString();
     453     }
     454
     455     public void uninstallUI(JComponent c) {
     456         // Remove listeners
     457         c.removePropertyChangeListener(filterComboBoxModel);
     458         c.removePropertyChangeListener(filePane);
     459
     460         if (filePane != null) {
     461             filePane.uninstallUI();
     462             filePane = null;
     463         }
     464
     465         super.uninstallUI(c);
     466     }
     467
     468     protected void installStrings(JFileChooser fc) {
     469         super.installStrings(fc);
     470
     471         Locale l = fc.getLocale();
     472
     473         lookInLabelMnemonic = getMnemonic("FileChooser.lookInLabelMnemonic", l);
     474         lookInLabelText = UIManager.getString("FileChooser.lookInLabelText", l);
     475         saveInLabelText = UIManager.getString("FileChooser.saveInLabelText", l);
     476
     477         fileNameLabelMnemonic = getMnemonic("FileChooser.fileNameLabelMnemonic", l);
     478         fileNameLabelText = UIManager.getString("FileChooser.fileNameLabelText", l);
     479         folderNameLabelMnemonic = getMnemonic("FileChooser.folderNameLabelMnemonic", l);
     480         folderNameLabelText = UIManager.getString("FileChooser.folderNameLabelText", l);
     481
     482         filesOfTypeLabelMnemonic = getMnemonic("FileChooser.filesOfTypeLabelMnemonic", l);
     483         filesOfTypeLabelText = UIManager.getString("FileChooser.filesOfTypeLabelText", l);
     484
     485     upFolderToolTipText =  UIManager.getString("FileChooser.upFolderToolTipText",l);
     486     upFolderAccessibleName = UIManager.getString("FileChooser.upFolderAccessibleName",l);
     487
     488     homeFolderToolTipText =  UIManager.getString("FileChooser.homeFolderToolTipText",l);
     489     homeFolderAccessibleName = UIManager.getString("FileChooser.homeFolderAccessibleName",l);
     490
     491     newFolderToolTipText = UIManager.getString("FileChooser.newFolderToolTipText",l);
     492     newFolderAccessibleName = UIManager.getString("FileChooser.newFolderAccessibleName",l);
     493
     494     listViewButtonToolTipText = UIManager.getString("FileChooser.listViewButtonToolTipText",l);
     495     listViewButtonAccessibleName = UIManager.getString("FileChooser.listViewButtonAccessibleName",l);
     496
     497     detailsViewButtonToolTipText = UIManager.getString("FileChooser.detailsViewButtonToolTipText",l);
     498     detailsViewButtonAccessibleName = UIManager.getString("FileChooser.detailsViewButtonAccessibleName",l);
     499     }
     500
     501     private int getMnemonic(String key, Locale l) {
     502         return SwingUtilities2.getUIDefaultsInt(key, l);
     503     }
     504
     505
     506     public String getFileName() {
     507         if (fileNameTextField != null) {
     508             return fileNameTextField.getText();
     509         } else {
     510             return null;
     511         }
     512     }
     513
     514     public void setFileName(String fileName) {
     515         if (fileNameTextField != null) {
     516             fileNameTextField.setText(fileName);
     517         }
     518     }
     519
     520     @Override public void rescanCurrentDirectory(JFileChooser fc) {
     521         filePane.rescanCurrentDirectory();
     522     }
     523
     524     protected void doSelectedFileChanged(PropertyChangeEvent e) {
     525         super.doSelectedFileChanged(e);
     526
     527         File f = (File) e.getNewValue();
     528         JFileChooser fc = getFileChooser();
     529         if (f != null
     530             && ((fc.isFileSelectionEnabled() && !f.isDirectory())
     531                 || (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {
     532
     533             setFileName(fileNameString(f));
     534         }
     535     }
     536
     537     protected void doSelectedFilesChanged(PropertyChangeEvent e) {
     538         super.doSelectedFilesChanged(e);
     539
     540         File[] files = (File[]) e.getNewValue();
     541         JFileChooser fc = getFileChooser();
     542         if (files != null
     543             && files.length > 0
     544             && (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) {
     545             setFileName(fileNameString(files));
     546         }
     547     }
     548
     549     protected void doDirectoryChanged(PropertyChangeEvent e) {
     550         super.doDirectoryChanged(e);
     551
     552         JFileChooser fc = getFileChooser();
     553         FileSystemView fsv = fc.getFileSystemView();
     554         File currentDirectory = fc.getCurrentDirectory();
     555
     556         if (!readOnly && currentDirectory != null) {
     557             getNewFolderAction().setEnabled(filePane.canWrite(currentDirectory));
     558         }
     559
     560         if (currentDirectory != null) {
     561             JComponent cb = getDirectoryComboBox();
     562             if (cb instanceof JComboBox) {
     563                 ComboBoxModel<?> model = ((JComboBox)cb).getModel();
     564                 if (model instanceof DirectoryComboBoxModel) {
     565                     ((DirectoryComboBoxModel)model).addItem(currentDirectory);
     566                 }
     567             }
     568
     569             if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
     570                 if (fsv.isFileSystem(currentDirectory)) {
     571                     setFileName(currentDirectory.getPath());
     572                 } else {
     573                     setFileName(null);
     574                 }
     575             }
     576         }
     577     }
     578
     579
     580     protected void doFileSelectionModeChanged(PropertyChangeEvent e) {
     581         super.doFileSelectionModeChanged(e);
     582
     583         JFileChooser fc = getFileChooser();
     584         File currentDirectory = fc.getCurrentDirectory();
     585         if (currentDirectory != null
     586             && fc.isDirectorySelectionEnabled()
     587             && !fc.isFileSelectionEnabled()
     588             && fc.getFileSystemView().isFileSystem(currentDirectory)) {
     589
     590             setFileName(currentDirectory.getPath());
     591         } else {
     592             setFileName(null);
     593         }
     594     }
     595
     596     protected void doAccessoryChanged(PropertyChangeEvent e) {
     597         if (getAccessoryPanel() != null) {
     598             if (e.getOldValue() != null) {
     599                 getAccessoryPanel().remove((JComponent)e.getOldValue());
     600             }
     601             JComponent accessory = (JComponent)e.getNewValue();
     602             if (accessory != null) {
     603                 getAccessoryPanel().add(accessory, BorderLayout.CENTER);
     604             }
     605         }
     606     }
     607
     608     protected void doControlButtonsChanged(PropertyChangeEvent e) {
     609         super.doControlButtonsChanged(e);
     610
     611         if (getFileChooser().getControlButtonsAreShown()) {
     612             addControlButtons();
     613         } else {
     614             removeControlButtons();
     615         }
     616     }
     617
     618     protected void addControlButtons() {
     619         if (bottomPanel != null) {
     620             bottomPanel.add(buttonPanel);
     621         }
     622     }
     623
     624     protected void removeControlButtons() {
     625         if (bottomPanel != null) {
     626             bottomPanel.remove(buttonPanel);
     627         }
     628     }
     629
     630
     631
     632
     633     // *******************************************************
     634     // ************ FileChooser UI PLAF methods **************
     635     // *******************************************************
     636
     637     protected ActionMap createActionMap() {
     638         ActionMap map = new ActionMapUIResource();
     639         // add standard actions
     640         FilePane.addActionsToMap(map, filePane.getActions());
     641         // add synth only actions
     642         map.put("fileNameCompletion", getFileNameCompletionAction());
     643         return map;
     644     }
     645
     646     // *****************************
     647     // ***** Directory Actions *****
     648     // *****************************
     649
     650     protected JComponent getDirectoryComboBox() {
     651         return directoryComboBox;
     652     }
     653
     654     protected Action getDirectoryComboBoxAction() {
     655         return directoryComboBoxAction;
     656     }
     657
     658     protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer(JFileChooser fc) {
     659         return new DirectoryComboBoxRenderer(directoryComboBox.getRenderer());
     660     }
     661
     662     //
     663     // Renderer for DirectoryComboBox
     664     //
     665     // Synth has some odd behavior with regards to renderers. Renderers are styled
     666     // in a specific manner by the SynthComboBoxUI. If we extend DefaultListCellRenderer
     667     // here, then we get none of those benefits or behaviors, leading to poor
     668     // looking combo boxes.
     669     // So what we do here is delegate most jobs to the "real" or original renderer,
     670     // and simply monkey with the icon and text of the renderer.
     671     private class DirectoryComboBoxRenderer implements ListCellRenderer<File> {
     672         private ListCellRenderer<? super File> delegate;
     673         IndentIcon ii = new IndentIcon();
     674
     675         private DirectoryComboBoxRenderer(ListCellRenderer<? super File> delegate) {
     676             this.delegate = delegate;
     677         }
     678
     679         @Override
     680         public Component getListCellRendererComponent(JList<? extends File> list, File value, int index, boolean isSelected, boolean cellHasFocus) {
     681             Component c = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
     682
     683             assert c instanceof JLabel;
     684             JLabel label = (JLabel)c;
     685             if (value == null) {
     686                 label.setText("");
     687                 return label;
     688             }
     689             label.setText(getFileChooser().getName(value));
     690             Icon icon = getFileChooser().getIcon(value);
     691             ii.icon = icon;
     692             ii.depth = directoryComboBoxModel.getDepth(index);
     693             label.setIcon(ii);
     694
     695             return label;
     696         }
     697     }
     698
     699     static final int space = 10;
     700     class IndentIcon implements Icon {
     701
     702         Icon icon = null;
     703         int depth = 0;
     704
     705         public void paintIcon(Component c, Graphics g, int x, int y) {
     706             if (icon != null) {
     707                 if (c.getComponentOrientation().isLeftToRight()) {
     708                     icon.paintIcon(c, g, x+depth*space, y);
     709                 } else {
     710                     icon.paintIcon(c, g, x, y);
     711                 }
     712             }
     713         }
     714
     715         public int getIconWidth() {
     716             return ((icon != null) ? icon.getIconWidth() : 0) + depth*space;
     717         }
     718
     719         public int getIconHeight() {
     720             return (icon != null) ? icon.getIconHeight() : 0;
     721         }
     722
     723     }
     724
     725     //
     726     // DataModel for DirectoryComboxbox
     727     //
     728     protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) {
     729         return new DirectoryComboBoxModel();
     730     }
     731
     732     /**
     733      * Data model for a type-face selection combo-box.
     734      */
     735     @SuppressWarnings("serial") // JDK-implementation class
     736     protected class DirectoryComboBoxModel extends AbstractListModel<File> implements ComboBoxModel<File> {
     737         Vector<File> directories = new Vector<File>();
     738         int[] depths = null;
     739         File selectedDirectory = null;
     740         JFileChooser chooser = getFileChooser();
     741         FileSystemView fsv = chooser.getFileSystemView();
     742
     743         public DirectoryComboBoxModel() {
     744             // Add the current directory to the model, and make it the
     745             // selectedDirectory
     746             File dir = getFileChooser().getCurrentDirectory();
     747             if (dir != null) {
     748                 addItem(dir);
     749             }
     750         }
     751
     752         /**
     753          * Adds the directory to the model and sets it to be selected,
     754          * additionally clears out the previous selected directory and
     755          * the paths leading up to it, if any.
     756          */
     757         public void addItem(File directory) {
     758
     759             if (directory == null) {
     760                 return;
     761             }
     762
     763             boolean useShellFolder = FilePane.usesShellFolder(chooser);
     764
     765             int oldSize = directories.size();
     766             directories.clear();
     767             if (oldSize > 0) {
     768                 fireIntervalRemoved(this, 0, oldSize);
     769             }
     770
     771             File[] baseFolders = (useShellFolder)
     772                     ? (File[]) ShellFolder.get("fileChooserComboBoxFolders")
     773                     : fsv.getRoots();
     774             directories.addAll(Arrays.asList(baseFolders));
     775
     776             // Get the canonical (full) path. This has the side
     777             // benefit of removing extraneous chars from the path,
     778             // for example /foo/bar/ becomes /foo/bar
     779             File canonical;
     780             try {
     781                 canonical = ShellFolder.getNormalizedFile(directory);
     782             } catch (IOException e) {
     783                 // Maybe drive is not ready. Can't abort here.
     784                 canonical = directory;
     785             }
     786
     787             // create File instances of each directory leading up to the top
     788             try {
     789                 File sf = useShellFolder ? ShellFolder.getShellFolder(canonical)
     790                                          : canonical;
     791                 File f = sf;
     792                 Vector<File> path = new Vector<File>(10);
     793                 do {
     794                     path.addElement(f);
     795                 } while ((f = f.getParentFile()) != null);
     796
     797                 int pathCount = path.size();
     798                 // Insert chain at appropriate place in vector
     799                 for (int i = 0; i < pathCount; i++) {
     800                     f = path.get(i);
     801                     if (directories.contains(f)) {
     802                         int topIndex = directories.indexOf(f);
     803                         for (int j = i-1; j >= 0; j--) {
     804                             directories.insertElementAt(path.get(j), topIndex+i-j);
     805                         }
     806                         break;
     807                     }
     808                 }
     809                 calculateDepths();
     810                 setSelectedItem(sf);
     811             } catch (FileNotFoundException ex) {
     812                 calculateDepths();
     813             }
     814         }
     815
     816         private void calculateDepths() {
     817             depths = new int[directories.size()];
     818             for (int i = 0; i < depths.length; i++) {
     819                 File dir = directories.get(i);
     820                 File parent = dir.getParentFile();
     821                 depths[i] = 0;
     822                 if (parent != null) {
     823                     for (int j = i-1; j >= 0; j--) {
     824                         if (parent.equals(directories.get(j))) {
     825                             depths[i] = depths[j] + 1;
     826                             break;
     827                         }
     828                     }
     829                 }
     830             }
     831         }
     832
     833         public int getDepth(int i) {
     834             return (depths != null && i >= 0 && i < depths.length) ? depths[i] : 0;
     835         }
     836
     837         public void setSelectedItem(Object selectedDirectory) {
     838             this.selectedDirectory = (File)selectedDirectory;
     839             fireContentsChanged(this, -1, -1);
     840         }
     841
     842         public Object getSelectedItem() {
     843             return selectedDirectory;
     844         }
     845
     846         public int getSize() {
     847             return directories.size();
     848         }
     849
     850         public File getElementAt(int index) {
     851             return directories.elementAt(index);
     852         }
     853     }
     854
     855     /**
     856      * Acts when DirectoryComboBox has changed the selected item.
     857      */
     858     @SuppressWarnings("serial") // JDK-implementation class
     859     protected class DirectoryComboBoxAction extends AbstractAction {
     860         protected DirectoryComboBoxAction() {
     861             super("DirectoryComboBoxAction");
     862         }
     863
     864         public void actionPerformed(ActionEvent e) {
     865             directoryComboBox.hidePopup();
     866             JComponent cb = getDirectoryComboBox();
     867             if (cb instanceof JComboBox) {
     868                 File f = (File)((JComboBox)cb).getSelectedItem();
     869                 getFileChooser().setCurrentDirectory(f);
     870             }
     871         }
     872     }
     873
     874     //
     875     // Renderer for Types ComboBox
     876     //
     877     protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
     878         return new FilterComboBoxRenderer(filterComboBox.getRenderer());
     879     }
     880
     881     /**
     882      * Render different type sizes and styles.
     883      */
     884     public class FilterComboBoxRenderer implements ListCellRenderer<FileFilter> {
     885         private ListCellRenderer<? super FileFilter> delegate;
     886         private FilterComboBoxRenderer(ListCellRenderer<? super FileFilter> delegate) {
     887             this.delegate = delegate;
     888         }
     889
     890         public Component getListCellRendererComponent(JList<? extends FileFilter> list, FileFilter value, int index,
     891                                                       boolean isSelected, boolean cellHasFocus) {
     892             Component c = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
     893
     894             String text = null;
     895             if (value != null) {
     896                 text = value.getDescription();
     897             }
     898
     899             //this should always be true, since SynthComboBoxUI's SynthComboBoxRenderer
     900             //extends JLabel
     901             assert c instanceof JLabel;
     902             if (text != null) {
     903                 ((JLabel)c).setText(text);
     904             }
     905             return c;
     906         }
     907     }
     908
     909     //
     910     // DataModel for Types Comboxbox
     911     //
     912     protected FilterComboBoxModel createFilterComboBoxModel() {
     913         return new FilterComboBoxModel();
     914     }
     915
     916     /**
     917      * Data model for a type-face selection combo-box.
     918      */
     919     @SuppressWarnings("serial") // JDK-implementation class
     920     protected class FilterComboBoxModel extends AbstractListModel<FileFilter> implements ComboBoxModel<FileFilter>,
     921             PropertyChangeListener {
     922         protected FileFilter[] filters;
     923         protected FilterComboBoxModel() {
     924             super();
     925             filters = getFileChooser().getChoosableFileFilters();
     926         }
     927
     928         public void propertyChange(PropertyChangeEvent e) {
     929             String prop = e.getPropertyName();
     930             if(prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) {
     931                 filters = (FileFilter[]) e.getNewValue();
     932                 fireContentsChanged(this, -1, -1);
     933             } else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) {
     934                 fireContentsChanged(this, -1, -1);
     935             }
     936         }
     937
     938         public void setSelectedItem(Object filter) {
     939             if(filter != null) {
     940                 getFileChooser().setFileFilter((FileFilter) filter);
     941                 fireContentsChanged(this, -1, -1);
     942             }
     943         }
     944
     945         public Object getSelectedItem() {
     946             // Ensure that the current filter is in the list.
     947             // NOTE: we shouldnt' have to do this, since JFileChooser adds
     948             // the filter to the choosable filters list when the filter
     949             // is set. Lets be paranoid just in case someone overrides
     950             // setFileFilter in JFileChooser.
     951             FileFilter currentFilter = getFileChooser().getFileFilter();
     952             boolean found = false;
     953             if(currentFilter != null) {
     954                 for (FileFilter filter : filters) {
     955                     if (filter == currentFilter) {
     956                         found = true;
     957                     }
     958                 }
     959                 if(found == false) {
     960                     getFileChooser().addChoosableFileFilter(currentFilter);
     961                 }
     962             }
     963             return getFileChooser().getFileFilter();
     964         }
     965
     966         public int getSize() {
     967             if(filters != null) {
     968                 return filters.length;
     969             } else {
     970                 return 0;
     971             }
     972         }
     973
     974         public FileFilter getElementAt(int index) {
     975             if(index > getSize() - 1) {
     976                 // This shouldn't happen. Try to recover gracefully.
     977                 return getFileChooser().getFileFilter();
     978             }
     979             if(filters != null) {
     980                 return filters[index];
     981             } else {
     982                 return null;
     983             }
     984         }
     985     }
     986
     987
     988
     989     /**
     990      * <code>ButtonAreaLayout</code> behaves in a similar manner to
     991      * <code>FlowLayout</code>. It lays out all components from left to
     992      * right, flushed right. The widths of all components will be set
     993      * to the largest preferred size width.
     994      */
     995     private static class ButtonAreaLayout implements LayoutManager {
     996         private int hGap = 5;
     997         private int topMargin = 17;
     998
     999         public void addLayoutComponent(String string, Component comp) {
    1000         }
    1001
    1002         public void layoutContainer(Container container) {
    1003             Component[] children = container.getComponents();
    1004
    1005             if (children != null && children.length > 0) {
    1006                 int         numChildren = children.length;
    1007                 Dimension[] sizes = new Dimension[numChildren];
    1008                 Insets      insets = container.getInsets();
    1009                 int         yLocation = insets.top + topMargin;
    1010                 int         maxWidth = 0;
    1011
    1012                 for (int counter = 0; counter < numChildren; counter++) {
    1013                     sizes[counter] = children[counter].getPreferredSize();
    1014                     maxWidth = Math.max(maxWidth, sizes[counter].width);
    1015                 }
    1016                 int xLocation, xOffset;
    1017                 if (container.getComponentOrientation().isLeftToRight()) {
    1018                     xLocation = container.getSize().width - insets.left - maxWidth;
    1019                     xOffset = hGap + maxWidth;
    1020                 } else {
    1021                     xLocation = insets.left;
    1022                     xOffset = -(hGap + maxWidth);
    1023                 }
    1024                 for (int counter = numChildren - 1; counter >= 0; counter--) {
    1025                     children[counter].setBounds(xLocation, yLocation,
    1026                                                 maxWidth, sizes[counter].height);
    1027                     xLocation -= xOffset;
    1028                 }
    1029             }
    1030         }
    1031
    1032         public Dimension minimumLayoutSize(Container c) {
    1033             if (c != null) {
    1034                 Component[] children = c.getComponents();
    1035
    1036                 if (children != null && children.length > 0) {
    1037                     int       numChildren = children.length;
    1038                     int       height = 0;
    1039                     Insets    cInsets = c.getInsets();
    1040                     int       extraHeight = topMargin + cInsets.top + cInsets.bottom;
    1041                     int       extraWidth = cInsets.left + cInsets.right;
    1042                     int       maxWidth = 0;
    1043
    1044                     for (int counter = 0; counter < numChildren; counter++) {
    1045                         Dimension aSize = children[counter].getPreferredSize();
    1046                         height = Math.max(height, aSize.height);
    1047                         maxWidth = Math.max(maxWidth, aSize.width);
    1048                     }
    1049                     return new Dimension(extraWidth + numChildren * maxWidth +
    1050                                          (numChildren - 1) * hGap,
    1051                                          extraHeight + height);
    1052                 }
    1053             }
    1054             return new Dimension(0, 0);
    1055         }
    1056
    1057         public Dimension preferredLayoutSize(Container c) {
    1058             return minimumLayoutSize(c);
    1059         }
    1060
    1061         public void removeLayoutComponent(Component c) { }
    1062     }
    1063
    1064     private static void groupLabels(AlignedLabel[] group) {
    1065         for (int i = 0; i < group.length; i++) {
    1066             group[i].group = group;
    1067         }
    1068     }
    1069
    1070     @SuppressWarnings("serial") // JDK-implementation class
    1071     private class AlignedLabel extends JLabel {
    1072         private AlignedLabel[] group;
    1073         private int maxWidth = 0;
    1074
    1075         AlignedLabel() {
    1076             super();
    1077             setAlignmentX(JComponent.LEFT_ALIGNMENT);
    1078         }
    1079
    1080         AlignedLabel(String text) {
    1081             super(text);
    1082             setAlignmentX(JComponent.LEFT_ALIGNMENT);
    1083         }
    1084
    1085         public Dimension getPreferredSize() {
    1086             Dimension d = super.getPreferredSize();
    1087             // Align the width with all other labels in group.
    1088             return new Dimension(getMaxWidth() + 11, d.height);
    1089         }
    1090
    1091         private int getMaxWidth() {
    1092             if (maxWidth == 0 && group != null) {
    1093                 int max = 0;
    1094                 for (int i = 0; i < group.length; i++) {
    1095                     max = Math.max(group[i].getSuperPreferredWidth(), max);
    1096                 }
    1097                 for (int i = 0; i < group.length; i++) {
    1098                     group[i].maxWidth = max;
    1099                 }
    1100             }
    1101             return maxWidth;
    1102         }
    1103
    1104         private int getSuperPreferredWidth() {
    1105             return super.getPreferredSize().width;
    1106         }
    1107     }
    1108 }