All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.randelshofer.quaqua.leopard.QuaquaLeopardFileChooserUI Maven / Gradle / Ivy

Go to download

A Mavenisation of the Quaqua Mac OSX Swing Look and Feel (Java library) Quaqua Look and Feel (C) 2003-2010, Werner Randelshofer. Mavenisation by Matt Gumbley, DevZendo.org - for problems with Mavenisation, see Matt; for issues with Quaqua, see the Quaqua home page. For full license details, see http://randelshofer.ch/quaqua/license.html

The newest version!
/*
 * @(#)QuaquaLeopardFileChooserUI.java  
 *
 * Copyright (c) 2007-2010 Werner Randelshofer, Immensee, Switzerland.
 * http://www.randelshofer.ch
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package ch.randelshofer.quaqua.leopard;

import ch.randelshofer.quaqua.*;
import ch.randelshofer.quaqua.filechooser.*;
import ch.randelshofer.quaqua.leopard.filechooser.*;
import ch.randelshofer.quaqua.panther.filechooser.*;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.filechooser.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.dnd.*;
import java.awt.event.*;
import java.beans.*;
import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * A replacement for the AquaFileChooserUI. Provides a column view similar
 * to the one provided with the native Aqua user interface on Mac OS X 10.5
 * (Leopard).
 *
 * @author Werner Randelshofer
 * @version $Id: QuaquaLeopardFileChooserUI.java 363 2010-11-21 17:41:04Z wrandelshofer $
 */
public class QuaquaLeopardFileChooserUI extends BasicFileChooserUI implements SubtreeFileChooserUI {
    // Implementation derived from MetalFileChooserUI
    /* Models. */

    private DirectoryComboBoxModel directoryComboBoxModel;
    private Action directoryComboBoxAction = new DirectoryComboBoxAction();
    private FileView fileView;
    private FilterComboBoxModel filterComboBoxModel;
    private FileSystemTreeModel model = null;
    private SubtreeTreeModel subtreeModel = null;
    // Labels, mnemonics, and tooltips (oh my!)
    private int fileNameLabelMnemonic = 0;
    private String fileNameLabelText = null;
    ///private int filesOfTypeLabelMnemonic = 0;
    ///private String filesOfTypeLabelText = null;
    ///private String upFolderToolTipText = null;
    ///private String upFolderAccessibleName = null;
    ///private String homeFolderToolTipText = null;
    ///private String homeFolderAccessibleName = null;
    private String newFolderButtonText = null;
    private String newFolderToolTipText = null;
    ///private String newFolderAccessibleName = null;
    protected String chooseButtonText = null;
    private String newFolderDialogPrompt, newFolderDefaultName, newFolderErrorText, newFolderExistsErrorText, newFolderTitleText;
    private final static File computer = FileSystemTreeModel.COMPUTER;
    private SidebarTreeModel sidebarTreeModel;
    /**
     * This listener is used to determine whether the JFileChooser is showing.
     */
    private AncestorListener ancestorListener;
    /**
     * This listener is used to handle files that were dropped on the dir chooser.
     */
    private FileTransferHandler fileTransferHandler;
    /**
     * Actions.
     */
    private Action newFolderAction = new NewFolderAction();
    private Action approveSelectionAction = new QuaquaApproveSelectionAction();
    /**
     * Values greater zero indicate that the UI is adjusting.
     * This is required to prevent the UI from changing the FileChooser's state
     * while processing a PropertyChangeEvent fired from the FileChooser.
     */
    private int isAdjusting = 0;
    /** XXX - These keystrokes should go into an InputMap created by the
     * BasicQuaquaLookAndFeel class.
     */
    private KeyStroke[] KEYSTROKES = {
        KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.META_MASK | InputEvent.SHIFT_MASK),
        KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.META_MASK | InputEvent.SHIFT_MASK),
        KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.META_MASK),
        KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.META_MASK | InputEvent.SHIFT_MASK),
        KeyStroke.getKeyStroke(KeyEvent.VK_G, InputEvent.META_MASK | InputEvent.SHIFT_MASK),
        KeyStroke.getKeyStroke(KeyEvent.VK_H, InputEvent.META_MASK | InputEvent.SHIFT_MASK),
        KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.META_MASK | InputEvent.SHIFT_MASK),
        KeyStroke.getKeyStroke(KeyEvent.VK_K, InputEvent.META_MASK | InputEvent.SHIFT_MASK),
        KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.META_MASK | InputEvent.SHIFT_MASK),
        KeyStroke.getKeyStroke(KeyEvent.VK_BACK_QUOTE, 0),
        KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, 0)
    };
    private AbstractAction keyListenerAction = new AbstractAction() {
        // XXX - This should be rewritten using an ActionMap

        @Override
        public void actionPerformed(ActionEvent ae) {
            File file = null;
            switch (ae.getActionCommand().charAt(0)) {
                case 'd':
                    file = new File(System.getProperty("user.home") + "/Desktop");
                    break;
                case 'c':
                    file = new File("/");
                    break;
                case 'h':
                    file = new File(System.getProperty("user.home"));
                    break;
                case 'k':
                    file = new File("/Network");
                    break;
                case 'i':
                    //not doing iDisk for now
                    file = null;
                    return;
                case 'a':
                    file = new File("/Applications");
                    break;
                /*
                case 'u':
                dir = new File( "/Applications/Utilities" );
                break;*/
                case 'g':
                /* case '`':*/
                case '/':
                    //need to pop up window for user to type dir. need tab completion!
                    file = null;
                    return;
                default:
                    //Unknown Key Command in: + ae );
                    break;
            }
            //set the dir if non-null:
            if (file != null) {
                // if the dir is in the sidebar,
                // select the sidebar, otherwise just
                // select the dir
                // FIXME - Implement me
                /*
                for (int i=0, n = sidebarTreeModel.getSize(); i < n; i++) {
                FileInfo sidebarFile = (FileInfo) sidebarTreeModel.getElementAt(i);
                if (sidebarFile != null && sidebarFile.getFile().equals(dir)) {
                sidebarTree.setSelectedIndex(i);
                return;
                }
                }*/
                getFileChooser().setSelectedFile(file);
            }
        }
    };
    // Variables declaration - do not modify
    // FIXME - accessoryPanel could be moved up to BasicFileChooserUI.
    private javax.swing.JPanel accessoryPanel;
    private javax.swing.JButton approveButton;
    private ch.randelshofer.quaqua.JBrowser browser;
    private javax.swing.JScrollPane browserScrollPane;
    //private javax.swing.JToggleButton browserToggleButton;
    private javax.swing.JPanel cancelOpenPanel;
    private javax.swing.JPanel buttonsPanel;
    private javax.swing.JButton cancelButton;
    private javax.swing.JPanel controlsPanel;
    private javax.swing.JComboBox directoryComboBox;
    private javax.swing.JLabel fileNameLabel;
    private javax.swing.JPanel fileNamePanel;
    private javax.swing.JPanel fileNameSpringPanel;
    private javax.swing.JTextField fileNameTextField;
    private javax.swing.JLabel filesOfTypeLabel;
    private javax.swing.JComboBox filterComboBox;
    private javax.swing.JPanel formatPanel;
    private javax.swing.JPanel formatSpringPanel;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JPanel navigationButtonsPanel;
    private javax.swing.JPanel navigationPanel;
    private javax.swing.JPanel navigationSpringPanel;
    private javax.swing.JButton newFolderButton;
    //private javax.swing.JButton nextButton;
    //private javax.swing.JButton previousButton;
    private javax.swing.JSeparator separator;
    private javax.swing.JSplitPane splitPane;
    //private javax.swing.JTable table;
    //private javax.swing.JScrollPane tableScrollPane;
    //private javax.swing.JToggleButton tableToggleButton;
    ///private javax.swing.ButtonGroup viewGroup;
    private javax.swing.JPanel viewsPanel;
    private javax.swing.JTree sidebarTree;
    private javax.swing.JScrollPane sidebarScrollPane;
    // End of variables declaration
    //
    // ComponentUI Interface Implementation methods
    //

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

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

    @Override
    public void installComponents(JFileChooser fc) {
        sidebarTree = new javax.swing.JTree() {

            @Override
            public Dimension getPreferredSize() {
                Dimension d = super.getPreferredSize();
                d.width = 10;
                return d;
            }
        };

        // Form definition  - do not modify
        java.awt.GridBagConstraints gridBagConstraints;

        ///viewGroup = new javax.swing.ButtonGroup();
        fileNamePanel = new javax.swing.JPanel();
        fileNameLabel = new javax.swing.JLabel();
        fileNameTextField = new javax.swing.JTextField();
        fileNameSpringPanel = new javax.swing.JPanel();
        separator = new javax.swing.JSeparator();
        mainPanel = new javax.swing.JPanel();
        navigationPanel = new javax.swing.JPanel();
        navigationButtonsPanel = new javax.swing.JPanel();
        //previousButton = new javax.swing.JButton();
        //nextButton = new javax.swing.JButton();
        //tableToggleButton = new javax.swing.JToggleButton();
        //browserToggleButton = new javax.swing.JToggleButton();
        directoryComboBox = new javax.swing.JComboBox();
        navigationSpringPanel = new javax.swing.JPanel();
        splitPane = new javax.swing.JSplitPane();
        sidebarScrollPane = new javax.swing.JScrollPane();
        //sidebarTree = new javax.swing.JTree();
        viewsPanel = new javax.swing.JPanel();
        browserScrollPane = new javax.swing.JScrollPane();
        browser = new ch.randelshofer.quaqua.JBrowser();
        //tableScrollPane = new javax.swing.JScrollPane();
        //table = new javax.swing.JTable();
        controlsPanel = new javax.swing.JPanel();
        accessoryPanel = new javax.swing.JPanel();
        formatPanel = new javax.swing.JPanel();
        filesOfTypeLabel = new javax.swing.JLabel();
        filterComboBox = new javax.swing.JComboBox();
        formatSpringPanel = new javax.swing.JPanel();
        buttonsPanel = new javax.swing.JPanel();
        newFolderButton = new javax.swing.JButton();
        cancelOpenPanel = new javax.swing.JPanel();
        cancelButton = new javax.swing.JButton();
        approveButton = new javax.swing.JButton();

        fc.setLayout(new java.awt.BorderLayout());

        fc.setBorder(javax.swing.BorderFactory.createEmptyBorder(6, 0, 10, 0));
        fileNamePanel.setLayout(new java.awt.GridBagLayout());

        fileNamePanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(4, 0, 1, 0));
        fileNameLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        fileNameLabel.setText("Save As:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(7, 0, 0, 6);
        fileNamePanel.add(fileNameLabel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.ipadx = 250;
        gridBagConstraints.insets = new java.awt.Insets(7, 0, 0, 0);
        fileNamePanel.add(fileNameTextField, gridBagConstraints);

        fileNameSpringPanel.setLayout(null);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 0);
        fileNamePanel.add(fileNameSpringPanel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
        fileNamePanel.add(separator, gridBagConstraints);

        fc.add(fileNamePanel, java.awt.BorderLayout.NORTH);

        mainPanel.setLayout(new java.awt.BorderLayout());

        navigationPanel.setLayout(new java.awt.GridBagLayout());

        navigationPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 8, 4, 8));
        navigationButtonsPanel.setLayout(new java.awt.GridBagLayout());

        //previousButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ch/randelshofer/quaqua/panther/images/FileChooser.previousIcon.png")));
        //navigationButtonsPanel.add(previousButton, new java.awt.GridBagConstraints());

        //nextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ch/randelshofer/quaqua/panther/images/FileChooser.nextIcon.png")));
        //navigationButtonsPanel.add(nextButton, new java.awt.GridBagConstraints());

        //viewGroup.add(tableToggleButton);
        //tableToggleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ch/randelshofer/quaqua/panther/images/FileChooser.tableIcon.png")));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0);
        //navigationButtonsPanel.add(tableToggleButton, gridBagConstraints);

        //viewGroup.add(browserToggleButton);
        //browserToggleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ch/randelshofer/quaqua/panther/images/FileChooser.browserIcon.png")));
        //browserToggleButton.setSelected(true);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        //navigationButtonsPanel.add(browserToggleButton, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 0.5;
        navigationPanel.add(navigationButtonsPanel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.ipadx = 250;
        gridBagConstraints.insets = new java.awt.Insets(1, 0, 0, 0);
        navigationPanel.add(directoryComboBox, gridBagConstraints);

        navigationSpringPanel.setLayout(null);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 0.5;
        navigationPanel.add(navigationSpringPanel, gridBagConstraints);

        mainPanel.add(navigationPanel, java.awt.BorderLayout.NORTH);

        splitPane.setBorder(javax.swing.BorderFactory.createMatteBorder(1, 0, 1, 0, new java.awt.Color(153, 153, 153)));
        splitPane.setDividerLocation(134);
        splitPane.setDividerSize(1);
        // Setting the background color is needed for the Quaqua FileChooser-only LAF.
        if (UIManager.getColor("FileChooser.splitPaneBackground") != null) {
            splitPane.setBackground(UIManager.getColor("FileChooser.splitPaneBackground"));
        }
        splitPane.setContinuousLayout(true);
        sidebarScrollPane.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        sidebarScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        sidebarTree.setRootVisible(false);
        sidebarTree.setShowsRootHandles(true);
        sidebarScrollPane.setViewportView(sidebarTree);

        splitPane.setLeftComponent(sidebarScrollPane);

        viewsPanel.setLayout(new java.awt.CardLayout());

        browserScrollPane.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        browserScrollPane.setViewportView(browser);

        viewsPanel.add(browserScrollPane, "browser");

        //tableScrollPane.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        //tableScrollPane.setViewportView(table);

        //viewsPanel.add(tableScrollPane, "table");

        splitPane.setRightComponent(viewsPanel);

        mainPanel.add(splitPane, java.awt.BorderLayout.CENTER);

        fc.add(mainPanel, java.awt.BorderLayout.CENTER);

        controlsPanel.setLayout(new javax.swing.BoxLayout(controlsPanel, javax.swing.BoxLayout.Y_AXIS));

        accessoryPanel.setLayout(new java.awt.BorderLayout());

        accessoryPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(6, 8, 0, 8));
        controlsPanel.add(accessoryPanel);

        formatPanel.setLayout(new java.awt.GridBagLayout());

        formatPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(6, 0, 0, 0));
        filesOfTypeLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        filesOfTypeLabel.setText("Format:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 6);
        formatPanel.add(filesOfTypeLabel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.ipadx = 250;
        formatPanel.add(filterComboBox, gridBagConstraints);

        formatSpringPanel.setLayout(null);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 0);
        formatPanel.add(formatSpringPanel, gridBagConstraints);

        controlsPanel.add(formatPanel);

        buttonsPanel.setLayout(new java.awt.GridBagLayout());

        buttonsPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(6, 20, 0, 20));
        newFolderButton.setText("New Folder");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        buttonsPanel.add(newFolderButton, gridBagConstraints);

        cancelOpenPanel.setLayout(new java.awt.GridLayout(1, 0, 8, 0));

        cancelButton.setText("Cancel");
        cancelOpenPanel.add(cancelButton);

        approveButton.setText("Open");
        cancelOpenPanel.add(approveButton);

        buttonsPanel.add(cancelOpenPanel, new java.awt.GridBagConstraints());

        controlsPanel.add(buttonsPanel);

        fc.add(controlsPanel, java.awt.BorderLayout.SOUTH);

        //add(fc, java.awt.BorderLayout.CENTER);
        // End of form definition

        // Tweak visual properties
        int dividerSize = UIManager.getInt("FileChooser.splitPaneDividerSize");
        if (dividerSize != 0) {
            splitPane.setDividerSize(dividerSize);
        }
        splitPane.putClientProperty("Quaqua.SplitPane.style", "bar");
        separator.putClientProperty("Quaqua.Component.visualMargin", new Insets(3, 0, 3, 0));
        if (UIManager.getBoolean("FileChooser.enforceQuaquaTreeUI")) {
            sidebarTree.setUI((TreeUI) QuaquaTreeUI.createUI(sidebarTree));
        }
        sidebarTree.putClientProperty("Quaqua.Tree.style", "sideBar");
        sidebarTree.setRequestFocusEnabled(false);

        sidebarTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);

        int h;
        h = fileNameLabel.getPreferredSize().height;
        fileNameLabel.setMinimumSize(new Dimension(0, h));
        fileNameLabel.setPreferredSize(new Dimension(0, h));
        fileNameLabel.setMaximumSize(new Dimension(32767, h));

        h = fileNameTextField.getPreferredSize().height;
        fileNameTextField.setPreferredSize(new Dimension(0, h));
        fileNameTextField.setMinimumSize(new Dimension(0, h));
        fileNameTextField.setMaximumSize(new Dimension(32767, h));

        h = directoryComboBox.getPreferredSize().height;
        directoryComboBox.setPreferredSize(new Dimension(0, h));
        directoryComboBox.setMinimumSize(new Dimension(0, h));
        directoryComboBox.setMaximumSize(new Dimension(32767, h));

        Dimension d = new Dimension(28, 25);
        Dimension d2 = new Dimension(29, 25);
        //previousButton.setPreferredSize(d);
        //nextButton.setPreferredSize(d2);
        //tableToggleButton.setPreferredSize(d);
        //browserToggleButton.setPreferredSize(d2);
        //previousButton.setMinimumSize(d);
        //nextButton.setMinimumSize(d2);
        //tableToggleButton.setMinimumSize(d);
        //browserToggleButton.setMinimumSize(d2);

        //previousButton.setVisible(false);
        //nextButton.setVisible(false);
        //tableToggleButton.setVisible(false);
        //browserToggleButton.setVisible(false);

        h = navigationButtonsPanel.getPreferredSize().height;
        navigationButtonsPanel.setMinimumSize(new Dimension(0, h));
        navigationButtonsPanel.setPreferredSize(new Dimension(0, h));
        navigationButtonsPanel.setMaximumSize(new Dimension(32767, h));

        h = filesOfTypeLabel.getPreferredSize().height;
        filesOfTypeLabel.setMinimumSize(new Dimension(0, h));
        filesOfTypeLabel.setPreferredSize(new Dimension(0, h));
        filesOfTypeLabel.setMaximumSize(new Dimension(32767, h));

        h = filterComboBox.getPreferredSize().height;
        filterComboBox.setPreferredSize(new Dimension(0, h));
        filterComboBox.setMinimumSize(new Dimension(0, h));
        filterComboBox.setMaximumSize(new Dimension(32767, h));

        //Configure JBrowser
        LeopardFileRenderer fileRenderer = new LeopardFileRenderer(
                fc,
                UIManager.getIcon("Browser.expandingIcon"),
                UIManager.getIcon("Browser.expandedIcon"),
                UIManager.getIcon("Browser.selectedExpandingIcon"),
                UIManager.getIcon("Browser.selectedExpandedIcon"),
                UIManager.getIcon("Browser.focusedSelectedExpandingIcon"),
                UIManager.getIcon("Browser.focusedSelectedExpandedIcon"));
        browser.setColumnCellRenderer(fileRenderer);
        if (fc.isMultiSelectionEnabled()) {
            browser.setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
        } else {
            browser.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        }
        browser.setModel(getTreeModel());
        browser.setPrototypeCellValue(getFileSystemTreeModel().getPrototypeValue());
        browser.addTreeSelectionListener(createBrowserSelectionListener(fc));
        browser.addMouseListener(createDoubleClickListener(fc));
        browser.setFixedCellWidth(170);
        browserScrollPane.putClientProperty("Quaqua.Component.visualMargin", new Insets(3, 2, 3, 2));
        //browserScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        browser.setShowCellTipOrigin((Point) UIManager.get("FileChooser.cellTipOrigin"));
        browser.setShowCellTips(true);

        // Configure Sidebar Panel
        sidebarScrollPane.putClientProperty("Quaqua.Component.visualMargin", new Insets(3, 2, 3, 2));

        // Configure Format Panel
        formatPanel.setVisible(fc.getChoosableFileFilters().length > 1);

        // Configure Accessory Panel
        JComponent accessory = fc.getAccessory();
        if (accessory != null) {
            getAccessoryPanel().add(accessory);
        } else {
            accessoryPanel.setVisible(false);
        }

        // Configure filename Panel
        separator.putClientProperty("Quaqua.Component.visualMargin", new Insets(3, 0, 3, 0));

        // Text assignment
        newFolderButton.setText(newFolderButtonText);
        newFolderButton.setToolTipText(newFolderToolTipText);
        fileNameLabel.setText(fileNameLabelText);
        fileNameLabel.setDisplayedMnemonic(fileNameLabelMnemonic);

        approveButton.setText(getApproveButtonText(fc));
        // Note: Metal does not use mnemonics for approve and cancel
        approveButton.addActionListener(getApproveSelectionAction());
        approveButton.setToolTipText(getApproveButtonToolTipText(fc));

        cancelButton.setText(cancelButtonText);
        cancelButton.setToolTipText(cancelButtonToolTipText);
        cancelButton.addActionListener(getCancelSelectionAction());

        if (!fc.getControlButtonsAreShown()) {
            cancelButton.setVisible(false);
            approveButton.setVisible(false);
        }
        // End of Text assignment

        // Model and Renderer assignment
        directoryComboBoxModel = createDirectoryComboBoxModel(fc);
        directoryComboBox.setModel(directoryComboBoxModel);
        directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
        sidebarTree.setModel(sidebarTreeModel = new SidebarTreeModel(fc, new TreePath(getFileSystemTreeModel().getRoot()), getFileSystemTreeModel()));
        sidebarTree.setCellRenderer(createSidebarCellRenderer(fc));
        for (int i = sidebarTree.getRowCount() - 1; i >= 0; i--) {
            sidebarTree.expandRow(i);
        }

        filterComboBoxModel = createFilterComboBoxModel();
        filterComboBox.setModel(filterComboBoxModel);
        filterComboBox.setRenderer(createFilterComboBoxRenderer());
        // Model and Renderer assignment

        // Listener assignment
        directoryComboBox.addActionListener(directoryComboBoxAction);
        newFolderButton.addActionListener(getNewFolderAction());
        fileNameTextField.addFocusListener(new SaveTextFocusListener());
        fileNameTextField.setDocument(new FilenameDocument());
        fileNameTextField.getDocument().addDocumentListener(new SaveTextDocumentListener());
        fileNameTextField.addActionListener(getApproveSelectionAction());
        sidebarTree.addTreeSelectionListener(createSidebarSelectionListener(fc));
        // End of listener assignment

        // Focus traversal
        browserScrollPane.setFocusable(false);
        browserScrollPane.getVerticalScrollBar().setFocusable(false);
        browserScrollPane.getHorizontalScrollBar().setFocusable(false);
        sidebarScrollPane.setFocusable(false);
        sidebarScrollPane.getVerticalScrollBar().setFocusable(false);
        sidebarScrollPane.getHorizontalScrollBar().setFocusable(false);

        // Drag and drop assignment
        fileTransferHandler = new FileTransferHandler(fc);
        Component[] dropComponents = {
            fc,
            accessoryPanel,
            approveButton,
            browser,
            browserScrollPane,
            //browserToggleButton,
            buttonsPanel,
            cancelButton,
            controlsPanel,
            directoryComboBox,
            fileNameLabel,
            fileNamePanel,
            fileNameSpringPanel,
            fileNameTextField,
            filesOfTypeLabel,
            filterComboBox,
            formatPanel,
            formatSpringPanel,
            mainPanel,
            navigationButtonsPanel,
            navigationPanel,
            navigationSpringPanel,
            newFolderButton,
            //nextButton,
            //previousButton,
            separator,
            splitPane,
            //table,
            //tableScrollPane,
            //tableToggleButton,

            viewsPanel,
            sidebarTree,
            sidebarScrollPane
        };
        for (int i = 0; i < dropComponents.length; i++) {
            new DropTarget(dropComponents[i], DnDConstants.ACTION_COPY, fileTransferHandler);
        }
        // End of drag and drop assignment

        // Change component visibility to match the dialog type
        boolean isSave = (fc.getDialogType() == JFileChooser.SAVE_DIALOG) || (fc.getDialogType() == JFileChooser.CUSTOM_DIALOG);
        fileNameTextField.setEnabled(isSave);
        fileNamePanel.setVisible(isSave);

        // Preview column
        doPreviewComponentChanged(null);

        // Button state
        updateApproveButtonState();

        // Configure size of split pane
        splitPane.setPreferredSize(new Dimension(518, 298));
        splitPane.setMinimumSize(new Dimension(400, 80));

        // register key events with window
        ActionMap am = mainPanel.getActionMap();
        InputMap im = mainPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        for (int i = 0; i < KEYSTROKES.length; ++i) {
            im.put(KEYSTROKES[i], KEYSTROKES[i]);
            am.put(KEYSTROKES[i], keyListenerAction);
        }
        controlsPanel.setActionMap(am);
        controlsPanel.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, im);
        fileNamePanel.setActionMap(am);
        fileNamePanel.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, im);

        // Enforce layout, so that the selected file is visible when the
        // file chooser is opened with its preferred size.
        Dimension ps = fc.getPreferredSize();
        fc.setBounds(0, 0, ps.width, ps.height);
        fc.doLayout();
    }

    @Override
    public void uninstallComponents(JFileChooser fc) {
        fc.removeAll();

        // Dispose model
        model.dispose();

        // Remove listeners on UI components
        cancelButton.removeActionListener(getCancelSelectionAction());
        approveButton.removeActionListener(getApproveSelectionAction());
        fileNameTextField.removeActionListener(getApproveSelectionAction());
    }

    /**
     * Installs listeners.
     * We install the same listeners as BasicFileChooserUI plus an
     * AncestorListener and a property change listener.
     */
    @Override
    protected void installListeners(JFileChooser fc) {
        super.installListeners(fc);
        ancestorListener = createAncestorListener(fc);
        if (ancestorListener != null) {
            fc.addAncestorListener(ancestorListener);
        }
        fc.addPropertyChangeListener(filterComboBoxModel);
    }

    @Override
    protected void uninstallListeners(JFileChooser fc) {
        super.uninstallListeners(fc);
        if (ancestorListener != null) {
            fc.removeAncestorListener(ancestorListener);
        }
        fc.removePropertyChangeListener(filterComboBoxModel);
    }

    private Locale getLocale() {
        try {
            return getFileChooser().getLocale();
        } catch (IllegalComponentStateException e) {
            return Locale.getDefault();
        }
    }

    @Override
    protected void installDefaults(JFileChooser fc) {
        super.installDefaults(fc);

        Object value = UIManager.get("FileChooser.fileHidingEnabled");
        boolean booleanValue = (value instanceof Boolean) ? ((Boolean) value).booleanValue() : true;
        fc.setFileHidingEnabled(booleanValue);
    }

    @Override
    protected void installStrings(JFileChooser fc) {
        super.installStrings(fc);

        Locale l;
        try {
            l = getLocale();
        } catch (IllegalComponentStateException e) {
            l = Locale.getDefault();
        }


        // FIXME - We must not read these strings from the UIManager, as long
        //         as we don't provide them with our own Look and Feel. This
        //         is, because these strings are version dependent, and thus
        //         are not necessarily in sync with what we need in our UI.
        chooseButtonText = UIManager.getString("FileChooser.chooseButtonText"/*,l*/);

        fileNameLabelMnemonic = UIManager.getInt("FileChooser.fileNameLabelMnemonic");
        fileNameLabelText = UIManager.getString("FileChooser.fileNameLabelText"/*,l*/);
        // XXX - Localize "Save as:" text.
        //if (fileNameLabelText == null || fileNameLabelText.charAt(fileNameLabelText.length() -1) != ':') fileNameLabelText = "Save as:";

        ///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*/);

        cancelButtonText = UIManager.getString("FileChooser.cancelButtonText"/*,l*/);
        cancelButtonToolTipText = UIManager.getString("FileChooser.cancelToolTipText"/*,l*/);

        // New Folder Dialog
        newFolderErrorText = getString("FileChooser.newFolderErrorText", l, "Error occured during folder creation");
        newFolderExistsErrorText = getString("FileChooser.newFolderExistsErrorText", l, "That name is already taken");
        // FIXME - There is no "FileChooser.newFolderButtonText", so we use the newFolderTitleText.
        newFolderButtonText = getString("FileChooser.newFolderButtonText", l, "New Folder");
        newFolderTitleText = getString("FileChooser.newFolderTitleText", l, "New Folder");
        newFolderDialogPrompt = getString("FileChooser.newFolderPromptText", l, "Name of new folder:");
        newFolderDefaultName = getString("FileChooser.untitledFolderName", l, "untitled folder");
        newFolderToolTipText = UIManager.getString("FileChooser.newFolderToolTipText"/*, l*/);
        ///newFolderAccessibleName = getString("FileChooser.newFolderAccessibleName", l, newFolderTitleText);
    }

    /**
     * FIXME - This could be moved up to BasicFileChooserUI.
     */
    @Override
    public JPanel getAccessoryPanel() {
        return accessoryPanel;
    }

    /**
     * Gets a locale dependent string.
     */
    private String getString(String string, Locale l, String defaultValue) {
        String value = UIManager.getString(string/*, l*/);
        return (value == null) ? defaultValue : value;
    }

    /**
     * Creates an AncestorListener.
     * The AncestorListener is used to take an action when the JFileChooser becomes
     * showing on screen.
     */
    protected AncestorListener createAncestorListener(JFileChooser fc) {
        return new FileChooserAncestorListener();
    }

    @Override
    public void createModel() {
        JFileChooser fc = getFileChooser();

        // FIXME - We should not overwrite the FileSystemView attribute
        // of the JFileChooser.
        fc.setFileSystemView(QuaquaFileSystemView.getQuaquaFileSystemView());

        // FIXME - We should not overwrite the FileView attribute
        // of the JFileChooser.
        if (UIManager.getBoolean("FileChooser.speed")) {
            fileView = new BasicFileView();
        } else {
            fileView = QuaquaFileSystemView.getQuaquaFileSystemView().createFileView(fc);
        }
        fc.setFileView(fileView);

        model = new FileSystemTreeModel(fc);
        subtreeModel = new SubtreeTreeModel(model);
        subtreeModel.setPathToRoot(model.toPath(new File(QuaquaManager.getProperty("user.home")), null));
    }

    public SubtreeTreeModel getTreeModel() {
        return subtreeModel;
    }

    public FileSystemTreeModel getFileSystemTreeModel() {
        return model;
    }

    /**
     * The array contains the selected dir(s) of the JFileChooser.
     * All files have an absolute path.
     * If no dir is selected, the length of the array is 0.
     * Always returns a non-null value.
     * All array elements are non-null.
     */
    private File[] getSelectedFiles() {
        JFileChooser fc = getFileChooser();

        if (fc.isMultiSelectionEnabled()) {
            File[] selectedFiles = fc.getSelectedFiles();
            ArrayList list = new ArrayList(selectedFiles.length);
            for (int i = 0; i < selectedFiles.length; i++) {
                if (selectedFiles[i] != null) {
                    if (selectedFiles[i].isAbsolute()) {
                        list.add(selectedFiles[i]);
                    } else {
                        list.add(new File(fc.getCurrentDirectory(), selectedFiles[i].getName()));
                    }
                }
            }
            return (File[]) list.toArray(new File[list.size()]);
        } else {
            File f = fc.getSelectedFile();
            if (f == null) {
                return new File[0];
            } else {
                if (f.isAbsolute()) {
                    return new File[]{f};
                } else {
                    return new File[]{new File(fc.getCurrentDirectory(), f.getName())};
                }
            }
        }
    }

    /**
     * Updates the selection in the JBrowser, to match the selected dir/s
     * of the JFileChooser.
     */
    private void updateSelection() {
        JFileChooser fc = getFileChooser();
        File[] files = getSelectedFiles();
        TreePath fullPath = null;

        boolean isAtLeastOneFileSelected = false;
        boolean isAtLeastOneDirSelected = false;

        if (files.length != 0) {
            TreePath[] paths = new TreePath[files.length];
            ArrayList list = new ArrayList(paths.length);

            TreePath commonParentPath = null;
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                if (!file.isAbsolute()) {
                    file = new File(fc.getCurrentDirectory(), file.getPath());
                }

                TreePath selectionPath = subtreeModel.toFullPath(browser.getSelectionPath());
                fullPath = model.toPath(file, selectionPath);
                FileSystemTreeModel.Node node = (FileSystemTreeModel.Node) fullPath.getLastPathComponent();

                boolean isDirectory = !node.isLeaf();
                if (isDirectory) {
                    isAtLeastOneDirSelected = true;
                } else {
                    isAtLeastOneFileSelected = true;
                }

                if (files.length == 1 || !isDirectory || fc.isDirectorySelectionEnabled()) {
                    TreePath subPath = getTreeModel().toSubPath(fullPath);

                    // All parent paths must be equal
                    TreePath parentPath = (subPath == null) ? null : subPath.getParentPath();

                    if (list.size() == 0) {
                        commonParentPath = parentPath;
                    }
                    if (parentPath == null && commonParentPath == null || parentPath != null && commonParentPath != null && parentPath.equals(commonParentPath)) {
                        list.add(subPath);
                    }
                }
            }
            if (list.size() == 0 && files.length > 0) {
                list.add(fc.getFileSystemView().getParentDirectory(files[0]));
            }

            if (!subtreeModel.isDescendant(fullPath)) {
                subtreeModel.setPathToRoot(new TreePath(model.getRoot()));
                // FIXME - We must honour multiple selection!!!
                browser.setSelectionPath(fullPath);
                // FIXME - Select the dir system root in the sidebar!!!
                sidebarTree.clearSelection();
            } else {
                browser.setSelectionPaths((TreePath[]) list.toArray(new TreePath[list.size()]));
            }

            if (files.length == 1) {
                FileSystemTreeModel.Node node = (FileSystemTreeModel.Node) fullPath.getLastPathComponent();
                if (node.isLeaf() || !files[0].exists()) {
                    setFileName(files[0].getName());
                }
            }
        }

        if (fullPath != null && fullPath.getPathCount() > 0) {
            FileSystemTreeModel.Node node = (FileSystemTreeModel.Node) fullPath.getLastPathComponent();

            directoryComboBoxModel.setPath(
                    (node.isLeaf()) ? fullPath.getParentPath() : fullPath);
        }


        if (files.length == 1) {
            ensureFileIsVisible(fc, files[0]);
        }
        updateApproveButtonState();
    }

    /**
     * Returns true, if the dir name field contains a dir name.
     */
    private boolean isFileNameFieldValid() {
        String string = getFileName();
        return string != null && !string.equals("");
    }

    /**
     * Returns true, if the dir name field is visible.
     */
    private boolean isFileNameFieldVisible() {
        JFileChooser fc = getFileChooser();
        return (fc.getDialogType() == JFileChooser.SAVE_DIALOG) || (fc.getDialogType() == JFileChooser.CUSTOM_DIALOG);
    }

    private void updateApproveButtonState() {
        JFileChooser fc = getFileChooser();

        if (fc.getControlButtonsAreShown()) {
            File[] files = getSelectedFiles();

            boolean isEnabled = true;
            boolean isSaveDialog = fc.getDialogType() == JFileChooser.SAVE_DIALOG;
            boolean isFileSelected = false;
            boolean isDirectorySelected = false;
            for (int i = 0; i < files.length; i++) {
                if (files[i].exists()) {
                    if (files[i].isDirectory() && fc.isTraversable(files[i])) {
                        isDirectorySelected = true;
                    } else {
                        isFileSelected = true;
                    }
                    isEnabled &= isSaveDialog || fc.accept(files[i]);
                    if (!isEnabled) {
                        System.err.println("ACCEPT? " + fc.accept(files[i]) + " " + files[i]);
                    }
                }
            }

            switch (fc.getFileSelectionMode()) {
                case JFileChooser.FILES_ONLY:
                    isEnabled &= isFileSelected || isFileNameFieldVisible() && isFileNameFieldValid();
                    break;
                case JFileChooser.DIRECTORIES_ONLY:
                    // Note: in the following expression we must not check
                    // whether isDirectorySelected is true, because a
                    // the file chooser always shows a directory of some kind
                    // in its view.
                    isEnabled &= /*isDirectorySelected &&*/ !isFileSelected;
                    break;
                case JFileChooser.FILES_AND_DIRECTORIES:
                    isEnabled &= true;
                    break;
            }
            setApproveButtonEnabled(isEnabled);
        }
    }

    private void setApproveButtonEnabled(boolean isEnabled) {
        JFileChooser fc = getFileChooser();
        if (fc.getControlButtonsAreShown()) {
            approveButton.setEnabled(isEnabled);
            if (isEnabled) {
                JRootPane rp = approveButton.getRootPane();
                if (rp != null) {
                    rp.setDefaultButton(approveButton);
                }
            }
        }
    }

    private void updateApproveButtonText() {
        JFileChooser fc = getFileChooser();

        approveButton.setText(getApproveButtonText(fc));
        approveButton.setToolTipText(getApproveButtonToolTipText(fc));
        approveButton.setMnemonic(getApproveButtonMnemonic(fc));
        //cancelButton.setToolTipText(getCancelButtonToolTipText(fc));
    }

    private void updateFileChooserFromBrowser() {
        JFileChooser fc = getFileChooser();
        TreePath[] paths = browser.getSelectionPaths();

        // Determine the selected files. If multiple files are selected,
        // we strip directories from this list, if the JFileChooser does
        // not allow directory selection.
        int count = 0;
        File[] files = new File[(paths == null) ? 0 : paths.length];
        ArrayList list = new ArrayList(files.length);
        for (int i = 0; i < files.length; i++) {
            FileSystemTreeModel.Node node = (FileSystemTreeModel.Node) paths[i].getLastPathComponent();
            File file = node.lazyGetResolvedFile();
            if (file != null && fc.accept(file)) {
                boolean isDirectory = !node.isLeaf();
                if (files.length == 1 || !isDirectory || fc.isDirectorySelectionEnabled()) {
                    list.add(file);
                }
            }
        }


        if (fc.isMultiSelectionEnabled()) {
            fc.setSelectedFiles((File[]) list.toArray(new File[list.size()]));
        } else {
            fc.setSelectedFile((list.size() > 0) ? (File) list.get(0) : null);
        }
    }

    protected TreeSelectionListener createBrowserSelectionListener(JFileChooser fc) {
        return new BrowserSelectionListener();
    }

    /**
     * Selection listener for the list of files and directories.
     */
    protected class BrowserSelectionListener implements TreeSelectionListener {

        @Override
        public void valueChanged(TreeSelectionEvent e) {
            if (isAdjusting != 0) {
                return;
            }
            
            TreePath path = browser.getSelectionPath();
            if (path != null) {
                model.lazyInvalidatePath(path);
                model.validatePath(path);
            }

            updateFileChooserFromBrowser();
        }
    }

    /* The following methods are used by the PropertyChange Listener */
    private void doSelectedFileChanged(PropertyChangeEvent e) {
        updateSelection();
    }

    private void doSelectedFilesChanged(PropertyChangeEvent e) {
        updateSelection();
    }

    private void doDirectoryChanged(PropertyChangeEvent e) {
        File dir = (File) e.getNewValue();

        TreePath selectionPath = (browser.getSelectionPath() == null) ? null : subtreeModel.toFullPath(browser.getSelectionPath());
        TreePath dirPath = model.toPath(dir, selectionPath);
        TreePath fullDirPath = dirPath;

        // XXX - This code occurs two times in this class - move it into a method
        if (!subtreeModel.getPathToRoot().isDescendant(dirPath)) {
            FileInfo sidebarFileInfo = null;
            TreePath sidebarSelectionPath = null;
            for (Enumeration i = ((DefaultMutableTreeNode) sidebarTree.getModel().getRoot()).preorderEnumeration(); i.hasMoreElements();) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) i.nextElement();
                if (node instanceof FileInfo) {
                    FileInfo info = (FileInfo) node;
                    if (info.getResolvedFile() != null && info.getResolvedFile().equals(dir)) {
                        sidebarFileInfo = info;
                        sidebarSelectionPath = new TreePath(node.getPath());
                        break;
                    }
                }
            }
            if (sidebarFileInfo != null) {
                TreePath sidebarPath = model.toPath(sidebarFileInfo.getResolvedFile(), selectionPath);
                subtreeModel.setPathToRoot(sidebarPath);
                sidebarTree.setSelectionPath(sidebarSelectionPath);
            } else {
                subtreeModel.setPathToRoot(new TreePath(model.getRoot()));
                sidebarTree.clearSelection();
            }
        }
        dirPath = subtreeModel.toSubPath(dirPath);

        directoryComboBoxModel.setPath(fullDirPath);
        browser.setSelectionPath(dirPath);
        browser.ensurePathIsVisible(dirPath);
        model.lazyInvalidatePath(dirPath);
    }

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

        model.invalidateAll();
//->the update did not occur from the browser        updateFileChooserFromBrowser();
        /*
        updateSelection();
        updateApproveButtonState();*/
        if (getFileChooser().isShowing()) {
            model.validatePath(subtreeModel.toFullPath(browser.getSelectionPath()));
            browser.repaint();
            browser.updatePreviewColumn();
        }

    }

    private void doFileViewChanged(PropertyChangeEvent e) {
        model.invalidateAll();
        if (getFileChooser().isShowing()) {
            model.validatePath(subtreeModel.toFullPath(browser.getSelectionPath()));
            browser.repaint();
            browser.updatePreviewColumn();
        }
    }

    private void doFileSelectionModeChanged(PropertyChangeEvent e) {
        updateApproveButtonText();
        updateApproveButtonState();
    }

    private void doMultiSelectionChanged(PropertyChangeEvent e) {
        if (getFileChooser().isMultiSelectionEnabled()) {
            browser.setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
        } else {
            browser.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
            getFileChooser().setSelectedFiles(null);
        }
    }

    private void doChoosableFilterChanged(PropertyChangeEvent e) {
        JFileChooser fc = getFileChooser();
        boolean isChooserVisible = ((FileFilter[]) e.getNewValue()).length > 1;
        formatPanel.setVisible(isChooserVisible);
        model.invalidateAll();
        /*
        updateSelection();
        updateApproveButtonState();*/
//->the update did not occur from the browser        updateFileChooserFromBrowser();
        updateApproveButtonState();
        if (fc.isShowing()) {
            model.validatePath(subtreeModel.toFullPath(browser.getSelectionPath()));
            browser.repaint();
            browser.updatePreviewColumn();
        }
    }

    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);
            }
            accessoryPanel.setVisible(accessory != null);
        }
    }

    private void doApproveButtonTextChanged(PropertyChangeEvent e) {
        JFileChooser chooser = getFileChooser();
        approveButton.setText(getApproveButtonText(chooser));
        approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
    }

    private void doDialogTypeChanged(PropertyChangeEvent e) {
        JFileChooser fc = getFileChooser();
        approveButton.setText(getApproveButtonText(fc));
        approveButton.setToolTipText(getApproveButtonToolTipText(fc));
        boolean isSave = isFileNameFieldVisible();
        fileNameTextField.setEnabled(isSave);
        fileNamePanel.setVisible(isSave);

        doPreviewComponentChanged(null);
    }

    private void doApproveButtonMnemonicChanged(PropertyChangeEvent e) {
        // Note: Metal does not use mnemonics for approve and cancel
    }

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

    private void doFileSystemViewChanged(PropertyChangeEvent e) {
        JFileChooser fc = getFileChooser();

        model = new FileSystemTreeModel(fc);
        subtreeModel = new SubtreeTreeModel(model);

        browser.setModel(getTreeModel());
        sidebarTree.setModel(sidebarTreeModel = new SidebarTreeModel(fc, new TreePath(getFileSystemTreeModel().getRoot()), getFileSystemTreeModel()));
    }

    private void doPreviewComponentChanged(PropertyChangeEvent e) {
        JFileChooser fc = getFileChooser();
        final Component pv = (Component) fc.getClientProperty("Quaqua.FileChooser.preview");
        if (pv != null) {
            browser.setPreviewRenderer(new BrowserPreviewRenderer() {

                public Component getPreviewRendererComponent(JBrowser browser, TreePath[] paths) {
                    return pv;
                }
            });
            browser.setPreviewColumnWidth(Math.max(browser.getFixedCellWidth(), pv.getPreferredSize().width));
        } else {
            boolean isSave = isFileNameFieldVisible();
            browser.setPreviewRenderer((isSave) ? null : new FilePreview(fc));
            browser.setPreviewColumnWidth(browser.getFixedCellWidth());
        }
    }
    /*
     * Listen for filechooser property changes, such as
     * the selected dir changing, or the type of the dialog changing.
     */

    @Override
    public PropertyChangeListener createPropertyChangeListener(final JFileChooser fc) {
        return new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent e) {
                isAdjusting++;

                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_SYSTEM_VIEW_CHANGED_PROPERTY)) {
                    doFileSystemViewChanged(e);
                } else if (s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
                    doFileSelectionModeChanged(e);
                } else if (s.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
                    doMultiSelectionChanged(e);
                } else if (s.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) {
                    doAccessoryChanged(e);
                } else if (s.equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) {
                    doChoosableFilterChanged(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(JFileChooser.FILE_VIEW_CHANGED_PROPERTY)) {
                    doFileViewChanged(e);
                } else if (s.equals("Quaqua.FileChooser.preview")) {
                    doPreviewComponentChanged(e);
                } else if (s.equals("componentOrientation")) {
                    /* FIXME - This needs JDK 1.4 to work.
                    ComponentOrientation o = (ComponentOrientation)e.getNewValue();
                    JFileChooser fc = (JFileChooser)e.getSource();
                    if (o != (ComponentOrientation)e.getOldValue()) {
                    fc.applyComponentOrientation(o);
                    }
                     */
                } else if (s.equals("ancestor")) {
                    if (e.getOldValue() == null && e.getNewValue() != null) {
                        // Ancestor was added, ensure path is visible and
                        // set initial focus
                        ensurePathIsVisible(subtreeModel.toFullPath(browser.getSelectionPath()));
                        if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) {
                            fileNameTextField.selectAll();
                            fileNameTextField.requestFocus();
                        } else {
                            browser.requestFocus();
                        }
                    }
                }

                isAdjusting--;
            }
        };
    }

    protected void removeControlButtons() {
        cancelButton.setVisible(false);
        approveButton.setVisible(false);
    }

    protected void addControlButtons() {
        cancelButton.setVisible(true);
        approveButton.setVisible(true);
    }

    private void ensurePathIsVisible(TreePath path) {
        if (!subtreeModel.isDescendant(path.getPath())) {
            isAdjusting++;
            if (((FileSystemTreeModel.Node) path.getLastPathComponent()).isLeaf()) {
                subtreeModel.setPathToRoot(path.getParentPath().getPath());
            } else {
                subtreeModel.setPathToRoot(path.getPath());
            }
            isAdjusting--;
        }
        browser.ensurePathIsVisible(path);
    }

    @Override
    public String getFileName() {
        if (fileNameTextField != null) {
            return fileNameTextField.getText();
        } else {
            return null;
        }
    }

    @Override
    public void setFileName(String filename) {
        if (fileNameTextField != null
                && !fileNameTextField.hasFocus()
                && (filename == null || !fileNameTextField.getText().equals(filename))) {
            fileNameTextField.setText(filename);
        }
    }

    /** Sets the root directory of the subtree. */
    public void setRootDirectory(File file) {
        if (file != null) {
            JFileChooser fc = getFileChooser();
            if (file.isDirectory() /*&& fc.isTraversable(file)*/) {
                isAdjusting++;
                TreePath fullPath = getFileSystemTreeModel().toPath(file, subtreeModel.getPathToRoot());
                subtreeModel.setPathToRoot(fullPath);
                getFileSystemTreeModel().lazyInvalidatePath(fullPath);
                
                // XXX - Bogus.
                //       We can not set a non-traversable directory as
                //       the current directory in a JFileChooser.
                //       But we have to be able to do this, because we
                //       want to be able to browse the contents of a resource
                //       bundle using the file chooser.
                if (fc.isTraversable(file)) {
                    fc.setCurrentDirectory(file);
                }
                TreePath dirPath = subtreeModel.toSubPath(fullPath);
                browser.setSelectionPath(dirPath);
                browser.ensurePathIsVisible(dirPath);
                directoryComboBoxModel.setPath(fullPath);
                isAdjusting--;
            }
        }
    }

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

    private SidebarRenderer createSidebarCellRenderer(JFileChooser fc) {
        return new SidebarRenderer();
    }

    protected TreeSelectionListener createSidebarSelectionListener(JFileChooser fc) {
        return new SidebarSelectionListener();
    }

    //
    // Renderer for DirectoryComboBox
    //
    static class DirectoryComboBoxRenderer extends DefaultListCellRenderer {

        private Border border = new EmptyBorder(1, 0, 1, 0);
        IndentIcon ii = new IndentIcon();
        private JSeparator separator = new JSeparator();

        public DirectoryComboBoxRenderer() {
            separator.setPreferredSize(new Dimension(9, 9));
        }

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


            // String objects are used to denote delimiters.
            if (value instanceof String) {
                super.getListCellRendererComponent(list, value, index, false, cellHasFocus);
                setText((String) value);
                setPreferredSize(new Dimension(10, 14));
                return this;
            }
            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            setPreferredSize(null);
            if (value instanceof File) {
                setText(value + " " + index);
                return this;
            }
            FileSystemTreeModel.Node node = (FileSystemTreeModel.Node) value;
            if (node == null) {
                return separator;
                /*
                File root = new File("/");
                setText(getFileChooser().getName(root));
                ii.icon = getFileChooser().getIcon(root);
                 */
            } else {
                setText(node.getUserName());
                ii.icon = node.getIcon();
            }
            ii.depth = 0;
            setIcon(ii);
            setBorder(border);
            return this;
        }
    }

    //
    // Renderer for Volumes list
    //
    // CHANGE This class is rewritten after adding support for the Quaqua.Tree.style property
    // CHANGE All methods except the getTreeCellRendererComponent(...) were
    // deleted and are no longer needed
    private static class SidebarRenderer extends DefaultTreeCellRenderer {

        public SidebarRenderer() {
            if (UIManager.getBoolean("FileChooser.enforceQuaquaTreeUI")) {
                setUI((LabelUI) QuaquaLabelUI.createUI(this));
            }
        }

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value,
                boolean isSelected, boolean isExpanded, boolean isLeaf,
                int row, boolean cellHasFocus) {
            super.getTreeCellRendererComponent(tree, value, isSelected,
                    isExpanded, isLeaf, row, false);

            if (value != null && value instanceof FileInfo) {
                FileInfo info = (FileInfo) value;
                setText(info.getUserName());

                if (isSpecialFolder(info)) {
                    setIcon(getSpecialFolderIcon(info));
                } else {
                    setIcon(info.getIcon());
                }
            }
            return this;
        }

        /**
         * Gets the special icon for the folder.
         * 
         * @param info The {@link FileInfo}.
         * @return The icon.
         **/
        private Icon getSpecialFolderIcon(FileInfo info) {
            // Load the icon from the UIDefaults table
            Icon icon = UIManager.getIcon("FileChooser.sideBarIcon." + info.getFile().getName());

            // If we somehow fail to load the icon, fall back to standard way
            if (icon == null) {
                icon = info.getIcon();
            }

            return icon;
        }

        /**
         * Gets whether the the {@link FileInfo} represents a "special" folder - a folder which
         * is visually different in the side bar than in the browser view of a file chooser.
         * 
         * @param info The {@link FileInfo}.
         * @return true if the OS is Mac OS X and the 
         */
        private boolean isSpecialFolder(FileInfo info) {
            // Only allow this for Mac OS X as directory structures are different on other OSs.
            if (!QuaquaManager.isOSX()) {
                return false;
            }

            File file = info.getFile();
            // Only directories can have special icons.
            if (file == null || file.isFile()) {
                return false;
            }

            if (file.getParentFile() != null) {
                String parentFile = file.getParentFile().getAbsolutePath();
                if (parentFile.equals(System.getProperty("user.home"))) {
                    // Look for user's home special folders
                    String name = file.getName();
                    return name.equals("Applications") || name.equals("Desktop") || name.equals("Documents") || name.equals("Downloads") || name.equals("Library") || name.equals("Movies") || name.equals("Music") || name.equals("Pictures") || name.equals("Public") || name.equals("Sites");
                } else if (parentFile.equals(computer.getAbsolutePath())) {
                    // Look for computer's special folders
                    String name = file.getName();
                    return name.equals("Applications") || name.equals("Library");
                } else if (!parentFile.equals(new File(computer, "Applications").getAbsolutePath())) {
                    // Look for Utility folder in the /Applications folder
                    return file.getName().equals("Utilities");
                }
            }
            // Nothing found - return null
            return false;
        }
    }
    final static int space = 10;

    private static class IndentIcon implements Icon {

        Icon icon = null;
        int depth = 0;

        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            if (icon != null) {
                if (c.getComponentOrientation().isLeftToRight()) {
                    icon.paintIcon(c, g, x + depth * space, y);
                } else {
                    icon.paintIcon(c, g, x, y);
                }
            }
        }

        @Override
        public int getIconWidth() {
            return (icon == null) ? depth * space : icon.getIconWidth() + depth * space;
        }

        @Override
        public int getIconHeight() {
            return (icon == null) ? 0 : icon.getIconHeight();
        }
    }

    //
    // DataModel for DirectoryComboxbox
    //
    protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) {
        return new DirectoryComboBoxModel();
    }

    /**
     * Data model for a directory selection combo-box.
     * There is always one node in the tree model: the dir system root (aka
     * the comouter).
     */
    protected class DirectoryComboBoxModel extends AbstractListModel
            implements ComboBoxModel {

        TreePath path;
        FileSystemTreeModel.Node selectedDirectory = null;
        JFileChooser chooser = getFileChooser();
        FileSystemView fsv = chooser.getFileSystemView();

        public DirectoryComboBoxModel() {
        }

        /**
         * Sets the path of the directory combo box.
         * TreePath
         */
        private void setPath(TreePath path) {
            if (this.path != null && this.path.getPathCount() > 0) {
                fireIntervalRemoved(this, 0, this.path.getPathCount() - 1);
            }
            this.path = path;
            if (this.path.getPathCount() > 0) {
                fireIntervalAdded(this, 0, this.path.getPathCount() - 1);
            }
            setSelectedItem(this.path.getLastPathComponent());
        }

        @Override
        public void setSelectedItem(Object selectedItem) {
            FileSystemTreeModel.Node node = (FileSystemTreeModel.Node) selectedItem;
            this.selectedDirectory = node;
            fireContentsChanged(this, -1, -1);
        }

        @Override
        public Object getSelectedItem() {
            return selectedDirectory;
        }

        @Override
        public int getSize() {
            return (path == null) ? 0 : path.getPathCount();
        }

        @Override
        public Object getElementAt(int index) {
            return path.getPathComponent(path.getPathCount() - index - 1);
        }
    }

    //
    // Renderer for Types ComboBox
    //
    protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
        return new FilterComboBoxRenderer();
    }

    /**
     * Render different type sizes and styles.
     */
    private static class FilterComboBoxRenderer extends DefaultListCellRenderer {

        private Border border = new EmptyBorder(1, 0, 1, 0);

        @Override
        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());
            }
            setBorder(border);

            return this;
        }
    }

    //
    // DataModel for Types Comboxbox
    //
    protected FilterComboBoxModel createFilterComboBoxModel() {
        return new FilterComboBoxModel();
    }

    /**
     * Data model for a type-face selection combo-box.
     */
    protected class FilterComboBoxModel
            extends AbstractListModel
            implements ComboBoxModel, PropertyChangeListener {

        protected FileFilter[] filters;

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

        @Override
        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);
            }
        }

        @Override
        public void setSelectedItem(Object filter) {
            if (filter != null) {
                getFileChooser().setFileFilter((FileFilter) filter);
                // Don't clear the filename field, when the user changes
                // the filename filter.
                // FIXME - Maybe we should disable the save
                // button when the name is not matched by the filter?
                //setFileName(null);
                fireContentsChanged(this, -1, -1);
            }
        }

        @Override
        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();
        }

        @Override
        public int getSize() {
            if (filters != null) {
                return filters.length;
            } else {
                return 0;
            }
        }

        @Override
        public Object getElementAt(int index) {
            if (index > getSize() - 1) {
                // This shouldn't happen. Try to recover gracefully.
                return getFileChooser().getFileFilter();
            }
            if (filters != null) {
                return filters[index];
            } else {
                return null;
            }
        }
    }

    /**
     * Acts when DirectoryComboBox has changed the selected item.
     */
    protected class DirectoryComboBoxAction extends AbstractAction {

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

        @Override
        public void actionPerformed(ActionEvent e) {
            if (isAdjusting != 0) {
                return;
            }

            FileSystemTreeModel.Node chosenNode = (FileSystemTreeModel.Node) directoryComboBox.getSelectedItem();
            if (chosenNode != null) {

                File dir = chosenNode.getResolvedFile();
                setRootDirectory(dir);
            }
        }
    }

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

    @Override
    public Action getApproveSelectionAction() {
        return approveSelectionAction;
    }

    protected class DoubleClickListener extends MouseAdapter {

        @Override
        public void mouseClicked(MouseEvent e) {
            // Note: We must not react on mouse clicks with clickCount=1.
            //       Because this interfers with the mouse handling code in
            //       the JBrowser which does list selection.
            JFileChooser fc = getFileChooser();
            if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2 && fc.getDialogType() != JFileChooser.SAVE_DIALOG) {

                // Only react on double click if all selected files are
                // acceptable
                for (TreePath tp:browser.getSelectionPaths()) {
                    FileSystemTreeModel.Node n =(FileSystemTreeModel.Node)tp.getLastPathComponent();
                    if (! fc.accept(n.getFile())) {
                        return;
                    }
                }
                maybeApproveSelection(false);
            }
        }
    }

    /**
     * This method is called, when the user double clicks the JBrowser, or
     * when she clicks at the approve button.
     *
     * @param allowDirectories true to allow selection of directories when
     * isMultiSelectionEnabled == true
     */
    private void maybeApproveSelection(boolean allowDirectories) {
        JFileChooser fc = getFileChooser();
        File selectedFile = null;
        File[] selectedFiles = null;

        String filename = null;
        if (isFileNameFieldVisible()) {
            filename = getFileName();
            if (filename.equals("")) {
                filename = null;
            }
        }

        if (fc.isMultiSelectionEnabled()) {
            TreePath[] selectedPaths = browser.getSelectionPaths();
            if (filename != null) {
                File f = new File(
                        ((FileSystemTreeModel.Node) selectedPaths[0].getLastPathComponent()).getResolvedFile().getParent(),
                        filename);
                selectedFiles = new File[]{f};
            } else {
                ArrayList a = new ArrayList();
                for (int i = 0; i < selectedPaths.length; i++) {
                    File f = ((FileSystemTreeModel.Node) selectedPaths[i].getLastPathComponent()).getResolvedFile();
                    a.add(f);
                }
                if (a.size() > 0) {
                    selectedFiles = a.toArray(new File[a.size()]);
                }
            }

            // see if this is a single directory. If so, don't allow
            // double-click to select it.
            if (!allowDirectories && selectedFiles.length == 1 && selectedFiles[0].isDirectory()) {
                // block selection
                return;
            }
        } else {
            TreePath selectionPath = browser.getSelectionPath();
            if (selectionPath == null) {
                // Abort we cannot approve empty selection
                return;
            }
            FileSystemTreeModel.Node node = (FileSystemTreeModel.Node) browser.getSelectionPath().getLastPathComponent();
            selectedFile = node.getResolvedFile();
            if (filename != null) {
                selectedFile = new File((!node.isLeaf()) ? selectedFile : selectedFile.getParentFile(), filename);
            } else if (fc.getFileSelectionMode() == JFileChooser.FILES_ONLY && !node.isLeaf()) {
                // Abort we cannot approve a directory
                return;
            }
        }

        if (selectedFiles != null || selectedFile != null) {
            if (selectedFiles != null) {
                fc.setSelectedFiles(selectedFiles);
            } else if (fc.isMultiSelectionEnabled()) {
                fc.setSelectedFiles(new File[]{selectedFile});
            } else {
                fc.setSelectedFile(selectedFile);
            }
            fc.approveSelection();
        } else {
            if (fc.isMultiSelectionEnabled()) {
                fc.setSelectedFiles(null);
            } else {
                fc.setSelectedFile(null);
            }
            fc.cancelSelection();
        }

    }

    // *****************************
    // ***** Directory Actions *****
    // *****************************
    @Override
    public Action getNewFolderAction() {
        return newFolderAction;
    }

    /**
     * Creates a new folder.
     */
    protected class NewFolderAction extends AbstractAction {

        protected NewFolderAction() {
            super("New Folder");
        }

        private String showNewFolderDialog() {
            JOptionPane optionPane = new JOptionPane(
                    newFolderDialogPrompt,
                    JOptionPane.PLAIN_MESSAGE,
                    JOptionPane.OK_CANCEL_OPTION);
            // Setup Input
            optionPane.setWantsInput(true);
            optionPane.putClientProperty("PrivateQuaqua.OptionPane.InputFieldDocument",
                    new FilenameDocument());
            optionPane.setInitialSelectionValue(newFolderDefaultName);

            // Setup Options
            optionPane.setOptions(new Object[]{
                        UIManager.getString("FileChooser.createFolderButtonText"),
                        UIManager.getString("FileChooser.cancelButtonText")
                    });
            optionPane.setInitialValue(UIManager.getString("FileChooser.createFolderButtonText"));

            // Show the dialog
            JDialog dialog = optionPane.createDialog(getFileChooser(), newFolderTitleText);
            dialog.setVisible(true);
            dialog.dispose();

            return (optionPane.getValue() == UIManager.getString("FileChooser.createFolderButtonText"))
                    ? (String) optionPane.getInputValue() : null;
        }

        @Override
        public void actionPerformed(ActionEvent actionevent) {
            JFileChooser fc = getFileChooser();
            String newFolderName = showNewFolderDialog();

            if (newFolderName != null) {

                File newFolder;
                FileSystemTreeModel.Node node = (FileSystemTreeModel.Node) browser.getSelectionPath().getLastPathComponent();
                File currentFile = node.getResolvedFile();
                if (node.isLeaf()) {
                    currentFile = currentFile.getParentFile();
                }
                newFolder = new File(currentFile, newFolderName);
                if (newFolder.exists()) {
                    JOptionPane.showMessageDialog(
                            fc,
                            newFolderExistsErrorText,
                            newFolderTitleText, JOptionPane.ERROR_MESSAGE);
                    return;
                }

                try {
                    if (! newFolder.mkdir()) {
                        if (! newFolder.isDirectory()) {
                            throw new IOException("Couldn't create folder \""+newFolder.getName()+"\".");
                        }
                    }
                    fc.rescanCurrentDirectory();
                    fc.setCurrentDirectory(newFolder);
                    if (fc.isMultiSelectionEnabled()) {
                        fc.setSelectedFiles(new File[]{newFolder});
                    } else {
                        fc.setSelectedFile(newFolder);
                    }
                } catch (Exception e) {
                    JOptionPane.showMessageDialog(
                            fc,
                            newFolderErrorText,
                            newFolderTitleText, JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    }

    protected class SaveTextFocusListener implements FocusListener {

        @Override
        public void focusGained(FocusEvent focusevent) {
            updateApproveButtonState();
        }

        @Override
        public void focusLost(FocusEvent focusevent) {
            /* empty */
        }
    }

    protected class SaveTextDocumentListener implements DocumentListener {

        @Override
        public void insertUpdate(DocumentEvent documentevent) {
            textChanged();
        }

        @Override
        public void removeUpdate(DocumentEvent documentevent) {
            textChanged();
        }

        @Override
        public void changedUpdate(DocumentEvent documentevent) {
            //textChanged();
        }

        private void textChanged() {
            if (isAdjusting != 0) {
                return;
            }

            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    JFileChooser fc = getFileChooser();
                    FileSystemTreeModel.Node node = (FileSystemTreeModel.Node) browser.getSelectionPath().getLastPathComponent();
                    File file = node.getResolvedFile();
                    if (fileNameTextField.getText().length() != 0) {
                        if (!node.isLeaf()) {
                            // Don't change the current directory when the user is entering
                            // text into the text field. It confuses our users!
                            // Instead, we update the state of the approve button
                            // only, and then we return!
                            // dir = new File(dir, fileNameTextField.getText());
                            updateApproveButtonState();
                            return;
                        } else {
                            file = new File(fc.getFileSystemView().getParentDirectory(file), fileNameTextField.getText());
                        }
                    }
                    if (fc.isMultiSelectionEnabled()) {
                        fc.setSelectedFiles(new File[]{file});
                    } else {
                        fc.setSelectedFile(file);
                    }
                    updateApproveButtonState();
                }
            });
        }
    }

    /**
     * The FileChooserAncestorListener listens for visibility changes of
     * the JFileChooser.
     * This is used to do validations (refreshes) of the tree model only,
     * when the JFileChooser is showing.
     */
    private class FileChooserAncestorListener implements AncestorListener {

        @Override
        public void ancestorAdded(AncestorEvent event) {
            if (model != null) {
                model.setAutoValidate(UIManager.getBoolean("FileChooser.autovalidate"));
                model.validatePath(browser.getSelectionPath());
                if (sidebarTreeModel != null) {
                    sidebarTreeModel.lazyValidate();
                }
            }
            // We update the approve button state here, because the approve 
            // button can only be made the default button, if it has a root pane
            // ancestor.
            updateApproveButtonState();
            JFileChooser fc = getFileChooser();
            if (fc.getSelectedFile() != null) {
                ensureFileIsVisible(fc, fc.getSelectedFile());
            }
            //QuaquaUtilities.setWindowAlpha(SwingUtilities.getWindowAncestor(event.getAncestorParent()), 230);
        }

        @Override
        public void ancestorRemoved(AncestorEvent event) {
            if (model != null) {
                model.setAutoValidate(false);
                model.stopValidation();
                if (browser.getSelectionPath() != null) {
                    model.invalidatePath(browser.getSelectionPath());
                }
                clearIconCache();
            }
        }

        @Override
        public void ancestorMoved(AncestorEvent event) {
        }
    }
    // *******************************************************
    // ************ FileChooserUI PLAF methods ***************
    // *******************************************************

    /**
     * API method of FileChooserUI.
     */
    @Override
    public void ensureFileIsVisible(JFileChooser fc, final File f) {
        if (browser.getSelectionPaths() != null) {
            TreePath[] paths = browser.getSelectionPaths();
            for (int i = 0; i < paths.length; i++) {
                if (((FileSystemTreeModel.Node) paths[i].getLastPathComponent()).getFile().equals(f)) {
                    browser.ensurePathIsVisible(paths[i]);
                    return;
                }
            }
        } else {
            TreePath fullPath = getFileSystemTreeModel().toPath(f,
                    subtreeModel.getPathToRoot());
            TreePath subPath = getTreeModel().toSubPath(fullPath);
            if (subPath == null) {
                isAdjusting++;
                getTreeModel().setPathToRoot(new Object[]{fullPath.getPathComponent(0)});
                isAdjusting--;
                subPath = fullPath;
            }
            ensurePathIsVisible(fullPath);
        }
    }

    /**
     * API method of FileChooserUI.
     */
    @Override
    public String getApproveButtonText(JFileChooser fc) {
        String buttonText = fc.getApproveButtonText();
        if (buttonText != null) {
            return buttonText;
        } else if (fc.isDirectorySelectionEnabled() && chooseButtonText != null) {
            return chooseButtonText;
        } else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) {
            return openButtonText;
        } else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) {
            return saveButtonText;
        } else {
            return null;
        }
    }

    /**
     * API method of FileChooserUI.
     */
    @Override
    public FileView getFileView(JFileChooser fc) {
        return fileView;
    }

    /**
     * API method of FileChooserUI.
     */
    @Override
    public void rescanCurrentDirectory(JFileChooser fc) {
        // Validation is only necessary, when the JFileChooser is showing.
        if (fc.isShowing()) {
            //clearIconCache();
            if (browser.getSelectionPath() != null) {
                model.lazyInvalidatePath(browser.getSelectionPath());
                model.validatePath(browser.getSelectionPath());
            }
        }
    }
    // *******************************************************
    // ******** End of FileChooserUI PLAF methods ************
    // *******************************************************
    // *******************************************************
    // ********** BasicFileChooserUI PLAF methods ************
    // *******************************************************

    @Override
    public void clearIconCache() {
        try {
            fileView.getClass().getMethod("clearIconCache", new Class[0]).invoke(fileView, new Object[0]);
        } catch (Exception e) {
            // empty
        }
    }

    protected MouseListener createDoubleClickListener(JFileChooser fc) {
        return new DoubleClickListener();
    }

    // *******************************************************
    // ******* End of BasicFileChooserUI PLAF methods ********
    // *******************************************************
    private class SidebarSelectionListener implements TreeSelectionListener {

        @Override
        public void valueChanged(TreeSelectionEvent e) {
            if (isAdjusting != 0) {
                return;
            }

            if (sidebarTree != null && sidebarTree.getSelectionPath() != null) {
                if (sidebarTree.getSelectionPath().getLastPathComponent() instanceof FileInfo) {
                    FileInfo info = (FileInfo) sidebarTree.getSelectionPath().getLastPathComponent();
                    File file = info.lazyGetResolvedFile();
                    setRootDirectory(file);

                    isAdjusting++;
                    JFileChooser fc = getFileChooser();
                    if (file.isDirectory() && fc.isTraversable(file)) {
                        fc.setCurrentDirectory(file);
                    } else {
                        if (fc.isMultiSelectionEnabled()) {
                            fc.setSelectedFiles(new File[]{file});
                        } else {
                            fc.setSelectedFile(file);
                        }
                    }
                    isAdjusting--;
                    /*
                    TreePath path = subtreeModel.getPathToRoot();
                    getFileSystemTreeModel().lazyInvalidatePath(path);
                    getFileSystemTreeModel().validatePath(path);
                     */
                }
            }
        }
    }

    /**
     * Responds to an Open or Save request
     */
    /**
     * Responds to an Open or Save request
     */
    protected class QuaquaApproveSelectionAction extends AbstractAction {

        protected QuaquaApproveSelectionAction() {
            super("approveSelection");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            maybeApproveSelection(true);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy