imageviewer.ui.swing.FileChooserUI.java Source code

Java tutorial

Introduction

Here is the source code for imageviewer.ui.swing.FileChooserUI.java

Source

/*******************************************************************************
 * Copyright (c) 2011 UCLA Medical Imaging Informatics Group
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 ******************************************************************************/
package imageviewer.ui.swing;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.Graphics;
import java.awt.LayoutManager;

import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import java.util.Arrays;
import java.util.Locale;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileView;
import javax.swing.filechooser.FileSystemView;

import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicDirectoryModel;
import javax.swing.plaf.basic.BasicFileChooserUI;

import sun.awt.shell.ShellFolder;
import sun.swing.FilePane;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

// =======================================================================
// Taken from the MetalFileChooserUI in Sun's Swing package. Adapated
// to handle different UI components in imageViewer. Couldn't subclass
// as the metal version of this class keeps everything private...Also,
// replaced components with the JGoodies formLayout for a cleaner
// layout of the involved components.

public class FileChooserUI extends BasicFileChooserUI {

    private static final Dimension hstrut5 = new Dimension(10, 1);
    private static final Dimension hstrut11 = new Dimension(11, 1);
    private static final Dimension vstrut5 = new Dimension(1, 5);

    private static final Insets shrinkwrap = new Insets(3, 3, 3, 3);

    // Preferred and Minimum sizes for the dialog box

    private static int LIST_PREF_WIDTH = 405;
    private static int LIST_PREF_HEIGHT = 135;
    private static int MIN_WIDTH = 500;
    private static int MIN_HEIGHT = 326;
    private static int PREF_WIDTH = 500;
    private static int PREF_HEIGHT = 326;

    private final static int space = 10;

    private static Dimension PREF_SIZE = new Dimension(PREF_WIDTH, PREF_HEIGHT);
    private static Dimension MIN_SIZE = new Dimension(MIN_WIDTH, MIN_HEIGHT);
    private static Dimension LIST_PREF_SIZE = new Dimension(LIST_PREF_WIDTH, LIST_PREF_HEIGHT);

    // =======================================================================
    // Labels, mnemonics, and tooltips (oh my!)

    int lookInLabelMnemonic = 0;
    int fileNameLabelMnemonic = 0;
    int filesOfTypeLabelMnemonic = 0;

    String lookInLabelText = null;
    String saveInLabelText = null;
    String fileNameLabelText = null;
    String filesOfTypeLabelText = null;
    String upFolderToolTipText = null;
    String upFolderAccessibleName = null;
    String homeFolderToolTipText = null;
    String homeFolderAccessibleName = null;
    String newFolderToolTipText = null;
    String newFolderAccessibleName = null;
    String listViewButtonToolTipText = null;
    String listViewButtonAccessibleName = null;
    String detailsViewButtonToolTipText = null;
    String detailsViewButtonAccessibleName = null;

    DirectoryComboBoxModel directoryComboBoxModel = null;
    Action directoryComboBoxAction = new DirectoryComboBoxAction();
    FilterComboBoxModel filterComboBoxModel = null;
    FilePane filePane = null;

    JComboBox filterComboBox = null;
    JComboBox directoryComboBox = null;
    JLabel lookInLabel = null;
    JToggleButton listViewButton = null;
    JToggleButton detailsViewButton = null;
    JButton approveButton = null;
    JButton cancelButton = null;
    JPanel buttonPanel = null;
    JPanel bottomPanel = null;
    JTextField fileNameTextField = null;

    BasicFileView fileView = new SystemFileView();

    boolean useShellFolder;

    // =======================================================================

    public static ComponentUI createUI(JComponent c) {
        return new FileChooserUI((JFileChooser) c);
    }

    // =======================================================================

    public FileChooserUI(JFileChooser filechooser) {
        super(filechooser);
    }

    public void installUI(JComponent c) {
        super.installUI(c);
    }

    public void uninstallComponents(JFileChooser fc) {
        fc.removeAll();
        bottomPanel = null;
        buttonPanel = null;
    }

    // =======================================================================

    private class FileChooserUIAccessor implements FilePane.FileChooserUIAccessor {

        public JFileChooser getFileChooser() {
            return FileChooserUI.this.getFileChooser();
        }

        public BasicDirectoryModel getModel() {
            return FileChooserUI.this.getModel();
        }

        public JPanel createList() {
            return FileChooserUI.this.createList(getFileChooser());
        }

        public JPanel createDetailsView() {
            return FileChooserUI.this.createDetailsView(getFileChooser());
        }

        public boolean isDirectorySelected() {
            return FileChooserUI.this.isDirectorySelected();
        }

        public File getDirectory() {
            return FileChooserUI.this.getDirectory();
        }

        public Action getChangeToParentDirectoryAction() {
            return FileChooserUI.this.getChangeToParentDirectoryAction();
        }

        public Action getApproveSelectionAction() {
            return FileChooserUI.this.getApproveSelectionAction();
        }

        public Action getNewFolderAction() {
            return FileChooserUI.this.getNewFolderAction();
        }

        public MouseListener createDoubleClickListener(JList list) {
            return FileChooserUI.this.createDoubleClickListener(getFileChooser(), list);
        }

        public ListSelectionListener createListSelectionListener() {
            return FileChooserUI.this.createListSelectionListener(getFileChooser());
        }

        public boolean usesShellFolder() {
            return true;
        }
    }

    // =======================================================================

    public void installComponents(JFileChooser fc) {

        FormLayout fl = new FormLayout("right:pref,2dlu,pref:grow,2dlu,26px,2px,26px,5px,26px,5px,26px,2px,26px",
                "pref,5px,fill:pref:grow,5px,pref,5px,pref,5px,pref");
        CellConstraints cc = new CellConstraints();

        FileSystemView fsv = fc.getFileSystemView();
        fc.setBorder(new EmptyBorder(5, 5, 5, 5));
        fc.setLayout(fl);
        filePane = new FilePane(new FileChooserUIAccessor());
        fc.addPropertyChangeListener(filePane);
        updateUseShellFolder();

        // Directory manipulation buttons

        JPanel topPanel = new JPanel(new BorderLayout(3, 0));
        lookInLabel = new JLabel(lookInLabelText);
        lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic);
        topPanel.add(lookInLabel, BorderLayout.WEST);

        // CurrentDir ComboBox

        directoryComboBox = new JComboBox() {
            public Dimension getPreferredSize() {
                Dimension d = super.getPreferredSize();
                d.width = 150;
                return d;
            }
        };

        directoryComboBox.setLightWeightPopupEnabled(false);
        directoryComboBox.getAccessibleContext().setAccessibleDescription(lookInLabelText);
        directoryComboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
        lookInLabel.setLabelFor(directoryComboBox);
        directoryComboBoxModel = createDirectoryComboBoxModel(fc);
        directoryComboBox.setModel(directoryComboBoxModel);
        directoryComboBox.addActionListener(directoryComboBoxAction);
        directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
        directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT);
        directoryComboBox.setAlignmentY(JComponent.TOP_ALIGNMENT);
        directoryComboBox.setMaximumRowCount(8);
        topPanel.add(directoryComboBox, BorderLayout.CENTER);
        fc.add(topPanel, cc.xywh(1, 1, 3, 1));

        // Up Button

        JButton upFolderButton = new JButton(getChangeToParentDirectoryAction());
        upFolderButton.setText(null);
        upFolderButton.setIcon(upFolderIcon);
        upFolderButton.setToolTipText(upFolderToolTipText);
        upFolderButton.getAccessibleContext().setAccessibleName(upFolderAccessibleName);
        upFolderButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
        upFolderButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
        upFolderButton.setMargin(shrinkwrap);
        fc.add(upFolderButton, cc.xy(5, 1));

        // Home Button

        File homeDir = fsv.getHomeDirectory();
        String toolTipText = homeFolderToolTipText;
        if (fsv.isRoot(homeDir))
            toolTipText = getFileView(fc).getName(homeDir); // Probably "Desktop"

        JButton b = new JButton(homeFolderIcon);
        b.setToolTipText(toolTipText);
        b.getAccessibleContext().setAccessibleName(homeFolderAccessibleName);
        b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
        b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
        b.setMargin(shrinkwrap);
        b.addActionListener(getGoHomeAction());
        fc.add(b, cc.xy(7, 1));

        // New Directory Button

        if (!UIManager.getBoolean("FileChooser.readOnly")) {
            b = new JButton(filePane.getNewFolderAction());
            b.setText(null);
            b.setIcon(newFolderIcon);
            b.setToolTipText(newFolderToolTipText);
            b.getAccessibleContext().setAccessibleName(newFolderAccessibleName);
            b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
            b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
            b.setMargin(shrinkwrap);
            fc.add(b, cc.xy(9, 1));
        }

        // View button group

        ButtonGroup viewButtonGroup = new ButtonGroup();

        // List Button

        listViewButton = new JToggleButton(listViewIcon);
        listViewButton.setToolTipText(listViewButtonToolTipText);
        listViewButton.getAccessibleContext().setAccessibleName(listViewButtonAccessibleName);
        listViewButton.setSelected(true);
        listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
        listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
        listViewButton.setMargin(shrinkwrap);
        listViewButton.setBorderPainted(false);
        listViewButton.setRolloverEnabled(true);
        listViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_LIST));
        viewButtonGroup.add(listViewButton);
        fc.add(listViewButton, cc.xy(11, 1));

        // Details Button

        detailsViewButton = new JToggleButton(detailsViewIcon);
        detailsViewButton.setToolTipText(detailsViewButtonToolTipText);
        detailsViewButton.getAccessibleContext().setAccessibleName(detailsViewButtonAccessibleName);
        detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
        detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
        detailsViewButton.setBorderPainted(false);
        detailsViewButton.setRolloverEnabled(true);
        detailsViewButton.setMargin(shrinkwrap);
        detailsViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_DETAILS));
        viewButtonGroup.add(detailsViewButton);
        fc.add(detailsViewButton, cc.xy(13, 1));

        filePane.addPropertyChangeListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent e) {
                if ("viewType".equals(e.getPropertyName())) {
                    int viewType = filePane.getViewType();
                    switch (viewType) {
                    case FilePane.VIEWTYPE_LIST:
                        listViewButton.setSelected(true);
                        break;
                    case FilePane.VIEWTYPE_DETAILS:
                        detailsViewButton.setSelected(true);
                        break;
                    }
                }
            }
        });

        filePane.setPreferredSize(LIST_PREF_SIZE);
        if (getAccessoryPanel() != null) {
            JPanel midPanel = new JPanel(new BorderLayout(0, 0));
            midPanel.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS);
            JComponent accessory = fc.getAccessory();
            if (accessory != null)
                getAccessoryPanel().add(accessory);
            midPanel.add(filePane, BorderLayout.CENTER);
            fc.add(midPanel, cc.xywh(1, 3, 13, 1));
        } else {
            fc.add(filePane, cc.xywh(1, 3, 13, 1));
        }

        // FileName label and textfield

        AlignedLabel fileNameLabel = new AlignedLabel(fileNameLabelText);
        fileNameLabel.setDisplayedMnemonic(fileNameLabelMnemonic);
        fc.add(fileNameLabel, cc.xy(1, 5));

        fileNameTextField = new JTextField(35) {
            public Dimension getMaximumSize() {
                return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height);
            }
        };
        fc.add(fileNameTextField, cc.xywh(3, 5, 11, 1));
        fileNameLabel.setLabelFor(fileNameTextField);
        fileNameTextField.addFocusListener(new FocusAdapter() {
            public void focusGained(FocusEvent e) {
                if (!getFileChooser().isMultiSelectionEnabled()) {
                    filePane.clearSelection();
                }
            }
        });

        setFileName((fc.isMultiSelectionEnabled()) ? fileNameString(fc.getSelectedFiles())
                : fileNameString(fc.getSelectedFile()));

        // Filetype label and combobox

        AlignedLabel filesOfTypeLabel = new AlignedLabel(filesOfTypeLabelText);
        filesOfTypeLabel.setDisplayedMnemonic(filesOfTypeLabelMnemonic);
        fc.add(filesOfTypeLabel, cc.xy(1, 7));

        filterComboBoxModel = createFilterComboBoxModel();
        fc.addPropertyChangeListener(filterComboBoxModel);
        filterComboBox = new JComboBox(filterComboBoxModel);
        filterComboBox.getAccessibleContext().setAccessibleDescription(filesOfTypeLabelText);
        filterComboBox.setLightWeightPopupEnabled(false);
        filesOfTypeLabel.setLabelFor(filterComboBox);
        filterComboBox.setRenderer(createFilterComboBoxRenderer());
        fc.add(filterComboBox, cc.xywh(3, 7, 11, 1));

        // Buttons

        approveButton = new JButton(getApproveButtonText(fc));
        approveButton.addActionListener(getApproveSelectionAction());
        approveButton.setToolTipText(getApproveButtonToolTipText(fc));
        cancelButton = new JButton(cancelButtonText);
        cancelButton.setToolTipText(cancelButtonToolTipText);
        cancelButton.addActionListener(getCancelSelectionAction());

        getButtonPanel().add(approveButton);
        getButtonPanel().add(Box.createHorizontalStrut(5));
        getButtonPanel().add(cancelButton);
        getBottomPanel().add(getButtonPanel());
        fc.add(getBottomPanel(), cc.xywh(1, 9, 13, 1));

        if (fc.getControlButtonsAreShown())
            addControlButtons();
        groupLabels(new AlignedLabel[] { fileNameLabel, filesOfTypeLabel });
    }

    // =======================================================================
    // Decide whether to use the ShellFolder class to populate shortcut
    // panel and combobox.

    private void updateUseShellFolder() {

        JFileChooser fc = getFileChooser();
        Boolean prop = (Boolean) fc.getClientProperty("FileChooser.useShellFolder");
        if (prop != null) {
            useShellFolder = prop.booleanValue();
        } else {
            useShellFolder = false;
            File[] roots = fc.getFileSystemView().getRoots();
            if ((roots != null) && (roots.length == 1)) {
                File[] cbFolders = (File[]) ShellFolder.get("fileChooserComboBoxFolders");
                if ((cbFolders != null) && (cbFolders.length > 0) && (roots[0] == cbFolders[0]))
                    useShellFolder = true;
            }
        }
    }

    // =======================================================================

    protected JPanel getButtonPanel() {
        if (buttonPanel == null)
            buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
        return buttonPanel;
    }

    protected JPanel getBottomPanel() {
        if (bottomPanel == null)
            bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
        return bottomPanel;
    }

    // =======================================================================

    protected void installStrings(JFileChooser fc) {

        super.installStrings(fc);
        Locale l = fc.getLocale();
        lookInLabelMnemonic = UIManager.getInt("FileChooser.lookInLabelMnemonic");
        lookInLabelText = UIManager.getString("FileChooser.lookInLabelText", l);
        saveInLabelText = UIManager.getString("FileChooser.saveInLabelText", l);
        fileNameLabelMnemonic = UIManager.getInt("FileChooser.fileNameLabelMnemonic");
        fileNameLabelText = UIManager.getString("FileChooser.fileNameLabelText", l);
        filesOfTypeLabelMnemonic = UIManager.getInt("FileChooser.filesOfTypeLabelMnemonic");
        filesOfTypeLabelText = UIManager.getString("FileChooser.filesOfTypeLabelText", l);
        upFolderToolTipText = UIManager.getString("FileChooser.upFolderToolTipText", l);
        upFolderAccessibleName = UIManager.getString("FileChooser.upFolderAccessibleName", l);
        homeFolderToolTipText = UIManager.getString("FileChooser.homeFolderToolTipText", l);
        homeFolderAccessibleName = UIManager.getString("FileChooser.homeFolderAccessibleName", l);
        newFolderToolTipText = UIManager.getString("FileChooser.newFolderToolTipText", l);
        newFolderAccessibleName = UIManager.getString("FileChooser.newFolderAccessibleName", l);
        listViewButtonToolTipText = UIManager.getString("FileChooser.listViewButtonToolTipText", l);
        listViewButtonAccessibleName = UIManager.getString("FileChooser.listViewButtonAccessibleName", l);
        detailsViewButtonToolTipText = UIManager.getString("FileChooser.detailsViewButtonToolTipText", l);
        detailsViewButtonAccessibleName = UIManager.getString("FileChooser.detailsViewButtonAccessibleName", l);
    }

    // =======================================================================

    protected void installListeners(JFileChooser fc) {
        super.installListeners(fc);
        ActionMap actionMap = getActionMap();
        SwingUtilities.replaceUIActionMap(fc, actionMap);
    }

    // =======================================================================

    protected ActionMap getActionMap() {
        return createActionMap();
    }

    protected ActionMap createActionMap() {
        ActionMap map = new ActionMapUIResource();
        FilePane.addActionsToMap(map, filePane.getActions());
        return map;
    }

    // =======================================================================

    protected JPanel createList(JFileChooser fc) {
        return filePane.createList();
    }

    protected JPanel createDetailsView(JFileChooser fc) {
        return filePane.createDetailsView();
    }

    public FileView getFileView(JFileChooser fc) {
        return fileView;
    }

    // =======================================================================

    public ListSelectionListener createListSelectionListener(JFileChooser fc) {
        return super.createListSelectionListener(fc);
    }

    public void uninstallUI(JComponent c) {

        c.removePropertyChangeListener(filterComboBoxModel);
        c.removePropertyChangeListener(filePane);
        cancelButton.removeActionListener(getCancelSelectionAction());
        approveButton.removeActionListener(getApproveSelectionAction());
        fileNameTextField.removeActionListener(getApproveSelectionAction());
        super.uninstallUI(c);
    }

    // =======================================================================
    /* Returns the preferred size of the specified
     * <code>JFileChooser</code>.  The preferred size is at least as
     * large, in both height and width, as the preferred size
     * recommended by the file chooser's layout manager.
     *
     * @param c  a <code>JFileChooser</code>
     * @return   a <code>Dimension</code> specifying the preferred
     *           width and height of the file chooser
     */

    public Dimension getPreferredSize(JComponent c) {

        int prefWidth = PREF_SIZE.width;
        Dimension d = c.getLayout().preferredLayoutSize(c);
        return (d != null)
                ? new Dimension((d.width < prefWidth) ? prefWidth : d.width,
                        (d.height < PREF_SIZE.height) ? PREF_SIZE.height : d.height)
                : new Dimension(prefWidth, PREF_SIZE.height);
    }

    // =======================================================================

    public Dimension getMinimumSize(JComponent c) {
        return MIN_SIZE;
    }

    public Dimension getMaximumSize(JComponent c) {
        return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
    }

    // =======================================================================

    private String fileNameString(File file) {

        if (file == null)
            return null;
        JFileChooser fc = getFileChooser();
        return ((fc.isDirectorySelectionEnabled()) && (!fc.isFileSelectionEnabled())) ? file.getPath()
                : file.getName();
    }

    private String fileNameString(File[] files) {

        StringBuffer buf = new StringBuffer();
        for (int i = 0; ((files != null) && (i < files.length)); i++) {
            if (i > 0)
                buf.append(" ");
            if (files.length > 1)
                buf.append("\"");
            buf.append(fileNameString(files[i]));
            if (files.length > 1)
                buf.append("\"");
        }
        return buf.toString();
    }

    // =======================================================================
    // The following methods are used by the PropertyChange Listener 

    private void doSelectedFileChanged(PropertyChangeEvent e) {

        File f = (File) e.getNewValue();
        JFileChooser fc = getFileChooser();
        if ((f != null) && ((fc.isFileSelectionEnabled() && !f.isDirectory())
                || (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {
            setFileName(fileNameString(f));
        }
    }

    private void doSelectedFilesChanged(PropertyChangeEvent e) {

        File[] files = (File[]) e.getNewValue();
        JFileChooser fc = getFileChooser();
        if ((files != null) && (files.length > 0)
                && (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) {
            setFileName(fileNameString(files));
        }
    }

    private void doDirectoryChanged(PropertyChangeEvent e) {

        JFileChooser fc = getFileChooser();
        FileSystemView fsv = fc.getFileSystemView();
        clearIconCache();
        File currentDirectory = fc.getCurrentDirectory();
        if (currentDirectory != null) {
            directoryComboBoxModel.addItem(currentDirectory);
            if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled())
                setFileName((fsv.isFileSystem(currentDirectory)) ? currentDirectory.getPath() : null);
        }
    }

    private void doFilterChanged(PropertyChangeEvent e) {
        clearIconCache();
    }

    private void doFileSelectionModeChanged(PropertyChangeEvent e) {

        clearIconCache();
        JFileChooser fc = getFileChooser();
        File currentDirectory = fc.getCurrentDirectory();
        if ((currentDirectory != null) && (fc.isDirectorySelectionEnabled()) && (!fc.isFileSelectionEnabled())
                && (fc.getFileSystemView().isFileSystem(currentDirectory))) {
            setFileName(currentDirectory.getPath());
        } else {
            setFileName(null);
        }
    }

    private void doAccessoryChanged(PropertyChangeEvent e) {

        if (getAccessoryPanel() != null) {
            if (e.getOldValue() != null)
                getAccessoryPanel().remove((JComponent) e.getOldValue());
            JComponent accessory = (JComponent) e.getNewValue();
            if (accessory != null)
                getAccessoryPanel().add(accessory, BorderLayout.CENTER);
        }
    }

    private void doApproveButtonTextChanged(PropertyChangeEvent e) {

        JFileChooser chooser = getFileChooser();
        approveButton.setText(getApproveButtonText(chooser));
        approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
    }

    private void doDialogTypeChanged(PropertyChangeEvent e) {

        JFileChooser chooser = getFileChooser();
        approveButton.setText(getApproveButtonText(chooser));
        approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
        lookInLabel
                .setText((chooser.getDialogType() == JFileChooser.SAVE_DIALOG) ? saveInLabelText : lookInLabelText);
    }

    private void doApproveButtonMnemonicChanged(PropertyChangeEvent e) {
    }

    private void doControlButtonsChanged(PropertyChangeEvent e) {
        if (getFileChooser().getControlButtonsAreShown())
            addControlButtons();
        else
            removeControlButtons();
    }

    public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) {

        return new PropertyChangeListener() {

            public void propertyChange(PropertyChangeEvent e) {
                String s = e.getPropertyName();
                if (s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
                    doSelectedFileChanged(e);
                } else if (s.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {
                    doSelectedFilesChanged(e);
                } else if (s.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {
                    doDirectoryChanged(e);
                } else if (s.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {
                    doFilterChanged(e);
                } else if (s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
                    doFileSelectionModeChanged(e);
                } else if (s.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) {
                    doAccessoryChanged(e);
                } else if (s.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY)
                        || s.equals(JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) {
                    doApproveButtonTextChanged(e);
                } else if (s.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY)) {
                    doDialogTypeChanged(e);
                } else if (s.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) {
                    doApproveButtonMnemonicChanged(e);
                } else if (s.equals(JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) {
                    doControlButtonsChanged(e);
                } else if (s.equals("componentOrientation")) {
                    ComponentOrientation o = (ComponentOrientation) e.getNewValue();
                    JFileChooser cc = (JFileChooser) e.getSource();
                    if (o != (ComponentOrientation) e.getOldValue()) {
                        cc.applyComponentOrientation(o);
                    }
                } else if (s == "FileChooser.useShellFolder") {
                    updateUseShellFolder();
                    doDirectoryChanged(e);
                } else if (s.equals("ancestor")) {
                    if (e.getOldValue() == null && e.getNewValue() != null) {
                        fileNameTextField.selectAll();
                        fileNameTextField.requestFocus();
                    }
                }
            }
        };
    }

    // =======================================================================

    protected void removeControlButtons() {
        getBottomPanel().remove(getButtonPanel());
    }

    protected void addControlButtons() {
        getBottomPanel().add(getButtonPanel());
    }

    public void ensureFileIsVisible(JFileChooser fc, File f) {
        filePane.ensureFileIsVisible(fc, f);
    }

    public void rescanCurrentDirectory(JFileChooser fc) {
        filePane.rescanCurrentDirectory();
    }

    public void setFileName(String filename) {
        if (fileNameTextField != null)
            fileNameTextField.setText(filename);
    }

    public String getFileName() {
        return (fileNameTextField != null) ? fileNameTextField.getText() : null;
    }

    // =======================================================================

    protected void setDirectorySelected(boolean directorySelected) {

        super.setDirectorySelected(directorySelected);
        JFileChooser chooser = getFileChooser();
        if (directorySelected) {
            if (approveButton != null) {
                approveButton.setText(directoryOpenButtonText);
                approveButton.setToolTipText(directoryOpenButtonToolTipText);
            }
        } else {
            if (approveButton != null) {
                approveButton.setText(getApproveButtonText(chooser));
                approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
            }
        }
    }

    // =======================================================================

    protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer(JFileChooser fc) {
        return new DirectoryComboBoxRenderer();
    }

    // =======================================================================

    private class DirectoryComboBoxRenderer extends DefaultListCellRenderer {

        IndentIcon ii = new IndentIcon();

        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                boolean cellHasFocus) {

            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            if (value == null) {
                setText("");
                return this;
            }
            File directory = (File) value;
            setText(getFileChooser().getName(directory));
            Icon icon = getFileChooser().getIcon(directory);
            ii.icon = icon;
            ii.depth = directoryComboBoxModel.getDepth(index);
            setIcon(ii);
            return this;
        }
    }

    // =======================================================================

    private class IndentIcon implements Icon {

        Icon icon = null;
        int depth = 0;

        public void paintIcon(Component c, Graphics g, int x, int y) {
            icon.paintIcon(c, g, (c.getComponentOrientation().isLeftToRight()) ? x + depth * space : x, y);
        }

        public int getIconWidth() {
            return (icon.getIconWidth() + (depth * space));
        }

        public int getIconHeight() {
            return icon.getIconHeight();
        }
    }

    // =======================================================================

    protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) {
        return new DirectoryComboBoxModel();
    }

    protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {

        Vector directories = new Vector();
        int[] depths = null;
        File selectedDirectory = null;
        JFileChooser chooser = getFileChooser();
        FileSystemView fsv = chooser.getFileSystemView();

        public DirectoryComboBoxModel() {

            File dir = getFileChooser().getCurrentDirectory();
            if (dir != null)
                addItem(dir);
        }

        /**
         * Adds the directory to the model and sets it to be selected,
         * additionally clears out the previous selected directory and the
         * paths leading up to it,if any.
         */

        private void addItem(File directory) {

            if (directory == null)
                return;
            directories.clear();
            File[] baseFolders;
            baseFolders = (useShellFolder) ? (File[]) ShellFolder.get("fileChooserComboBoxFolders")
                    : fsv.getRoots();
            directories.addAll(Arrays.asList(baseFolders));

            // Get the canonical (full) path. This has the side benefit of
            // removing extraneous chars from the path, for example
            // /foo/bar/ becomes /foo/bar

            File canonical = null;
            try {
                canonical = directory.getCanonicalFile();
            } catch (IOException e) {
                canonical = directory;
            }

            // Create File instances of each directory leading up to the top

            try {
                File sf = (useShellFolder) ? ShellFolder.getShellFolder(canonical) : canonical;
                File f = sf;
                Vector path = new Vector(10);
                do {
                    path.addElement(f);
                } while ((f = f.getParentFile()) != null);

                int pathCount = path.size();
                for (int i = 0; i < pathCount; i++) {
                    f = (File) path.get(i);
                    if (directories.contains(f)) {
                        int topIndex = directories.indexOf(f);
                        for (int j = i - 1; j >= 0; j--)
                            directories.insertElementAt(path.get(j), topIndex + i - j);
                        break;
                    }
                }
                calculateDepths();
                setSelectedItem(sf);
            } catch (FileNotFoundException ex) {
                calculateDepths();
            }
        }

        private void calculateDepths() {

            depths = new int[directories.size()];
            for (int i = 0; i < depths.length; i++) {
                File dir = (File) directories.get(i);
                File parent = dir.getParentFile();
                depths[i] = 0;
                if (parent != null) {
                    for (int j = i - 1; j >= 0; j--) {
                        if (parent.equals((File) directories.get(j))) {
                            depths[i] = depths[j] + 1;
                            break;
                        }
                    }
                }
            }
        }

        public int getDepth(int i) {
            return (depths != null && i >= 0 && i < depths.length) ? depths[i] : 0;
        }

        public int getSize() {
            return directories.size();
        }

        public Object getSelectedItem() {
            return selectedDirectory;
        }

        public Object getElementAt(int index) {
            return directories.elementAt(index);
        }

        public void setSelectedItem(Object selectedDirectory) {
            this.selectedDirectory = (File) selectedDirectory;
            fireContentsChanged(this, -1, -1);
        }
    }

    // =======================================================================

    protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
        return new FilterComboBoxRenderer();
    }

    public class FilterComboBoxRenderer extends DefaultListCellRenderer {

        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                boolean cellHasFocus) {

            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            if (value != null && value instanceof FileFilter)
                setText(((FileFilter) value).getDescription());
            return this;
        }
    }

    // =======================================================================

    protected FilterComboBoxModel createFilterComboBoxModel() {
        return new FilterComboBoxModel();
    }

    protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener {

        protected FileFilter[] filters;

        protected FilterComboBoxModel() {
            super();
            filters = getFileChooser().getChoosableFileFilters();
        }

        public void propertyChange(PropertyChangeEvent e) {

            String prop = e.getPropertyName();
            if (prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) {
                filters = (FileFilter[]) e.getNewValue();
                fireContentsChanged(this, -1, -1);
            } else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) {
                fireContentsChanged(this, -1, -1);
            }
        }

        public void setSelectedItem(Object filter) {

            if (filter != null) {
                getFileChooser().setFileFilter((FileFilter) filter);
                setFileName(null);
                fireContentsChanged(this, -1, -1);
            }
        }

        public Object getSelectedItem() {

            // Ensure that the current filter is in the list.  NOTE: we
            // shouldnt' have to do this,since JFileChooser adds the filter
            // to the choosable filters list when the filter is set. Lets be
            // paranoid just in case someone overrides setFileFilter in
            // JFileChooser.

            FileFilter currentFilter = getFileChooser().getFileFilter();
            boolean found = false;
            if (currentFilter != null) {
                for (int i = 0; i < filters.length; i++)
                    if (filters[i] == currentFilter)
                        found = true;
                if (found == false)
                    getFileChooser().addChoosableFileFilter(currentFilter);
            }
            return getFileChooser().getFileFilter();
        }

        public int getSize() {
            return (filters != null) ? filters.length : 0;
        }

        public Object getElementAt(int index) {
            if (index > getSize() - 1)
                return getFileChooser().getFileFilter();
            return (filters != null) ? filters[index] : null;
        }
    }

    // =======================================================================

    public void valueChanged(ListSelectionEvent e) {

        JFileChooser fc = getFileChooser();
        File f = fc.getSelectedFile();
        if (!e.getValueIsAdjusting() && f != null && !getFileChooser().isTraversable(f))
            setFileName(fileNameString(f));
    }

    // =======================================================================

    protected class DirectoryComboBoxAction extends AbstractAction {

        protected DirectoryComboBoxAction() {
            super("DirectoryComboBoxAction");
        }

        public void actionPerformed(ActionEvent e) {
            directoryComboBox.hidePopup();
            File f = (File) directoryComboBox.getSelectedItem();
            if (!getFileChooser().getCurrentDirectory().equals(f))
                getFileChooser().setCurrentDirectory(f);
        }
    }

    protected JButton getApproveButton(JFileChooser fc) {
        return approveButton;
    }

    private static void groupLabels(AlignedLabel[] group) {
        for (int i = 0; i < group.length; i++)
            group[i].group = group;
    }

    // =======================================================================

    private class AlignedLabel extends JLabel {

        private AlignedLabel[] group;
        private int maxWidth = 0;

        AlignedLabel(String text) {
            super(text, JLabel.TRAILING);
        }

        public Dimension getPreferredSize() {

            Dimension d = super.getPreferredSize();
            return new Dimension(getMaxWidth() + 11, d.height);
        }

        private int getMaxWidth() {

            if ((maxWidth == 0) && (group != null)) {
                int max = 0;
                for (int i = 0; i < group.length; i++)
                    max = Math.max(group[i].getSuperPreferredWidth(), max);
                for (int i = 0; i < group.length; i++)
                    group[i].maxWidth = max;
            }
            return maxWidth;
        }

        private int getSuperPreferredWidth() {
            return super.getPreferredSize().width;
        }
    }

    // =======================================================================
    // Override default behavior to use system icons, as per the windows L&F.

    protected class SystemFileView extends BasicFileView {

        public Icon getIcon(File f) {
            Icon icon = getCachedIcon(f);
            if (icon != null)
                return icon;
            if (f != null)
                icon = getFileChooser().getFileSystemView().getSystemIcon(f);
            if (icon == null)
                icon = super.getIcon(f);
            cacheIcon(f, icon);
            return icon;
        }
    }
}