/*
 * @(#)WindowsFileChooserUI.java	1.58 01/04/02
 *
 * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * This software is the proprietary information of Sun Microsystems, Inc.  
 * Use is subject to license terms.
 * 
 */

package com.sun.java.swing.plaf.windows;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.filechooser.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.DateFormat;
import java.util.*;

import sun.awt.shell.ShellFolder;

/**
 * Windows L&F implementation of a FileChooser.
 *
 * @version 1.58 04/02/01
 * @author Jeff Dinkins
 */
public class WindowsFileChooserUI extends BasicFileChooserUI {

    // The following are private because the implementation of the
    // Windows FileChooser L&F is not complete yet.

    private static final String[] OS_NAMES =
		new String[] { "Windows 3.1", "Windows 95", "Windows NT",
			       "Windows 98", "Windows 2000", "Windows Me" };
    private static int WIN_31 = 0;
    private static int WIN_95 = 1;
    private static int WIN_NT = 2;
    private static int WIN_98 = 3;
    private static int WIN_2k = 4;
    private static int WIN_Me = 5;
    private static String osName = System.getProperty("os.name");
    private static String osVersion = System.getProperty("os.version");
    private static final String OS_NAME = ((osName.equals(OS_NAMES[WIN_98]) && osVersion.startsWith("4.9"))
					   ? "Windows Me" : osName);
    private static final int OS_LEVEL = Arrays.asList(OS_NAMES).indexOf(OS_NAME);

    private JPanel centerPanel;

    private JLabel lookInLabel;
    private JComboBox directoryComboBox;
    private DirectoryComboBoxModel directoryComboBoxModel;
    private ActionListener directoryComboBoxAction = new DirectoryComboBoxAction();

    private FilterComboBoxModel filterComboBoxModel;

    private JTextField filenameTextField;
    private ShortCutPanel shortCutPanel;
    private JToggleButton listViewButton;
    private JToggleButton detailsViewButton;
    private JPanel listViewPanel;
    private JPanel detailsViewPanel;
    private JPanel currentViewPanel;
    private FocusListener editorFocusListener = new FocusAdapter() {
	public void focusLost(FocusEvent e) {
	    if (! e.isTemporary()) {
		applyEdit();
	    }
	}
    };
    private boolean smallIconsView = false;
    private Border loweredBevelBorder;

    private ListSelectionModel listSelectionModel;
    private JList list;
    private JTable detailsTable;

    private JButton approveButton;
    private JButton cancelButton;

    private JPanel buttonPanel;
    private JPanel bottomPanel;

    private JComboBox filterComboBox;

    private static final Dimension hstrut10 = new Dimension(10, 1);
    private static final Dimension hstrut25 = new Dimension(25, 1);

    private static final Dimension vstrut1  = new Dimension(1, 1);
    private static final Dimension vstrut4  = new Dimension(1, 4);
    private static final Dimension vstrut5  = new Dimension(1, 5);
    private static final Dimension vstrut6  = new Dimension(1, 6);
    private static final Dimension vstrut8  = new Dimension(1, 8);

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

    // Preferred and Minimum sizes for the dialog box
    private static int PREF_WIDTH = 425;
    private static int PREF_HEIGHT = 245;
    private static Dimension PREF_SIZE = new Dimension(PREF_WIDTH, PREF_HEIGHT);

    private static int MIN_WIDTH = 425;
    private static int MIN_HEIGHT = 245;
    private static Dimension MIN_SIZE = new Dimension(MIN_WIDTH, MIN_HEIGHT);

    private static int LIST_PREF_WIDTH = 444;
    private static int LIST_PREF_HEIGHT = 138;
    private static Dimension LIST_PREF_SIZE = new Dimension(LIST_PREF_WIDTH, LIST_PREF_HEIGHT);

    private static final int COLUMN_FILENAME = 0;
    private static final int COLUMN_FILESIZE = 1;
    private static final int COLUMN_FILETYPE = 2;
    private static final int COLUMN_FILEDATE = 3;
    private static final int COLUMN_FILEATTR = 4;
    private static final int COLUMN_COLCOUNT = 5;

    private int[] COLUMN_WIDTHS = { 150,  75,  130,  130,  40 };

    // Labels, mnemonics, and tooltips (oh my!)
    private int    lookInLabelMnemonic = 0;
    private String lookInLabelText = null;
    private String saveInLabelText = null;

    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 newFolderToolTipText = null;
    private String newFolderAccessibleName = null;

    private String listViewButtonToolTipText = null;
    private String listViewButtonAccessibleName = null;

    private String detailsViewButtonToolTipText = null;
    private String detailsViewButtonAccessibleName = null;

    private String fileNameHeaderText = null;
    private String fileSizeHeaderText = null;
    private String fileTypeHeaderText = null;
    private String fileDateHeaderText = null;
    private String fileAttrHeaderText = null;

    private Action newFolderAction = new WindowsNewFolderAction();
    private File newFolderFile;
    private BasicFileView fileView = new WindowsFileView();

    //
    // ComponentUI Interface Implementation methods
    //
    public static ComponentUI createUI(JComponent c) {
        return new WindowsFileChooserUI((JFileChooser) c);
    }

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

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

    public void uninstallComponents(JFileChooser fc) {
	fc.removeAll();
    }

    public void installComponents(JFileChooser fc) {
	FileSystemView fsv = fc.getFileSystemView();
	loweredBevelBorder = new BevelBorder(BevelBorder.LOWERED,
					     UIManager.getColor("ToolBar.highlight"),
					     UIManager.getColor("ToolBar.background"),
					     UIManager.getColor("ToolBar.darkShadow"),
					     UIManager.getColor("ToolBar.shadow"));

	fc.setBorder(new EmptyBorder(4, 10, 10, 10));
	fc.setLayout(new BorderLayout(8, 8));

	// ********************************* //
	// **** Construct the top panel **** //
	// ********************************* //

	// Directory manipulation buttons
	JToolBar topPanel = new JToolBar();
	topPanel.setFloatable(false);
	if (OS_LEVEL >= WIN_2k) {
	    topPanel.putClientProperty("JToolBar.isRollover", Boolean.TRUE);
	}

	// Add the top panel to the fileChooser
	fc.add(topPanel, BorderLayout.NORTH);

	// ComboBox Label
     	lookInLabel = new JLabel(lookInLabelText);
     	lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic);
	lookInLabel.setAlignmentX(JComponent.LEFT_ALIGNMENT);
	lookInLabel.setAlignmentY(JComponent.CENTER_ALIGNMENT);
	topPanel.add(Box.createRigidArea(new Dimension(14,0)));
	topPanel.add(lookInLabel);
	topPanel.add(Box.createRigidArea(new Dimension(29,0)));

	// CurrentDir ComboBox
	directoryComboBox = new JComboBox();
	directoryComboBox.putClientProperty( "JComboBox.lightweightKeyboardNavigation", "Lightweight" );
	lookInLabel.setLabelFor(directoryComboBox);
	directoryComboBoxModel = createDirectoryComboBoxModel(fc);
	directoryComboBox.setModel(directoryComboBoxModel);
	directoryComboBox.addActionListener(directoryComboBoxAction);
	directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
	directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT);
	directoryComboBox.setAlignmentY(JComponent.CENTER_ALIGNMENT);
	directoryComboBox.setMaximumRowCount(8);

	topPanel.add(directoryComboBox);
	topPanel.add(Box.createRigidArea(hstrut10));

	// 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);
	upFolderButton.setFocusPainted(false);
	topPanel.add(upFolderButton);
	if (OS_LEVEL < WIN_2k) {
	    topPanel.add(Box.createRigidArea(hstrut10));
	}

	JButton b;

	if (OS_LEVEL == WIN_98) {
	    // Home Button
	    File homeDir = fsv.getHomeDirectory();
	    String toolTipText = homeFolderToolTipText;
	    if (fsv.isRoot(homeDir)) {
		toolTipText = getFileView(fc).getName(homeDir); // Probably "Desktop".
	    }
	    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.setFocusPainted(false);
	    b.addActionListener(getGoHomeAction());
	    topPanel.add(b);
	    topPanel.add(Box.createRigidArea(hstrut10));
	}

	// New Directory Button
	b = new JButton(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);
	b.setFocusPainted(false);
	topPanel.add(b);
	if (OS_LEVEL < WIN_2k) {
	    topPanel.add(Box.createRigidArea(hstrut10));
	}

	// View button group
	ButtonGroup viewButtonGroup = new ButtonGroup();

	class ViewButtonListener implements ActionListener {
	    JFileChooser fc;

	    ViewButtonListener(JFileChooser fc) {
		this.fc = fc;
	    }

	    public void actionPerformed(ActionEvent e) {
		JToggleButton b = (JToggleButton)e.getSource();
		JPanel oldViewPanel = currentViewPanel;

		if (b == detailsViewButton) {
		    if (detailsViewPanel == null) {
			detailsViewPanel = createDetailsView(fc);
			detailsViewPanel.setPreferredSize(LIST_PREF_SIZE);
		    }
		    currentViewPanel = detailsViewPanel;
		} else {
		    currentViewPanel = listViewPanel;
		}
		if (currentViewPanel != oldViewPanel) {
		    centerPanel.remove(oldViewPanel);
		    centerPanel.add(currentViewPanel, BorderLayout.CENTER);
		    centerPanel.revalidate();
		    centerPanel.repaint();
		}
	    }
	}

	ViewButtonListener viewButtonListener = new ViewButtonListener(fc);

	// List Button
	listViewButton = new JToggleButton(listViewIcon);
     	listViewButton.setToolTipText(listViewButtonToolTipText);
     	listViewButton.getAccessibleContext().setAccessibleName(listViewButtonAccessibleName);
	listViewButton.setFocusPainted(false);
	listViewButton.setSelected(true);
	listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
	listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
	listViewButton.setMargin(shrinkwrap);
	listViewButton.addActionListener(viewButtonListener);
	topPanel.add(listViewButton);
	viewButtonGroup.add(listViewButton);

	// Details Button
	detailsViewButton = new JToggleButton(detailsViewIcon);
     	detailsViewButton.setToolTipText(detailsViewButtonToolTipText);
     	detailsViewButton.getAccessibleContext().setAccessibleName(detailsViewButtonAccessibleName);
	detailsViewButton.setFocusPainted(false);
	detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
	detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
	detailsViewButton.setMargin(shrinkwrap);
	detailsViewButton.addActionListener(viewButtonListener);
	topPanel.add(detailsViewButton);
	viewButtonGroup.add(detailsViewButton);

	if (OS_LEVEL >= WIN_2k) {
	    shortCutPanel = new ShortCutPanel();
	    fc.add(shortCutPanel, BorderLayout.BEFORE_LINE_BEGINS);
	}

	// ************************************** //
	// ******* Add the directory pane ******* //
	// ************************************** //
	centerPanel = new JPanel(new BorderLayout());
	listViewPanel = createList(fc);
	listSelectionModel = list.getSelectionModel();
	listViewPanel.setPreferredSize(LIST_PREF_SIZE);
	centerPanel.add(listViewPanel, BorderLayout.CENTER);
	currentViewPanel = listViewPanel;
	centerPanel.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS);
	JComponent accessory = fc.getAccessory();
	if(accessory != null) {
	    getAccessoryPanel().add(accessory);
	}
	fc.add(centerPanel, BorderLayout.CENTER);

	// ********************************** //
	// **** Construct the bottom panel ** //
	// ********************************** //
	getBottomPanel().setLayout(new BoxLayout(getBottomPanel(), BoxLayout.LINE_AXIS));

	// Add the bottom panel to file chooser
	centerPanel.add(getBottomPanel(), BorderLayout.SOUTH);

	// labels
	JPanel labelPanel = new JPanel();
	labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.PAGE_AXIS));

     	JLabel fnl = new JLabel(fileNameLabelText);
     	fnl.setDisplayedMnemonic(fileNameLabelMnemonic);
	fnl.setAlignmentY(0);
	labelPanel.add(fnl);

	labelPanel.add(Box.createRigidArea(vstrut8));

     	JLabel ftl = new JLabel(filesOfTypeLabelText);
     	ftl.setDisplayedMnemonic(filesOfTypeLabelMnemonic);
	labelPanel.add(ftl);

	getBottomPanel().add(labelPanel);
	getBottomPanel().add(Box.createRigidArea(new Dimension(15, 0)));

	// file entry and filters
	JPanel fileAndFilterPanel = new JPanel();
	fileAndFilterPanel.setLayout(new BoxLayout(fileAndFilterPanel, BoxLayout.Y_AXIS));


	filenameTextField = new JTextField() {
	    public Dimension getMaximumSize() {
		return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height);
	    }
	};

	fnl.setLabelFor(filenameTextField);
	filenameTextField.addActionListener(getApproveSelectionAction());
        filenameTextField.addFocusListener(
                new FocusAdapter() {
		    public void focusGained(FocusEvent e) {
			listSelectionModel.clearSelection();
		    }
                }
        );

	File f = fc.getSelectedFile();
	if (f != null) {
	    if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
		setFileName(f.getPath());
	    } else {
		setFileName(f.getName());
	    }
	}

	fileAndFilterPanel.add(filenameTextField);
	fileAndFilterPanel.add(Box.createRigidArea(vstrut6));

	filterComboBoxModel = createFilterComboBoxModel();
	fc.addPropertyChangeListener(filterComboBoxModel);
	filterComboBox = new JComboBox(filterComboBoxModel);
	ftl.setLabelFor(filterComboBox);
	filterComboBox.setRenderer(createFilterComboBoxRenderer());
	fileAndFilterPanel.add(filterComboBox);

	getBottomPanel().add(fileAndFilterPanel);
	getBottomPanel().add(Box.createRigidArea(hstrut10));

	// buttons
	getButtonPanel().setLayout(new BoxLayout(getButtonPanel(), BoxLayout.Y_AXIS));

	approveButton = new JButton(getApproveButtonText(fc)) {
	    public Dimension getMaximumSize() {
		return approveButton.getPreferredSize().width > cancelButton.getPreferredSize().width ?
		       approveButton.getPreferredSize() : cancelButton.getPreferredSize();
	    }
	}; 
	approveButton.setMnemonic(getApproveButtonMnemonic(fc));
	approveButton.addActionListener(getApproveSelectionAction());
	approveButton.setToolTipText(getApproveButtonToolTipText(fc));
	getButtonPanel().add(Box.createRigidArea(vstrut4));
	getButtonPanel().add(approveButton);
	getButtonPanel().add(Box.createRigidArea(vstrut6));

	cancelButton = new JButton(cancelButtonText) {
	    public Dimension getMaximumSize() {
		return approveButton.getPreferredSize().width > cancelButton.getPreferredSize().width ?
		       approveButton.getPreferredSize() : cancelButton.getPreferredSize();
	    }
	}; 
	cancelButton.setMnemonic(cancelButtonMnemonic);
	cancelButton.setToolTipText(cancelButtonToolTipText);
	cancelButton.addActionListener(getCancelSelectionAction());
	getButtonPanel().add(cancelButton);

	if(fc.getControlButtonsAreShown()) {
	    addControlButtons();
	}
    }

    protected JPanel getButtonPanel() {
	if(buttonPanel == null) {
	    buttonPanel = new JPanel();
	}
	return buttonPanel;
    }

    protected JPanel getBottomPanel() {
	if(bottomPanel == null) {
	    bottomPanel = new JPanel();
	}
	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); 


	fileNameHeaderText = UIManager.getString("FileChooser.fileNameHeaderText",l);
	fileSizeHeaderText = UIManager.getString("FileChooser.fileSizeHeaderText",l);
	fileTypeHeaderText = UIManager.getString("FileChooser.fileTypeHeaderText",l);
	fileDateHeaderText = UIManager.getString("FileChooser.fileDateHeaderText",l);
	fileAttrHeaderText = UIManager.getString("FileChooser.fileAttrHeaderText",l);
    }

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

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

    protected ActionMap createActionMap() {
        AbstractAction escAction = new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
		if(editing) {
    		   cancelEdit();
		} else {
                   getFileChooser().cancelSelection();
		}
            }
            public boolean isEnabled(){
                return getFileChooser().isEnabled();
            }
        };
        ActionMap map = new ActionMapUIResource();
        map.put("cancelSelection", escAction);
        return map;
    }

    class ShortCutPanel extends JToolBar implements ActionListener {
	final Dimension buttonSize = new Dimension(83, 54);
	AbstractButton[] buttons;
	File[] files;

	ShortCutPanel() {
	    super(JToolBar.VERTICAL);
	    setBorder(loweredBevelBorder);
	    setFloatable(false);
	    putClientProperty("JToolBar.isRollover", Boolean.TRUE);
	    Color bgColor = new Color(UIManager.getColor("ToolBar.shadow").getRGB());
	    Color fgColor = new Color(UIManager.getColor("ToolBar.highlight").getRGB());
	    setBackground(bgColor);
	    JFileChooser chooser = getFileChooser();
	    FileSystemView fsv = chooser.getFileSystemView();

	    files = ShellFolder.getFolders("fileChooserShortcutPanel");
	    buttons = new AbstractButton[files.length];
	    for (int i = 0; i < files.length; i++) {
		if (fsv.isFileSystemRoot(files[i])) {
		    // Create special File wrapper for drive path
		    files[i] = fsv.createFileObject(files[i].getAbsolutePath());
		}

		String folderName = fsv.getSystemDisplayName(files[i]);
		int index = folderName.lastIndexOf(File.separatorChar);
		if (index >= 0 && index < folderName.length() - 1) {
		    folderName = folderName.substring(index + 1);
		}
		Icon icon = null;
		if (files[i] instanceof ShellFolder) {
		    icon = ((ShellFolder)files[i]).getIcon(true);
		} else {
		    icon = fsv.getSystemIcon(files[i]);
		}
		buttons[i] = new JButton(folderName, icon);//Should be JToggleButton
		buttons[i].setForeground(fgColor);
		//buttons[i].setBackground(bgColor);
		buttons[i].setContentAreaFilled(false);
		buttons[i].setHorizontalTextPosition(AbstractButton.CENTER);
		buttons[i].setVerticalTextPosition(AbstractButton.BOTTOM);
		buttons[i].setAlignmentX(JComponent.CENTER_ALIGNMENT);
		buttons[i].setPreferredSize(buttonSize);
		buttons[i].setMaximumSize(buttonSize);
		buttons[i].addActionListener(this);
		add(buttons[i]);
	    }
	    //doDirectoryChanged(chooser.getCurrentDirectory());
	}

	void doDirectoryChanged(File f) {
	    for (int i=0; i<buttons.length; i++) {
		buttons[i].setSelected(files[i].equals(f));
	    }
	}

	public void actionPerformed(ActionEvent e) {
	    AbstractButton b = (AbstractButton)e.getSource();
	    for (int i=0; i<buttons.length; i++) {
		if (b == buttons[i]) {
		    getFileChooser().setCurrentDirectory(files[i]);
		    break;
		}
	    }
	}
    } // class ShortCutPanel


    private void updateListRowCount() {
	if (smallIconsView) {
	    list.setVisibleRowCount(getModel().getSize() / 3);
	} else {
	    list.setVisibleRowCount(-1);
	}
    }

    protected JPanel createList(JFileChooser fc) {
	JPanel p = new JPanel(new BorderLayout());
	list = new JList();
	list.setCellRenderer(new FileRenderer());
	list.setLayoutOrientation(JList.HORIZONTAL);

	updateListRowCount();

	getModel().addListDataListener(new ListDataListener() {
	    public void intervalAdded(ListDataEvent e) {
		updateListRowCount();
	    }
	    public void intervalRemoved(ListDataEvent e) {
		updateListRowCount();
	    }
	    public void contentsChanged(ListDataEvent e) {
		updateListRowCount();
	    }
	});

	if (fc.isMultiSelectionEnabled()) {
	    list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
	} else {
	    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	}
	list.setModel(getModel());
	list.addListSelectionListener(createListSelectionListener(fc));
	list.addMouseListener(createDoubleClickListener(fc, list));
	list.addMouseListener(createSingleClickListener(fc, list));
	getModel().addListDataListener(new ListDataListener() {
	    public void contentsChanged(ListDataEvent e) {
		// Update the selection after JList has been updated
		new DelayedSelectionUpdater();
	    }
	    public void intervalAdded(ListDataEvent e) {
		int i0 = e.getIndex0();
		int i1 = e.getIndex1();
		if (i0 == i1) {
		    File file = (File)getModel().getElementAt(i0);
		    if (file.equals(newFolderFile)) {
			new DelayedSelectionUpdater(file);
			newFolderFile = null;
		    }
		}
	    }
	    public void intervalRemoved(ListDataEvent e) {
	    }
	});

	JScrollPane scrollpane = new JScrollPane(list);
	scrollpane.setBorder(loweredBevelBorder);
	p.add(scrollpane, BorderLayout.CENTER);
	return p;
    }

    class DetailsTableModel extends AbstractTableModel implements ListDataListener {
	String[] columnNames = {
	    fileNameHeaderText,
	    fileSizeHeaderText,
	    fileTypeHeaderText,
	    fileDateHeaderText,
	    fileAttrHeaderText
	};
	JFileChooser chooser;
	ListModel listModel;

	DetailsTableModel(JFileChooser fc) {
	    this.chooser = fc;
	    listModel = getModel();
	    listModel.addListDataListener(this);
	}

	public int getRowCount() {
	    return listModel.getSize();
	}

	public int getColumnCount() {
	    return COLUMN_COLCOUNT;
	}

	public String getColumnName(int column) {
	    return columnNames[column];
	}

	public Class getColumnClass(int column) {
	    switch (column) {
	      case COLUMN_FILENAME:
		  return File.class;
	      case COLUMN_FILEDATE:
		  return Date.class;
	      default:
		  return super.getColumnClass(column);
	    }
	}

	public Object getValueAt(int row, int col) {
	    // Note: It is very important to avoid getting info on drives, as
	    // this will trigger "No disk in A:" and similar dialogs.
	    //
	    // Use (f.exists() && !chooser.getFileSystemView().isFileSystemRoot(f)) to
	    // determine if it is safe to call methods directly on f.

	    File f = (File)listModel.getElementAt(row);
	    switch (col) {
	      case COLUMN_FILENAME:
		  return f;

	      case COLUMN_FILESIZE:
		  if (!f.exists() || f.isDirectory()) {
		      return null;
		  }
		  return (f.length() / 1024 + 1) + "KB";

	      case COLUMN_FILETYPE:
		  if (!f.exists()) {
		      return null;
		  }
		  return chooser.getFileSystemView().getSystemTypeDescription(f);

	      case COLUMN_FILEDATE:
		  if (!f.exists() || chooser.getFileSystemView().isFileSystemRoot(f)) {
		      return null;
		  }
		  long time = f.lastModified();
		  return (time == 0L) ? null : new Date(time);

	      case COLUMN_FILEATTR:
		  if (!f.exists() || chooser.getFileSystemView().isFileSystemRoot(f)) {
		      return null;
		  }
		  String attributes = "";
		  if (!f.canWrite()) {
		      attributes += "R";
		  }
		  if (f.isHidden()) {
		      attributes += "H";
		  }
		  return attributes;
	    }
	    return null;
	}

	public void setValueAt(Object value, int row, int col) {
	    if (col == COLUMN_FILENAME) {
		JFileChooser chooser = getFileChooser();
		File f = (File)getValueAt(row, col);
		String oldDisplayName = chooser.getName(f);
		String oldFileName = f.getName();
		String newDisplayName = ((String)value).trim();
		String newFileName;

		if (!newDisplayName.equals(oldDisplayName)) {
		    newFileName = newDisplayName;
		    //Check if extension is hidden from user
		    int i1 = oldFileName.length();
		    int i2 = oldDisplayName.length();
		    if (i1 > i2 && oldFileName.charAt(i2) == '.') {
			newFileName = newDisplayName + oldFileName.substring(i2);
		    }

		    // rename
		    FileSystemView fsv = chooser.getFileSystemView();
		    File f2 = fsv.createFileObject(f.getParentFile(), newFileName);
		    if (WindowsFileChooserUI.this.getModel().renameFile(f, f2)) {
			if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
			    chooser.setSelectedFile(f2);
			} else {
			    //Could be because of delay in updating Desktop folder
			    //chooser.setSelectedFile(null);
			}
		    } else {
			// PENDING(jeff) - show a dialog indicating failure
		    }
		}
	    }
	}

	public boolean isCellEditable(int row, int column) {
	    return (column == COLUMN_FILENAME);
	}

	public void contentsChanged(ListDataEvent e) {
	    fireTableDataChanged();
	}
	public void intervalAdded(ListDataEvent e) {
	    fireTableDataChanged();
	}
	public void intervalRemoved(ListDataEvent e) {
	    fireTableDataChanged();
	}
    }

    class DetailsTableCellRenderer extends DefaultTableCellRenderer {
	JFileChooser chooser;
	DateFormat df;

	DetailsTableCellRenderer(JFileChooser chooser) {
	    this.chooser = chooser;
	    df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT,
						chooser.getLocale());
	}

	public void setBounds(int x, int y, int width, int height) { 
	    super.setBounds(x, y, Math.min(width, this.getPreferredSize().width+4), height); 
	}

	public Component getTableCellRendererComponent(JTable table, Object value,
			      boolean isSelected, boolean hasFocus, int row, int column) {

	    if (column == COLUMN_FILESIZE || column == COLUMN_FILEATTR) {
		setHorizontalAlignment(SwingConstants.TRAILING);
	    } else {
		setHorizontalAlignment(SwingConstants.LEADING);
	    }

	    if (column == COLUMN_FILENAME && table.isRowSelected(row) && table.isFocusOwner()) {
		super.setForeground(table.getSelectionForeground());
		super.setBackground(table.getSelectionBackground());
	    } else {
		super.setForeground(table.getForeground());
		super.setBackground(table.getBackground());
	    }
	    setFont(table.getFont());
	    setValue(value); 

	    return this;
	}

	public void setValue(Object value) { 
	    setIcon(null);
	    if (value instanceof File) {
		File file = (File)value;
		String fileName = chooser.getName(file);
		setText(fileName);
		Icon icon = chooser.getIcon(file);
		setIcon(icon);
	    } else if (value instanceof Date) {
		setText((value == null) ? "" : df.format((Date)value));
	    } else {
		super.setValue(value);
	    }
	}
    }

    protected JPanel createDetailsView(JFileChooser fc) {
	final JFileChooser chooser = fc;
	JPanel p = new JPanel(new BorderLayout());

	DetailsTableModel detailsTableModel = new DetailsTableModel(chooser);

	detailsTable = new JTable(detailsTableModel) {
	    // Handle Escape key events here
	    protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
		if (e.getKeyCode() == KeyEvent.VK_ESCAPE && getCellEditor() == null) {
		    // We are not editing, forward to filechooser.
		    chooser.dispatchEvent(e);
		    return true;
		}

		boolean retValue = super.processKeyBinding(ks, e, condition, pressed);

		// Look for key representing start of filename
// 		if (!retValue && condition == WHEN_ANCESTOR_OF_FOCUSED_COMPONENT && isFocusOwner()) {
// 		    // We do not have a binding for the event.
// 		    // Only attempt to install the editor on a KEY_PRESSED,
// 		    if (e == null || e.getID() != KeyEvent.KEY_PRESSED) {
// 			return false;
// 		    }
// 		    // Don't start when just a modifier is pressed
// 		    int code = e.getKeyCode();
// 		    if (code == KeyEvent.VK_SHIFT || code == KeyEvent.VK_CONTROL ||
// 			code == KeyEvent.VK_ALT) {
// 			return false;
// 		    }
// 		    //dispatch the key
// 		}
		return retValue;
	    }
	};

	detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
	detailsTable.setShowGrid(false);
	detailsTable.setSelectionModel(listSelectionModel); 
	detailsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);

	TableColumnModel columnModel = detailsTable.getColumnModel();
	TableColumn[] columns = new TableColumn[COLUMN_COLCOUNT];

	for (int i = 0; i < COLUMN_COLCOUNT; i++) {
	    columns[i] = columnModel.getColumn(i);
	    columns[i].setPreferredWidth(COLUMN_WIDTHS[i]);
	}

	TableCellRenderer cellRenderer = new DetailsTableCellRenderer(chooser);
	detailsTable.setDefaultRenderer(File.class, cellRenderer);
	detailsTable.setDefaultRenderer(Date.class, cellRenderer);
	detailsTable.setDefaultRenderer(Object.class, cellRenderer);

	// Install cell editor for editing file name
	final JTextField tf = new JTextField();
	tf.addFocusListener(editorFocusListener);
	columns[COLUMN_FILENAME].setCellEditor(new DefaultCellEditor(tf) {
	    public boolean isCellEditable(EventObject e) {
		if (e instanceof MouseEvent) { 
		    MouseEvent me = (MouseEvent)e;
		    int index = detailsTable.rowAtPoint(me.getPoint());
		    return (me.getClickCount() == 1 && detailsTable.isRowSelected(index));
		}
		return super.isCellEditable(e);
	    }

	    public Component getTableCellEditorComponent(JTable table, Object value,
							 boolean isSelected, int row, int column) {
		Component comp = super.getTableCellEditorComponent(table, value, isSelected, row, column);
		if (value instanceof File) {
		    tf.setText(chooser.getName((File)value));
		    tf.requestFocus();
		    tf.selectAll();
		}		
		return comp;
	    }
	});

	/* FIX ME!
	 * This is a temporary fix, and should be revised after
	 * JTable supports Right-to-Left ComponentOrientatin.
	 */
	if (!detailsTable.getComponentOrientation().isLeftToRight()) {
	    detailsTable.moveColumn(4, 0);
	    detailsTable.moveColumn(4, 1);
	    detailsTable.moveColumn(4, 2);
	    detailsTable.moveColumn(4, 3);
	}

	JList fakeList = new JList(detailsTableModel.listModel) {
	    JTable table = detailsTable;

	    public int locationToIndex(Point location) {
		return table.rowAtPoint(location);
	    }

	    public Rectangle getCellBounds(int index0, int index1) {
		Rectangle r0 = table.getCellRect(index0, COLUMN_FILENAME, false);
		Rectangle r1 = table.getCellRect(index1, COLUMN_FILENAME, false);
		return r0.union(r1);
	    }
	    
	    public Object getSelectedValue() {
		return table.getValueAt(table.getSelectedRow(), COLUMN_FILENAME);
	    }

	    public Component add(Component comp) {
		if (comp instanceof JTextField) {
		    return table.add(comp);
		} else {
		    return super.add(comp);
		}
	    }

	    public void repaint() {
		if (table != null)
		    table.repaint();
	    }
	};

	fakeList.setSelectionModel(listSelectionModel);
	detailsTable.addMouseListener(createDoubleClickListener(chooser, fakeList));
	//detailsTable.addMouseListener(createSingleClickListener(chooser, fakeList));

	JScrollPane scrollpane = new JScrollPane(detailsTable);
        LookAndFeel.installColors(scrollpane.getViewport(), "Table.background", "Table.foreground");
	scrollpane.setBorder(loweredBevelBorder);

	scrollpane.addComponentListener(new ComponentAdapter() {
	    public void componentResized(ComponentEvent e) {
		JScrollPane sp = (JScrollPane)e.getComponent();
		fixNameColumnWidth(sp.getViewport().getSize().width);
		sp.removeComponentListener(this);
	    }
	});

	p.add(scrollpane, BorderLayout.CENTER);
	return p;
    }

    private void fixNameColumnWidth(int viewWidth) {
	TableColumn nameCol = detailsTable.getColumnModel().getColumn(COLUMN_FILENAME);
	int tableWidth = detailsTable.getPreferredSize().width;

	if (tableWidth < viewWidth) {
	    nameCol.setPreferredWidth(nameCol.getPreferredWidth() + viewWidth - tableWidth);
	}
    }

    private class DelayedSelectionUpdater implements Runnable {
	File editFile;

	DelayedSelectionUpdater() {
	    this(null);
	}

	DelayedSelectionUpdater(File editFile) {
	    this.editFile = editFile;
	    SwingUtilities.invokeLater(this);
	}

	public void run() {
	    setFileSelected();
	    if (editFile != null) {
		editFileName(getModel().indexOf(editFile));
		editFile = null;
	    }
	}
    }


    /**
     * Creates a selection listener for the list of files and directories.
     *
     * @param fc a <code>JFileChooser</code>
     * @return a <code>ListSelectionListener</code>
     */
    public ListSelectionListener createListSelectionListener(JFileChooser fc) {
	return new SelectionListener() {
	    public void valueChanged(ListSelectionEvent e) {
		if (!e.getValueIsAdjusting()) {
		    JFileChooser chooser = getFileChooser();
		    JList list = (JList) e.getSource();

		    File file = (File) list.getSelectedValue();

		    if (file != null
			&& file.isDirectory()
			&& chooser.isTraversable(file)
			&& (chooser.getFileSelectionMode() == chooser.FILES_ONLY
			    || !chooser.getFileSystemView().isFileSystem(file))) {

			setDirectorySelected(true);
			setDirectory(file);
			chooser.setSelectedFile(null);
			return;
		    } else {
			setDirectorySelected(false);
		    }

		    if (file != null) {
		       chooser.setSelectedFile(file);
		    }

		    if (chooser.isMultiSelectionEnabled()) {
			Object[] objects = list.getSelectedValues();
			if (objects != null) {
			    File[] files = new File[objects.length];
			    for (int i = 0; i < objects.length; i++) {
				files[i] = (File) objects[i];
			    }
			    chooser.setSelectedFiles(files);
			}
		    }
		}
	    }
	};
    }

    private MouseListener createSingleClickListener(JFileChooser fc, JList list) {
	return new SingleClickListener(list);
    }

    int lastIndex = -1;
    boolean editing = false;
    int editX = 20;

    private int getEditIndex() {
	return lastIndex;
    }

    private void setEditIndex(int i) {
	lastIndex = i;
    }

    private void resetEditIndex() {
	lastIndex = -1;
    }

    private void cancelEdit() {
	if (editing) {
	    editing = false;
	    list.remove(editCell);
	    centerPanel.repaint();
	} else if (detailsTable != null && detailsTable.isEditing()) {
	    detailsTable.getCellEditor().cancelCellEditing();
	}
    }

    JTextField editCell = null;

    private void editFileName(int index) {
	ensureIndexIsVisible(index);
	if (listViewPanel.isVisible()) {
	    editing = true;
	    Rectangle r = list.getCellBounds(index, index);
	    if (editCell == null) {
		editCell = new JTextField();
		editCell.addActionListener(new EditActionListener());
		editCell.addFocusListener(editorFocusListener);
		editCell.setNextFocusableComponent(list);
	    }
	    list.add(editCell);
	    File f = (File)getModel().getElementAt(index);
	    editCell.setText(getFileChooser().getName(f));
	    if (list.getComponentOrientation().isLeftToRight()) {
		editCell.setBounds(editX + r.x, r.y, r.width - editX, r.height);
	    } else {
		editCell.setBounds(r.x, r.y, r.width - editX, r.height);
	    }
	    editCell.requestFocus();
	    editCell.selectAll();
	} else if (detailsViewPanel.isVisible()) {
	    detailsTable.editCellAt(index, COLUMN_FILENAME);
	}
    }


    protected class SingleClickListener extends MouseAdapter {
	JList list;

	public  SingleClickListener(JList list) {
	    this.list = list;
	}

	public void mouseClicked(MouseEvent e) {
	    if (SwingUtilities.isLeftMouseButton(e)) {
		if (e.getClickCount() == 1) {
		    int index = list.locationToIndex(e.getPoint());
		    if (index >= 0 && getEditIndex() == index && !editing) {
			editFileName(index);
		    } else {
			if (index >= 0) {
			    setEditIndex(index);
			} else {
			    resetEditIndex();
			}
		    }
		} else {
		    // on double click (open or drill down one directory) be
		    // sure to clear the edit index
		    resetEditIndex();
		}
	    }	    
	}
    }

    public Action getNewFolderAction() {
	return newFolderAction;
    }

    /**
     * Creates a new folder.
     */
    protected class WindowsNewFolderAction extends NewFolderAction {
	public void actionPerformed(ActionEvent e) {
	    JFileChooser fc = getFileChooser();
	    File oldFile = fc.getSelectedFile();
	    super.actionPerformed(e);
	    File newFile = fc.getSelectedFile();
	    if (newFile != null && !newFile.equals(oldFile) && newFile.isDirectory()) {
		newFolderFile = newFile;
	    }
	}
    }

    class EditActionListener implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    applyEdit();
	} 
    }

    private void applyEdit() {
	if (editing) {
	    JFileChooser chooser = getFileChooser();
	    File f = null;
	    if (isDirectorySelected()) {
		f = getDirectory();
	    } else {
		f = chooser.getSelectedFile();
	    }
	    if (f != null) {
		String oldDisplayName = chooser.getName(f);
		String oldFileName = f.getName();
		String newDisplayName = editCell.getText().trim();
		String newFileName;

		if (!newDisplayName.equals(oldDisplayName)) {
		    newFileName = newDisplayName;
		    //Check if extension is hidden from user
		    int i1 = oldFileName.length();
		    int i2 = oldDisplayName.length();
		    if (i1 > i2 && oldFileName.charAt(i2) == '.') {
			newFileName = newDisplayName + oldFileName.substring(i2);
		    }

		    // rename
		    FileSystemView fsv = chooser.getFileSystemView();
		    File f2 = fsv.createFileObject(f.getParentFile(), newFileName);
		    if (getModel().renameFile(f, f2)) {
			if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
			    chooser.setSelectedFile(f2);
			} else {
			    //Could be because of delay in updating Desktop folder
			    //chooser.setSelectedFile(null);
			}
		    } else {
			// PENDING(jeff) - show a dialog indicating failure
		    }
		}
	    }
	} 
        if (detailsTable != null && detailsTable.isEditing()) {
            detailsTable.getCellEditor().stopCellEditing();
        }
	cancelEdit();
    }

    protected class FileRenderer extends DefaultListCellRenderer  {

	public void setBounds(int x, int y, int width, int height) { 
	    super.setBounds(x, y, Math.min(width, this.getPreferredSize().width+4), height); 
	}

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

	    super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
	    File file = (File) value;
	    String fileName = getFileChooser().getName(file);
	    setText(fileName);

	    Icon icon = getFileChooser().getIcon(file);
	    setIcon(icon);

	    if(isSelected) {
		// PENDING(jeff) - grab padding (4) below from defaults table.
		editX = icon.getIconWidth() + 4;
	    }

	    return this;
	}
    }

    public void uninstallUI(JComponent c) {
	// Remove listeners
	c.removePropertyChangeListener(filterComboBoxModel);
	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);
	if (d != null) {
	    return new Dimension(d.width < prefWidth ? prefWidth : d.width,
				 d.height < PREF_SIZE.height ? PREF_SIZE.height : d.height);
	} else {
	    return new Dimension(prefWidth, PREF_SIZE.height);
	}
    }

    /**
     * Returns the minimum size of the <code>JFileChooser</code>.
     *
     * @param c  a <code>JFileChooser</code>
     * @return   a <code>Dimension</code> specifying the minimum
     *           width and height of the file chooser
     */
    public Dimension getMinimumSize(JComponent c) {
	return MIN_SIZE;
    }

    /**
     * Returns the maximum size of the <code>JFileChooser</code>.
     *
     * @param c  a <code>JFileChooser</code>
     * @return   a <code>Dimension</code> specifying the maximum 
     *           width and height of the file chooser
     */
    public Dimension getMaximumSize(JComponent c) {
	return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
    }

    void setFileSelected() {
	File f = getFileChooser().getSelectedFile();
	int i;
	if (f != null && (i = getModel().indexOf(f)) >= 0) {
	    listSelectionModel.setSelectionInterval(i, i);
	    ensureIndexIsVisible(i);
	} else if (!isDirectorySelected()) {
	    listSelectionModel.clearSelection();
	}
    }

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

    private void doSelectedFileChanged(PropertyChangeEvent e) {
	applyEdit();
	File f = (File) e.getNewValue();
	JFileChooser fc = getFileChooser();
	if (f != null 
	    && ((fc.isFileSelectionEnabled() && !f.isDirectory())
		|| (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {

	    if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
		setFileName(f.getPath());
	    } else {
		setFileName(f.getName());
	    }
	} else {
	    setFileName(null);
	}
	setFileSelected();
    }
    
    private void doDirectoryChanged(PropertyChangeEvent e) {
	JFileChooser fc = getFileChooser();
	FileSystemView fsv = fc.getFileSystemView();

	applyEdit();
	resetEditIndex();
	clearIconCache();
	listSelectionModel.clearSelection();
	ensureIndexIsVisible(0);
	File currentDirectory = fc.getCurrentDirectory();
	if (shortCutPanel != null) {
	    shortCutPanel.doDirectoryChanged(currentDirectory);
	}
	if(currentDirectory != null) {
	    directoryComboBoxModel.addItem(currentDirectory);
	    // Currently can not create folder in the Desktop folder on Windows
	    // (ShellFolder limitation)
	    getNewFolderAction().setEnabled(fsv.isFileSystem(currentDirectory)
					    && currentDirectory.canWrite());
	    getChangeToParentDirectoryAction().setEnabled(!fsv.isRoot(currentDirectory));

	    if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
		if (fsv.isFileSystem(currentDirectory)) {
		    setFileName(currentDirectory.getPath());
		} else {
		    setFileName(null);
		}
	    }
	}
    }

    private void doFilterChanged(PropertyChangeEvent e) {
	applyEdit();
	resetEditIndex();
	clearIconCache();
	listSelectionModel.clearSelection();
    }

    private void doFileSelectionModeChanged(PropertyChangeEvent e) {
	applyEdit();
	resetEditIndex();
	clearIconCache();
	listSelectionModel.clearSelection();

	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 doMultiSelectionChanged(PropertyChangeEvent e) {
	if (getFileChooser().isMultiSelectionEnabled()) {
	    listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
	} else {
	    listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	    listSelectionModel.clearSelection();
	    getFileChooser().setSelectedFiles(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));
	approveButton.setMnemonic(getApproveButtonMnemonic(chooser));
    }

    private void doDialogTypeChanged(PropertyChangeEvent e) {
	JFileChooser chooser = getFileChooser();
	approveButton.setText(getApproveButtonText(chooser));
	approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
	approveButton.setMnemonic(getApproveButtonMnemonic(chooser));
	if (chooser.getDialogType() == JFileChooser.SAVE_DIALOG) {
	    lookInLabel.setText(saveInLabelText);
	} else {
	    lookInLabel.setText(lookInLabelText);
	}
    }

    private void doApproveButtonMnemonicChanged(PropertyChangeEvent e) {
	approveButton.setMnemonic(getApproveButtonMnemonic(getFileChooser()));
    }

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

    /*
     * Listen for filechooser property changes, such as
     * the selected file changing, or the type of the dialog changing.
     */
    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.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.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
		    doMultiSelectionChanged(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()) {
			SwingUtilities.applyComponentOrientation(cc, o);
		    }
		}
	    }
	};
    }


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

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

    private void ensureIndexIsVisible(int i) {
	if (i >= 0) {
	    list.ensureIndexIsVisible(i);
	    if (detailsTable != null) {
		detailsTable.scrollRectToVisible(detailsTable.getCellRect(i, COLUMN_FILENAME, true));
	    }
	}
    }

    public void ensureFileIsVisible(JFileChooser fc, File f) {
	ensureIndexIsVisible(getModel().indexOf(f));
    }

    public void rescanCurrentDirectory(JFileChooser fc) {
	getModel().validateFileCache();
    }

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

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

    /**
     * Property to remember whether a directory is currently selected in the UI.
     * This is normally called by the UI on a selection event.
     *
     * @param directorySelected if a directory is currently selected.
     * @since 1.4
     */
    protected void setDirectorySelected(boolean directorySelected) {
	super.setDirectorySelected(directorySelected);
	JFileChooser chooser = getFileChooser();
	if(directorySelected) {
	    approveButton.setText(directoryOpenButtonText);
	    approveButton.setToolTipText(directoryOpenButtonToolTipText);
	    approveButton.setMnemonic(directoryOpenButtonMnemonic);
	} else {
	    approveButton.setText(getApproveButtonText(chooser));
	    approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
	    approveButton.setMnemonic(getApproveButtonMnemonic(chooser));
	}
    }

    public String getDirectoryName() {
	// PENDING(jeff) - get the name from the directory combobox
	return null;
    }

    public void setDirectoryName(String dirname) {
	// PENDING(jeff) - set the name in the directory combobox
    }

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

    //
    // Renderer for DirectoryComboBox
    //
    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;
	}
    }

    final static int space = 10;
    class IndentIcon implements Icon {

	Icon icon = null;
	int depth = 0;

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

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

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

    }

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

    /**
     * Data model for a type-face selection combo-box.
     */
    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() {
	    // Add the current directory to the model, and make it the
	    // selectedDirectory
	    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();

	    directories.addAll(Arrays.asList(ShellFolder.getFolders("fileChooserComboBoxFolders")));

	    // 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) {
		// Maybe drive is not ready. Can't abort here.
		canonical = directory;
	    }

	    // create File instances of each directory leading up to the top
	    try {
		File sf = ShellFolder.getShellFolder(canonical);
		File f = sf;
		Vector path = new Vector(10);
		do {
		    path.addElement(f);
		} while ((f = f.getParentFile()) != null);

		int pathCount = path.size();
		// Insert chain at appropriate place in vector
		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 void setSelectedItem(Object selectedDirectory) {
	    this.selectedDirectory = (File)selectedDirectory;
            fireContentsChanged(this, -1, -1);
	}

	public Object getSelectedItem() {
	    return selectedDirectory;
	}

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

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

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

    /**
     * Render different type sizes and styles.
     */
    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;
	}
    }

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

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

	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() {
	    if(filters != null) {
		return filters.length;
	    } else {
		return 0;
	    }
	}

	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;
	    }
	}
    }

    public void valueChanged(ListSelectionEvent e) {
	JFileChooser fc = getFileChooser();
	File f = fc.getSelectedFile();
	if (!e.getValueIsAdjusting() && f != null && !getFileChooser().isTraversable(f)) {
	    if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
		setFileName(f.getPath());
	    } else {
		setFileName(f.getName());
	    }
	}
    }

    /**
     * Acts when DirectoryComboBox has changed the selected item.
     */
    protected class DirectoryComboBoxAction implements ActionListener {




	public void actionPerformed(ActionEvent e) {
	    File f = (File)directoryComboBox.getSelectedItem();
	    getFileChooser().setCurrentDirectory(f);
	}
    }

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

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

    // ***********************
    // * FileView operations *
    // ***********************
    protected class WindowsFileView extends BasicFileView {
	/* FileView type descriptions */

	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;
	}
    }
}

