net.sf.vfsjfilechooser.filepane.VFSFilePane.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.vfsjfilechooser.filepane.VFSFilePane.java

Source

/*
 *
 * Copyright (C) 2005-2008 Yves Zoundi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * under the License.
 */
package net.sf.vfsjfilechooser.filepane;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.EventObject;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.ButtonGroup;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListSelectionModel;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.text.Position;

import net.sf.vfsjfilechooser.VFSJFileChooser;
import net.sf.vfsjfilechooser.constants.VFSJFileChooserConstants;
import net.sf.vfsjfilechooser.filechooser.AbstractVFSFileSystemView;
import net.sf.vfsjfilechooser.plaf.VFSFileChooserUIAccessorIF;
import net.sf.vfsjfilechooser.plaf.basic.BasicVFSDirectoryModel;
import net.sf.vfsjfilechooser.utils.FileObjectComparatorFactory;
import net.sf.vfsjfilechooser.utils.SwingCommonsUtilities;
import net.sf.vfsjfilechooser.utils.VFSResources;
import net.sf.vfsjfilechooser.utils.VFSUtils;

import org.apache.commons.vfs.FileObject;

/**
 * This class is based on sun.swing.FilePane
 * @author Yves Zoundi <yveszoundi at users dot sf dot net>
 * @version 0.0.1
 */
@SuppressWarnings("serial")
public final class VFSFilePane extends JPanel implements PropertyChangeListener {
    public final static String ACTION_APPROVE_SELECTION = "approveSelection";
    public final static String ACTION_CANCEL = "cancelSelection";
    public final static String ACTION_EDIT_FILE_NAME = "editFileName";
    public final static String ACTION_REFRESH = "refresh";
    public final static String ACTION_CHANGE_TO_PARENT_DIRECTORY = "Go Up";
    public final static String ACTION_NEW_FOLDER = "New Folder";
    public final static String ACTION_VIEW_LIST = "viewTypeList";
    public final static String ACTION_VIEW_DETAILS = "viewTypeDetails";
    public static final int VIEWTYPE_LIST = 0;
    public static final int VIEWTYPE_DETAILS = 1;
    private static final int VIEWTYPE_COUNT = 2;
    private static final Cursor waitCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
    private static FocusListener repaintListener = new FocusListener() {
        public void focusGained(FocusEvent fe) {
            repaintSelection(fe.getSource());
        }

        public void focusLost(FocusEvent fe) {
            repaintSelection(fe.getSource());
        }

        private void repaintSelection(Object source) {
            if (source instanceof JList) {
                repaintListSelection((JList) source);
            } else if (source instanceof JTable) {
                repaintTableSelection((JTable) source);
            }
        }

        private void repaintListSelection(JList list) {
            int[] indices = list.getSelectedIndices();

            for (int i : indices) {
                Rectangle bounds = list.getCellBounds(i, i);
                list.repaint(bounds);
            }
        }

        private void repaintTableSelection(JTable table) {
            int minRow = table.getSelectionModel().getMinSelectionIndex();
            int maxRow = table.getSelectionModel().getMaxSelectionIndex();

            if ((minRow == -1) || (maxRow == -1)) {
                return;
            }

            int col0 = table.convertColumnIndexToView(COLUMN_FILENAME);

            Rectangle first = table.getCellRect(minRow, col0, false);
            Rectangle last = table.getCellRect(maxRow, col0, false);
            Rectangle dirty = first.union(last);
            table.repaint(dirty);
        }
    };

    private static final int COLUMN_FILENAME = 0;
    private static final int COLUMN_SIZE = 1;
    private static final int COLUMN_DATE = 2;

    // Constants for actions. These are used for the actions' ACTION_COMMAND_KEY
    // and as keys in the action maps for FilePane and the corresponding UI classes
    //    private DetailsTableRowSorter rowSorter;
    private JTable detailsTable;
    private Action[] actions;
    private int viewType = -1;
    private JPanel[] viewPanels = new JPanel[VIEWTYPE_COUNT];
    private JPanel currentViewPanel;
    private String[] viewTypeActionNames;
    private JPopupMenu contextMenu;
    private JMenu viewMenu;
    private String viewMenuLabelText;
    private String refreshActionLabelText;
    private String newFolderActionLabelText;
    private String renameErrorTitleText;
    private String renameErrorText;
    private String renameErrorFileExistsText;
    private String fileNameHeaderText = null;
    private String fileSizeHeaderText = null;
    private String fileDateHeaderText = null;
    private transient final FocusListener editorFocusListener = new FocusAdapter() {
        @Override
        public void focusLost(FocusEvent e) {
            if (!e.isTemporary()) {
                applyEdit();
            }
        }
    };

    private boolean smallIconsView = false;
    private Border listViewBorder;
    private Color listViewBackground;
    private boolean listViewWindowsStyle;
    private boolean readOnly;
    private ListSelectionModel listSelectionModel;
    private JList list;
    private DetailsTableModel detailsTableModel;

    // Provides a way to recognize a newly created folder, so it can
    // be selected when it appears in the model.
    private FileObject newFolderFile;

    // Used for accessing methods in the corresponding UI class
    private VFSFileChooserUIAccessorIF fileChooserUIAccessor;
    private DetailsTableCellEditor tableCellEditor;
    private int lastIndex = -1;
    private FileObject editFile = null;
    private int editX = 20;
    private JTextField editCell = null;
    protected Action newFolderAction;
    private Handler handler;

    // details view
    @SuppressWarnings("unused")
    private final transient KeyListener detailsKeyListener = new KeyAdapter() {
        private final long timeFactor;
        private final StringBuilder typedString = new StringBuilder();
        private long lastTime = 1000L;

        {
            Long l = (Long) UIManager.get("Table.timeFactor");
            timeFactor = (l != null) ? l : 1000L;
        }

        /**
         * Moves the keyboard focus to the first element whose prefix matches
         * the sequence of alphanumeric keys pressed by the user with delay
         * less than value of <code>timeFactor</code>. Subsequent same key
         * presses move the keyboard focus to the next object that starts with
         * the same letter until another key is pressed, then it is treated
         * as the prefix with appropriate number of the same letters followed
         * by first typed another letter.
         */
        @Override
        public void keyTyped(KeyEvent e) {
            BasicVFSDirectoryModel model = getModel();
            int rowCount = model.getSize();

            if ((detailsTable == null) || (rowCount == 0) || e.isAltDown() || e.isControlDown() || e.isMetaDown()) {
                return;
            }

            InputMap inputMap = detailsTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
            KeyStroke key = KeyStroke.getKeyStrokeForEvent(e);

            if ((inputMap != null) && (inputMap.get(key) != null)) {
                return;
            }

            int startIndex = detailsTable.getSelectionModel().getLeadSelectionIndex();

            if (startIndex < 0) {
                startIndex = 0;
            }

            if (startIndex >= rowCount) {
                startIndex = rowCount - 1;
            }

            char c = e.getKeyChar();

            long time = e.getWhen();

            if ((time - lastTime) < timeFactor) {
                if ((typedString.length() == 1) && (typedString.charAt(0) == c)) {
                    // Subsequent same key presses move the keyboard focus to the next
                    // object that starts with the same letter.
                    startIndex++;
                } else {
                    typedString.append(c);
                }
            } else {
                startIndex++;

                typedString.setLength(0);
                typedString.append(c);
            }

            lastTime = time;

            if (startIndex >= rowCount) {
                startIndex = 0;
            }

            // Find next file 
            int index = getNextMatch(startIndex, rowCount - 1);

            if ((index < 0) && (startIndex > 0)) { // wrap
                index = getNextMatch(0, startIndex - 1);
            }

            if (index >= 0) {
                detailsTable.getSelectionModel().setSelectionInterval(index, index);

                Rectangle cellRect = detailsTable.getCellRect(index,
                        detailsTable.convertColumnIndexToView(COLUMN_FILENAME), false);
                detailsTable.scrollRectToVisible(cellRect);
            }
        }

        private int getNextMatch(int startIndex, int finishIndex) {
            return -1;
        }
    };

    /**
     * @param fileChooserUIAccessor
     */
    public VFSFilePane(VFSFileChooserUIAccessorIF fileChooserUIAccessor) {
        super(new BorderLayout());

        this.fileChooserUIAccessor = fileChooserUIAccessor;
        installDefaults();
        createActionMap();
    }

    /**
     *
     */
    public void uninstallUI() {
        if (getModel() != null) {
            getModel().removePropertyChangeListener(this);
        }
    }

    /**
     * @return
     */
    protected VFSJFileChooser getFileChooser() {
        return fileChooserUIAccessor.getFileChooser();
    }

    /**
     * @return
     */
    protected BasicVFSDirectoryModel getModel() {
        return fileChooserUIAccessor.getModel();
    }

    /**
     * @return
     */
    public int getViewType() {
        return viewType;
    }

    /**
     * @param viewType
     */
    public void setViewType(int viewType) {
        int oldValue = this.viewType;

        if (viewType == oldValue) {
            return;
        }

        this.viewType = viewType;

        if (viewType == VIEWTYPE_LIST) {
            if (viewPanels[viewType] == null) {
                JPanel p = fileChooserUIAccessor.createList();

                if (p == null) {
                    p = createList();
                }

                setViewPanel(viewType, p);
            }

            list.setLayoutOrientation(JList.VERTICAL_WRAP);
        } else if (viewType == VIEWTYPE_DETAILS) {
            if (viewPanels[viewType] == null) {
                JPanel p = fileChooserUIAccessor.createDetailsView();

                if (p == null) {
                    p = createDetailsView();
                }

                setViewPanel(viewType, p);
            }
        }

        JPanel oldViewPanel = currentViewPanel;
        currentViewPanel = viewPanels[viewType];

        if (currentViewPanel != oldViewPanel) {
            if (oldViewPanel != null) {
                remove(oldViewPanel);
            }

            add(currentViewPanel, BorderLayout.CENTER);
            revalidate();
            repaint();
        }

        updateViewMenu();
        firePropertyChange("viewType", oldValue, viewType);
    }

    /**
     * @param viewType
     * @param viewPanel
     */
    public void setViewPanel(int viewType, JPanel viewPanel) {
        viewPanels[viewType] = viewPanel;
        recursivelySetInheritsPopupMenu(viewPanel, true);

        // switch to list view
        if (viewType == VIEWTYPE_LIST) {
            list = (JList) findChildComponent(viewPanels[viewType], JList.class);

            if (listSelectionModel == null) {
                listSelectionModel = list.getSelectionModel();

                if (detailsTable != null) {
                    detailsTable.setSelectionModel(listSelectionModel);
                }
            } else {
                list.setSelectionModel(listSelectionModel);
            }
        }

        // switch to details view
        else if (viewType == VIEWTYPE_DETAILS) {
            detailsTable = (JTable) findChildComponent(viewPanels[viewType], JTable.class);
            detailsTable.setRowHeight(Math.max(detailsTable.getFont().getSize() + 4, 16 + 1));

            if (listSelectionModel != null) {
                detailsTable.setSelectionModel(listSelectionModel);
            }
        }

        if (this.viewType == viewType) {
            if (currentViewPanel != null) {
                remove(currentViewPanel);
            }

            currentViewPanel = viewPanel;
            add(currentViewPanel, BorderLayout.CENTER);
            revalidate();
            repaint();
        }
    }

    /**
     * @param viewType
     * @return
     */
    public Action getViewTypeAction(int viewType) {
        return new ViewTypeAction(viewType);
    }

    private static void recursivelySetInheritsPopupMenu(Container container, boolean b) {
        if (container instanceof JComponent) {
            ((JComponent) container).setInheritsPopupMenu(b);
        }

        final Component[] components = container.getComponents();

        for (Component component : components) {
            recursivelySetInheritsPopupMenu((Container) component, b);
        }
    }

    protected void installDefaults() {

        listViewBorder = UIManager.getBorder("FileChooser.listViewBorder");
        listViewBackground = UIManager.getColor("FileChooser.listViewBackground");
        listViewWindowsStyle = UIManager.getBoolean("FileChooser.listViewWindowsStyle");
        readOnly = UIManager.getBoolean("FileChooser.readOnly");

        // TODO: On windows, get the following localized strings from the OS
        viewMenuLabelText = VFSResources.getMessage("VFSJFileChooser.viewMenuLabelText");
        refreshActionLabelText = VFSResources.getMessage("VFSJFileChooser.refreshActionLabelText");
        newFolderActionLabelText = VFSResources.getMessage("VFSJFileChooser.newFolderActionLabelText");

        viewTypeActionNames = new String[VIEWTYPE_COUNT];
        viewTypeActionNames[VIEWTYPE_LIST] = VFSResources.getMessage("VFSJFileChooser.listViewActionLabelText");
        viewTypeActionNames[VIEWTYPE_DETAILS] = VFSResources
                .getMessage("VFSJFileChooser.detailsViewActionLabelText");

        renameErrorTitleText = VFSResources.getMessage("VFSJFileChooser.renameErrorTitleText");
        renameErrorText = VFSResources.getMessage("VFSJFileChooser.renameErrorText");
        renameErrorFileExistsText = VFSResources.getMessage("VFSJFileChooser.renameErrorFileExistsText");

        fileNameHeaderText = VFSResources.getMessage("VFSJFileChooser.fileNameHeaderText");
        fileSizeHeaderText = VFSResources.getMessage("VFSJFileChooser.fileSizeHeaderText");
        fileDateHeaderText = VFSResources.getMessage("VFSJFileChooser.fileDateHeaderText");
    }

    /**
     * Fetches the command list for the FilePane. These commands
     * are useful for binding to events, such as in a keymap.
     *
     * @return the command list
     */
    public Action[] getActions() {
        if (actions == null) {
            class FilePaneAction extends AbstractAction {
                FilePaneAction(String name) {
                    this(name, name);
                }

                FilePaneAction(String name, String cmd) {
                    super(name);
                    putValue(Action.ACTION_COMMAND_KEY, cmd);
                }

                public void actionPerformed(ActionEvent e) {
                    String cmd = (String) getValue(Action.ACTION_COMMAND_KEY);

                    if (cmd.equals(ACTION_CANCEL)) {
                        if (editFile != null) {
                            cancelEdit();
                        } else {
                            getFileChooser().cancelSelection();
                        }
                    } else if (cmd.equals(ACTION_EDIT_FILE_NAME)) {
                        VFSJFileChooser fc = getFileChooser();
                        int index = listSelectionModel.getMinSelectionIndex();

                        if ((index >= 0) && (editFile == null)
                                && (!fc.isMultiSelectionEnabled() || (fc.getSelectedFiles().length <= 1))) {
                            editFileName(index);
                        }
                    } else if (cmd.equals(ACTION_REFRESH)) {
                        getFileChooser().rescanCurrentDirectory();
                    }
                }

                @Override
                public boolean isEnabled() {
                    String cmd = (String) getValue(Action.ACTION_COMMAND_KEY);

                    if (cmd.equals(ACTION_CANCEL)) {
                        return getFileChooser().isEnabled();
                    } else if (cmd.equals(ACTION_EDIT_FILE_NAME)) {
                        return !readOnly && getFileChooser().isEnabled();
                    } else {
                        return true;
                    }
                }
            }

            ArrayList<Action> actionList = new ArrayList<Action>(8);
            Action action;

            actionList.add(new FilePaneAction(ACTION_CANCEL));
            actionList.add(new FilePaneAction(ACTION_EDIT_FILE_NAME));
            actionList.add(new FilePaneAction(refreshActionLabelText, ACTION_REFRESH));

            action = fileChooserUIAccessor.getApproveSelectionAction();

            if (action != null) {
                actionList.add(action);
            }

            action = fileChooserUIAccessor.getChangeToParentDirectoryAction();

            if (action != null) {
                actionList.add(action);
            }

            action = getNewFolderAction();

            if (action != null) {
                actionList.add(action);
            }

            action = getViewTypeAction(VIEWTYPE_LIST);

            if (action != null) {
                actionList.add(action);
            }

            actions = actionList.toArray(new Action[actionList.size()]);
        }

        return actions.clone();
    }

    protected void createActionMap() {
        addActionsToMap(super.getActionMap(), getActions());
    }

    /**
     * @param map
     * @param actions
     */
    public static void addActionsToMap(ActionMap map, Action[] actions) {
        if ((map != null) && (actions != null)) {
            for (Action a : actions) {
                String cmd = (String) a.getValue(Action.ACTION_COMMAND_KEY);

                if (cmd == null) {
                    cmd = (String) a.getValue(Action.NAME);
                }

                map.put(cmd, a);
            }
        }
    }

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

    public JPanel createList() {
        JPanel p = new JPanel(new BorderLayout());
        final VFSJFileChooser fileChooser = getFileChooser();
        final JList aList = new JList() {
            @Override
            public int getNextMatch(String prefix, int startIndex, Position.Bias bias) {
                ListModel model = getModel();
                int max = model.getSize();

                if ((prefix == null) || (startIndex < 0) || (startIndex >= max)) {
                    throw new IllegalArgumentException();
                }

                // start search from the next element before/after the selected element
                boolean backwards = (bias == Position.Bias.Backward);

                for (int i = startIndex; backwards ? (i >= 0) : (i < max); i += (backwards ? (-1) : 1)) {
                    String filename = fileChooser.getName((FileObject) model.getElementAt(i));

                    if (filename.regionMatches(true, 0, prefix, 0, prefix.length())) {
                        return i;
                    }
                }

                return -1;
            }
        };

        aList.setCellRenderer(new FileRenderer());
        aList.setLayoutOrientation(JList.VERTICAL_WRAP);

        // 4835633 : tell BasicListUI that this is a file list
        aList.putClientProperty("List.isFileList", Boolean.TRUE);

        if (listViewWindowsStyle) {
            aList.addFocusListener(repaintListener);
        }

        updateListRowCount(aList);

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

            public void intervalRemoved(ListDataEvent e) {
                updateListRowCount(aList);
            }

            public void contentsChanged(ListDataEvent e) {
                if (isShowing()) {
                    clearSelection();
                }

                updateListRowCount(aList);
            }
        });

        getModel().addPropertyChangeListener(this);

        if (fileChooser.isMultiSelectionEnabled()) {
            aList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        } else {
            aList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        }

        aList.setModel(getModel());

        aList.addListSelectionListener(createListSelectionListener());
        aList.addMouseListener(getMouseHandler());

        JScrollPane scrollpane = new JScrollPane(aList);

        if (listViewBackground != null) {
            aList.setBackground(listViewBackground);
        }

        if (listViewBorder != null) {
            scrollpane.setBorder(listViewBorder);
        }

        p.add(scrollpane, BorderLayout.CENTER);

        return p;
    }

    /**
     * Creates a selection listener for the list of files and directories.
     *
     * @return a <code>ListSelectionListener</code>
     */
    public ListSelectionListener createListSelectionListener() {
        return fileChooserUIAccessor.createListSelectionListener();
    }

    private int getEditIndex() {
        return lastIndex;
    }

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

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

    private void cancelEdit() {
        if (editFile != null) {
            editFile = null;
            list.remove(editCell);
            repaint();
        }
    }

    /**
     * @param index visual index of the file to be edited
     */
    @SuppressWarnings("deprecation")
    private void editFileName(int index) {
        FileObject currentDirectory = getFileChooser().getCurrentDirectory();

        if (readOnly || !canWrite(currentDirectory)) {
            return;
        }

        ensureIndexIsVisible(index);

        switch (viewType) {
        case VIEWTYPE_LIST:
            editFile = (FileObject) getModel().getElementAt(index);

            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);
            editCell.setText(getFileChooser().getName(editFile));

            ComponentOrientation orientation = list.getComponentOrientation();
            editCell.setComponentOrientation(orientation);

            if (orientation.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();

            break;

        case VIEWTYPE_DETAILS:
            detailsTable.editCellAt(index, COLUMN_FILENAME);

            break;
        }
    }

    private void applyEdit() {
        if ((editFile != null) && VFSUtils.exists(editFile)) {
            VFSJFileChooser chooser = getFileChooser();
            String oldDisplayName = chooser.getName(editFile);
            String oldFileName = editFile.getName().getBaseName();
            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
                AbstractVFSFileSystemView fsv = chooser.getFileSystemView();
                FileObject f2 = fsv.createFileObject(VFSUtils.getParentDirectory(editFile), newFileName);

                if (VFSUtils.exists(f2)) {
                    JOptionPane.showMessageDialog(chooser,
                            MessageFormat.format(renameErrorFileExistsText, oldFileName), renameErrorTitleText,
                            JOptionPane.ERROR_MESSAGE);
                } else {
                    if (getModel().renameFile(editFile, f2)) {
                        if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
                            if (chooser.isMultiSelectionEnabled()) {
                                chooser.setSelectedFiles(new FileObject[] { f2 });
                            } else {
                                chooser.setSelectedFile(f2);
                            }
                        } else {
                            //Could be because of delay in updating Desktop folder
                            //chooser.setSelectedFile(null);
                        }
                    } else {
                        JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorText, oldFileName),
                                renameErrorTitleText, JOptionPane.ERROR_MESSAGE);
                    }
                }
            }
        }

        if ((detailsTable != null) && detailsTable.isEditing()) {
            detailsTable.getCellEditor().stopCellEditing();
        }

        cancelEdit();
    }

    public Action getNewFolderAction() {
        if (!readOnly && (newFolderAction == null)) {
            newFolderAction = new AbstractAction(newFolderActionLabelText) {
                private Action basicNewFolderAction;

                {
                    putValue(Action.ACTION_COMMAND_KEY, VFSFilePane.ACTION_NEW_FOLDER);

                    FileObject currentDirectory = getFileChooser().getCurrentDirectory();

                    if (currentDirectory != null) {
                        setEnabled(canWrite(currentDirectory));
                    }
                }

                public void actionPerformed(ActionEvent ev) {
                    if (basicNewFolderAction == null) {
                        basicNewFolderAction = fileChooserUIAccessor.getNewFolderAction();
                    }

                    VFSJFileChooser fc = getFileChooser();
                    FileObject oldFile = fc.getSelectedFile();
                    basicNewFolderAction.actionPerformed(ev);

                    FileObject newFile = fc.getSelectedFile();

                    if ((newFile != null) && !newFile.equals(oldFile) && VFSUtils.isDirectory(newFile)) {
                        newFolderFile = newFile;
                    }
                }
            };
        }

        return newFolderAction;
    }

    void setFileSelected() {
        if (getFileChooser().isMultiSelectionEnabled() && !isDirectorySelected()) {
            FileObject[] files = getFileChooser().getSelectedFiles(); // Should be selected

            Object[] selectedObjects = list.getSelectedValues(); // Are actually selected

            listSelectionModel.setValueIsAdjusting(true);

            try {
                int lead = listSelectionModel.getLeadSelectionIndex();
                int anchor = listSelectionModel.getAnchorSelectionIndex();

                //                Arrays.sort(files);
                //                Arrays.sort(selectedObjects);
                int shouldIndex = 0;
                int actuallyIndex = 0;

                final int fileCount = files.length;
                final int selectedFileCount = selectedObjects.length;

                // Remove files that shouldn't be selected and add files which should be selected
                // Note: Assume files are already sorted in compareTo order.
                while ((shouldIndex < fileCount) && (actuallyIndex < selectedFileCount)) {
                    shouldIndex++;
                    actuallyIndex++;
                }

                while (shouldIndex < fileCount) {
                    doSelectFile(files[shouldIndex++]);
                }

                while (actuallyIndex < selectedFileCount) {
                    doDeselectFile(selectedObjects[actuallyIndex++]);
                }

                // restore the anchor and lead
                if (listSelectionModel instanceof DefaultListSelectionModel) {
                    ((DefaultListSelectionModel) listSelectionModel).moveLeadSelectionIndex(lead);
                    listSelectionModel.setAnchorSelectionIndex(anchor);
                }
            } finally {
                listSelectionModel.setValueIsAdjusting(false);
            }
        } else {
            VFSJFileChooser chooser = getFileChooser();
            FileObject f;

            if (isDirectorySelected()) {
                f = getDirectory();
            } else {
                f = chooser.getSelectedFile();
            }

            int i;

            if ((f != null) && ((i = getModel().indexOf(f)) >= 0)) {
                int viewIndex = i;

                listSelectionModel.setSelectionInterval(viewIndex, viewIndex);
                ensureIndexIsVisible(viewIndex);
            } else {
                clearSelection();
            }
        }
    }

    private void doSelectFile(FileObject fileToSelect) {
        int index = getModel().indexOf(fileToSelect);

        // could be missed in the current directory if it changed
        if (index >= 0) {
            listSelectionModel.addSelectionInterval(index, index);
        }
    }

    private void doDeselectFile(Object fileToDeselect) {
        int index = getModel().indexOf(fileToDeselect);
        listSelectionModel.removeSelectionInterval(index, index);
    }

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

        FileObject f = (FileObject) e.getNewValue();

        if ((f != null)) {
            setFileSelected();
        }
    }

    private void doSelectedFilesChanged(PropertyChangeEvent e) {
        applyEdit();

        FileObject[] files = (FileObject[]) e.getNewValue();
        VFSJFileChooser fc = getFileChooser();

        if ((files != null) && (files.length > 0)
                && ((files.length > 1) || fc.isDirectorySelectionEnabled() || !VFSUtils.isDirectory(files[0]))) {
            setFileSelected();
        }
    }

    private void doDirectoryChanged(PropertyChangeEvent e) {
        VFSJFileChooser fc = getFileChooser();
        AbstractVFSFileSystemView fsv = fc.getFileSystemView();

        applyEdit();
        resetEditIndex();
        ensureIndexIsVisible(0);

        FileObject currentDirectory = fc.getCurrentDirectory();

        if (currentDirectory != null) {
            if (!readOnly) {
                getNewFolderAction().setEnabled(canWrite(currentDirectory));
            }

            fileChooserUIAccessor.getChangeToParentDirectoryAction().setEnabled(!fsv.isRoot(currentDirectory));
        }

        if (list != null) {
            list.clearSelection();
        }
    }

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

    private void doFileSelectionModeChanged(PropertyChangeEvent e) {
        applyEdit();
        resetEditIndex();
        clearSelection();
    }

    private void doMultiSelectionChanged(PropertyChangeEvent e) {
        if (getFileChooser().isMultiSelectionEnabled()) {
            listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        } else {
            listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            clearSelection();
            getFileChooser().setSelectedFiles(null);
        }
    }

    /*
     * Listen for filechooser property changes, such as
     * the selected file changing, or the type of the dialog changing.
     */
    public void propertyChange(PropertyChangeEvent e) {
        if (viewType == -1) {
            setViewType(VIEWTYPE_LIST);
        }

        String s = e.getPropertyName();

        if (s.equals(VFSJFileChooserConstants.SELECTED_FILE_CHANGED_PROPERTY)) {
            doSelectedFileChanged(e);
        } else if (s.equals(VFSJFileChooserConstants.SELECTED_FILES_CHANGED_PROPERTY)) {
            doSelectedFilesChanged(e);
        } else if (s.equals(VFSJFileChooserConstants.DIRECTORY_CHANGED_PROPERTY)) {
            doDirectoryChanged(e);
        } else if (s.equals(VFSJFileChooserConstants.FILE_FILTER_CHANGED_PROPERTY)) {
            doFilterChanged(e);
        } else if (s.equals(VFSJFileChooserConstants.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
            doFileSelectionModeChanged(e);
        } else if (s.equals(VFSJFileChooserConstants.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
            doMultiSelectionChanged(e);
        } else if (s.equals(VFSJFileChooserConstants.CANCEL_SELECTION)) {
            applyEdit();
        } else if (s.equals("busy")) {
            setCursor((Boolean) e.getNewValue() ? waitCursor : null);
        } else if (s.equals("componentOrientation")) {
            ComponentOrientation o = (ComponentOrientation) e.getNewValue();
            VFSJFileChooser cc = (VFSJFileChooser) e.getSource();

            if (o != e.getOldValue()) {
                cc.applyComponentOrientation(o);
            }
        }
    }

    private void ensureIndexIsVisible(int i) {
        if (i >= 0) {
            if (list != null) {
                Rectangle cellBounds = list.getCellBounds(i, i);

                if (cellBounds == null) {
                    cellBounds = list.getCellBounds(i - 1, i - 1);

                    if (cellBounds != null) {
                        //  2* so that you get bottom of cell
                        cellBounds.translate(0, 2 * cellBounds.height);
                        list.scrollRectToVisible(cellBounds);
                    }
                } else {
                    list.ensureIndexIsVisible(i);
                }
            }

            if (detailsTable != null) {
                Rectangle r = detailsTable.getCellRect(i, COLUMN_FILENAME, true);
                detailsTable.scrollRectToVisible(r);
            }
        }
    }

    public void ensureFileIsVisible(VFSJFileChooser fc, FileObject f) {
        int modelIndex = getModel().indexOf(f);

        if (modelIndex >= 0) {
            ensureIndexIsVisible(modelIndex);
        }
    }

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

    public void clearSelection() {
        if (listSelectionModel != null) {
            listSelectionModel.clearSelection();

            if (listSelectionModel instanceof DefaultListSelectionModel) {
                ((DefaultListSelectionModel) listSelectionModel).moveLeadSelectionIndex(0);
                listSelectionModel.setAnchorSelectionIndex(0);
            }
        }
    }

    public JMenu getViewMenu() {
        if (viewMenu == null) {
            viewMenu = new JMenu(viewMenuLabelText);

            ButtonGroup viewButtonGroup = new ButtonGroup();

            for (int i = 0; i < VIEWTYPE_COUNT; i++) {
                JRadioButtonMenuItem mi = new JRadioButtonMenuItem();
                mi.setAction(new ViewTypeAction(i));
                viewButtonGroup.add(mi);
                viewMenu.add(mi);
            }

            updateViewMenu();
        }

        return viewMenu;
    }

    private void updateViewMenu() {
        if (viewMenu != null) {
            Component[] components = viewMenu.getMenuComponents();

            for (Component component : components) {
                if (component instanceof JRadioButtonMenuItem) {
                    JRadioButtonMenuItem mi = (JRadioButtonMenuItem) component;

                    if (((ViewTypeAction) mi.getAction()).viewType == viewType) {
                        mi.setSelected(true);
                    }
                }
            }
        }
    }

    @Override
    public JPopupMenu getComponentPopupMenu() {
        JPopupMenu popupMenu = getFileChooser().getComponentPopupMenu();

        if (popupMenu != null) {
            return popupMenu;
        }

        JMenu aViewMenu = getViewMenu();

        if (contextMenu == null) {
            contextMenu = new JPopupMenu();

            if (aViewMenu != null) {
                contextMenu.add(aViewMenu);

                if (listViewWindowsStyle) {
                    contextMenu.addSeparator();
                }
            }

            ActionMap actionMap = getActionMap();
            Action refreshAction = actionMap.get(ACTION_REFRESH);
            Action aNewFolderAction = actionMap.get(ACTION_NEW_FOLDER);

            if (refreshAction != null) {
                contextMenu.add(refreshAction);

                if (listViewWindowsStyle && (aNewFolderAction != null)) {
                    contextMenu.addSeparator();
                }
            }

            if (aNewFolderAction != null) {
                contextMenu.add(aNewFolderAction);
            }
        }

        if (aViewMenu != null) {
            aViewMenu.getPopupMenu().setInvoker(aViewMenu);
        }

        return contextMenu;
    }

    protected Handler getMouseHandler() {
        if (handler == null) {
            handler = new Handler();
        }

        return handler;
    }

    /**
     * Property to remember whether a directory is currently selected in the UI.
     *
     * @return <code>true</code> iff a directory is currently selected.
     */
    protected boolean isDirectorySelected() {
        return fileChooserUIAccessor.isDirectorySelected();
    }

    /**
     * Property to remember the directory that is currently selected in the UI.
     *
     * @return the value of the <code>directory</code> property
     * @see javax.swing.plaf.basic.BasicFileChooserUI#setDirectory
     */
    protected FileObject getDirectory() {
        return fileChooserUIAccessor.getDirectory();
    }

    private Component findChildComponent(Container container, Class<? extends Object> cls) {
        final Component[] components = container.getComponents();

        for (Component component : components) {
            if (cls.isInstance(component)) {
                return component;
            } else if (component instanceof Container) {
                Component c = findChildComponent((Container) component, cls);

                if (c != null) {
                    return c;
                }
            }
        }

        return null;
    }

    public boolean canWrite(FileObject f) {
        return VFSUtils.canWrite(f);
    }

    private void updateDetailsColumnModel(JTable table) {
        if (table != null) {
            // Install cell editor for editing file name
            if (!readOnly && (table.getColumnCount() > COLUMN_FILENAME)) {
                table.getColumnModel().getColumn(COLUMN_FILENAME).setCellEditor(getDetailsTableCellEditor());
            }
        }
    }

    private DetailsTableCellEditor getDetailsTableCellEditor() {
        if (tableCellEditor == null) {
            tableCellEditor = new DetailsTableCellEditor(new JTextField());
        }

        return tableCellEditor;
    }

    private DetailsTableModel getDetailsTableModel() {
        if (detailsTableModel == null) {
            detailsTableModel = new DetailsTableModel(getFileChooser());
        }

        return detailsTableModel;
    }

    public JPanel createDetailsView() {
        final VFSJFileChooser chooser = getFileChooser();

        JPanel p = new JPanel(new BorderLayout());

        final JTable detailsTable = new JTable(getDetailsTableModel()) {
            // Handle Escape key events here
            @Override
            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;
                }

                return super.processKeyBinding(ks, e, condition, pressed);
            }

            @Override
            public void tableChanged(TableModelEvent e) {
                super.tableChanged(e);

                if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
                    // update header with possibly changed column set
                    updateDetailsColumnModel(this);
                }
            }
        };

        //        detailsTable.setRowSorter(getRowSorter());
        detailsTable.setAutoCreateColumnsFromModel(false);
        detailsTable.setComponentOrientation(chooser.getComponentOrientation());
        //detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        detailsTable.setShowGrid(false);
        detailsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);

        //        detailsTable.addKeyListener(detailsKeyListener);
        Font font = list.getFont();
        detailsTable.setFont(font);
        detailsTable.setIntercellSpacing(new Dimension(0, 0));

        TableCellRenderer headerRenderer = new AlignableTableHeaderRenderer(
                detailsTable.getTableHeader().getDefaultRenderer());
        detailsTable.getTableHeader().setDefaultRenderer(headerRenderer);

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

        // So that drag can be started on a mouse press
        detailsTable.getColumnModel().getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        detailsTable.addMouseListener(getMouseHandler());
        // No need to addListSelectionListener because selections are forwarded
        // to our JList.

        // 4835633 : tell BasicTableUI that this is a file list
        detailsTable.putClientProperty("Table.isFileList", Boolean.TRUE);

        if (listViewWindowsStyle) {
            detailsTable.addFocusListener(repaintListener);
        }

        JTableHeader header = detailsTable.getTableHeader();
        header.setUpdateTableInRealTime(true);
        header.addMouseListener(detailsTableModel.new ColumnListener());
        header.setReorderingAllowed(true);

        // TAB/SHIFT-TAB should transfer focus and ENTER should select an item.
        // We don't want them to navigate within the table
        ActionMap am = SwingUtilities.getUIActionMap(detailsTable);
        am.remove("selectNextRowCell");
        am.remove("selectPreviousRowCell");
        am.remove("selectNextColumnCell");
        am.remove("selectPreviousColumnCell");
        detailsTable.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
        detailsTable.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);

        JScrollPane scrollpane = new JScrollPane(detailsTable);
        scrollpane.setComponentOrientation(chooser.getComponentOrientation());
        LookAndFeel.installColors(scrollpane.getViewport(), "Table.background", "Table.foreground");

        // Adjust width of first column so the table fills the viewport when
        // first displayed (temporary listener).
        scrollpane.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                JScrollPane sp = (JScrollPane) e.getComponent();
                fixNameColumnWidth(sp.getViewport().getSize().width);
                sp.removeComponentListener(this);
            }
        });

        // 4835633.
        // If the mouse is pressed in the area below the Details view table, the
        // event is not dispatched to the Table MouseListener but to the
        // scrollpane.  Listen for that here so we can clear the selection.
        scrollpane.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                JScrollPane jsp = ((JScrollPane) e.getComponent());
                JTable table = (JTable) jsp.getViewport().getView();

                if (!e.isShiftDown()
                        || (table.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)) {
                    clearSelection();

                    TableCellEditor tce = table.getCellEditor();

                    if (tce != null) {
                        tce.stopCellEditing();
                    }
                }
            }
        });

        detailsTable.setForeground(list.getForeground());
        detailsTable.setBackground(list.getBackground());

        if (listViewBorder != null) {
            scrollpane.setBorder(listViewBorder);
        }

        p.add(scrollpane, BorderLayout.CENTER);

        detailsTableModel.fireTableStructureChanged();

        return p;
    } // createDetailsView

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

    class DetailsTableCellRenderer extends DefaultTableCellRenderer {
        VFSJFileChooser chooser;
        DateFormat df;

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

        @Override
        public void setBounds(int x, int y, int width, int height) {
            if (getHorizontalAlignment() == SwingConstants.LEADING) {
                // Restrict width to actual text
                width = Math.min(width, this.getPreferredSize().width + 4);
            } else {
                x -= 4;
            }

            super.setBounds(x, y, width, height);
        }

        @Override
        public Insets getInsets(Insets i) {
            // Provide some space between columns
            i = super.getInsets(i);
            i.left += 4;
            i.right += 4;

            return i;
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int column) {
            if ((table.convertColumnIndexToModel(column) != COLUMN_FILENAME)
                    || (listViewWindowsStyle && !table.isFocusOwner())) {
                isSelected = false;
            }

            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

            setIcon(null);
            setHorizontalAlignment(SwingConstants.LEFT);

            // formatting cell text
            // TODO: it's rather a temporary trick, to be revised
            String text;

            if (value == null) {
                text = "";
            } else if (value instanceof FileObject) {
                FileObject file = (FileObject) value;
                text = chooser.getName(file);

                Icon icon = chooser.getIcon(file);
                setIcon(icon);
            } else if (value instanceof Long) {
                long len = (Long) value; //((Long) value) / 1024L;

                text = VFSUtils.byteCountToDisplaySize(len);
            } else if (value instanceof Date) {
                text = df.format((Date) value);
            } else {
                text = value.toString();
            }

            setText(text);

            return this;
        }
    }

    // This interface is used to access methods in the FileChooserUI
    // that are not public.
    class ViewTypeAction extends AbstractAction {
        private int viewType;

        ViewTypeAction(int viewType) {
            super(viewTypeActionNames[viewType]);
            this.viewType = viewType;

            String cmd;

            if (viewType == VIEWTYPE_LIST) {
                cmd = ACTION_VIEW_LIST;
            } else if (viewType == VIEWTYPE_DETAILS) {
                cmd = ACTION_VIEW_DETAILS;
            } else {
                cmd = (String) getValue(Action.NAME);
            }

            putValue(Action.ACTION_COMMAND_KEY, cmd);
        }

        public void actionPerformed(ActionEvent e) {
            setViewType(viewType);
        }
    }

    private class DetailsTableCellEditor extends DefaultCellEditor {
        private final JTextField tf;

        public DetailsTableCellEditor(JTextField tf) {
            super(tf);
            this.tf = tf;
            tf.addFocusListener(editorFocusListener);
        }

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

        @Override
        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 FileObject) {
                tf.setText(getFileChooser().getName((FileObject) value));
                tf.selectAll();
            }

            return comp;
        }
    }

    private class DelayedSelectionUpdater implements Runnable {
        FileObject editFile;

        DelayedSelectionUpdater() {
            this(null);
        }

        DelayedSelectionUpdater(FileObject editFile) {
            this.editFile = editFile;

            if (isShowing()) {
                SwingUtilities.invokeLater(this);
            }
        }

        public void run() {
            setFileSelected();

            if (editFile != null) {
                editFileName(getModel().indexOf(editFile));
                editFile = null;
            }
        }
    }

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

    protected class FileRenderer extends DefaultListCellRenderer {
        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                boolean cellHasFocus) {
            FileObject f = (FileObject) value;

            if (f != null) {
                setText(getFileChooser().getName(f));
                setIcon(getFileChooser().getIcon(f));
            } else {
                setText("");
                setIcon(null);
            }

            setOpaque(true);

            if (isSelected) {
                setBackground(list.getSelectionBackground());
                setForeground(list.getSelectionForeground());
            } else {
                setBackground(list.getBackground());
                setForeground(list.getForeground());
            }

            setEnabled(list.isEnabled());
            setFont(list.getFont());

            if (cellHasFocus) {
                setBorder(UIManager.getBorder("List.focusCellHighlightBorder"));
            } else {
                setBorder(noFocusBorder);
            }

            return this;
        }
    }

    private class Handler implements MouseListener {
        private MouseListener doubleClickListener;

        public void mouseClicked(MouseEvent evt) {
            JComponent source = (JComponent) evt.getSource();

            int index;

            if (source instanceof JList) {
                index = SwingCommonsUtilities.loc2IndexFileList(list, evt.getPoint());
            } else if (source instanceof JTable) {
                JTable table = (JTable) source;
                Point p = evt.getPoint();
                index = table.rowAtPoint(p);

                if (SwingCommonsUtilities.pointOutsidePrefSize(table, index, table.columnAtPoint(p), p)) {
                    return;
                }

                // Translate point from table to list
                if ((index >= 0) && (list != null) && listSelectionModel.isSelectedIndex(index)) {
                    // Make a new event with the list as source, placing the
                    // click in the corresponding list cell. 
                    Rectangle r = list.getCellBounds(index, index);
                    evt = new MouseEvent(list, evt.getID(), evt.getWhen(), evt.getModifiers(), r.x + 1,
                            r.y + (r.height / 2), evt.getClickCount(), evt.isPopupTrigger(), evt.getButton());
                }
            } else {
                return;
            }

            if ((index >= 0) && SwingUtilities.isLeftMouseButton(evt)) {
                VFSJFileChooser fc = getFileChooser();

                // For single click, we handle editing file name
                if ((evt.getClickCount() == 1) && source instanceof JList) {
                    if ((!fc.isMultiSelectionEnabled() || (fc.getSelectedFiles().length <= 1)) && (index >= 0)
                            && listSelectionModel.isSelectedIndex(index) && (getEditIndex() == index)
                            && (editFile == null)) {
                        editFileName(index);
                    } else {
                        if (index >= 0) {
                            setEditIndex(index);
                        } else {
                            resetEditIndex();
                        }
                    }
                } else if (evt.getClickCount() == 2) {
                    // System.out.println("double click");

                    // on double click (open or drill down one directory) be
                    // sure to clear the edit index
                    resetEditIndex();
                }
            }

            // Forward event to Basic
            if (getDoubleClickListener() != null) {
                getDoubleClickListener().mouseClicked(evt);
            }
        }

        public void mouseEntered(MouseEvent evt) {
            JComponent source = (JComponent) evt.getSource();

            if (source instanceof JTable) {
                JTable table = (JTable) evt.getSource();

                TransferHandler th1 = getFileChooser().getTransferHandler();
                TransferHandler th2 = table.getTransferHandler();

                if (th1 != th2) {
                    table.setTransferHandler(th1);
                }

                boolean dragEnabled = getFileChooser().getDragEnabled();

                if (dragEnabled != table.getDragEnabled()) {
                    table.setDragEnabled(dragEnabled);
                }
            } else if (source instanceof JList) {
                // Forward event to Basic
                if (getDoubleClickListener() != null) {
                    getDoubleClickListener().mouseEntered(evt);
                }
            }
        }

        public void mouseExited(MouseEvent evt) {
            if (evt.getSource() instanceof JList) {
                // Forward event to Basic
                if (getDoubleClickListener() != null) {
                    getDoubleClickListener().mouseExited(evt);
                }
            }
        }

        public void mousePressed(MouseEvent evt) {
            if (evt.getSource() instanceof JList) {
                // Forward event to Basic
                if (getDoubleClickListener() != null) {
                    getDoubleClickListener().mousePressed(evt);
                }
            }
        }

        public void mouseReleased(MouseEvent evt) {
            if (evt.getSource() instanceof JList) {
                // Forward event to Basic
                if (getDoubleClickListener() != null) {
                    getDoubleClickListener().mouseReleased(evt);
                }
            }
        }

        private MouseListener getDoubleClickListener() {
            // Lazy creation of Basic's listener
            if ((doubleClickListener == null) && (list != null)) {
                doubleClickListener = fileChooserUIAccessor.createDoubleClickListener(list);
            }

            return doubleClickListener;
        }
    }

    class DetailsTableModel extends AbstractTableModel implements ListDataListener {
        public static final long ONE_KB = 1024;
        public static final long ONE_MB = ONE_KB * ONE_KB;
        public static final long ONE_GB = ONE_KB * ONE_MB;
        protected int sortCol = 0;
        protected boolean isSortAsc = true;
        private VFSJFileChooser chooser;
        private BasicVFSDirectoryModel directoryModel;
        private List<String> columns;
        private final int columnsCount = 3;
        int[] columnMap;
        DateFormat df;
        final String[] headers = { fileNameHeaderText, fileSizeHeaderText, fileDateHeaderText };

        DetailsTableModel(VFSJFileChooser fc) {
            this.chooser = fc;
            directoryModel = getModel();
            directoryModel.addListDataListener(this);
            columns = new ArrayList<String>(headers.length);
            df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, chooser.getLocale());

            for (String header : headers) {
                columns.add(header);
            }

            updateColumnInfo();
        }

        void updateColumnInfo() {
        }

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

        public int getColumnCount() {
            return this.columnsCount;
        }

        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.
            return getFileColumnValue((FileObject) directoryModel.getElementAt(row), col);
        }

        private Object getFileColumnValue(FileObject f, int col) {
            Object o = f;

            try {
                switch (col) {
                case COLUMN_FILENAME:
                    o = f;

                    break;

                case COLUMN_SIZE:

                    if (VFSUtils.isDirectory(f)) {
                        o = null;
                    } else {
                        o = Long.parseLong("" + f.getContent().getSize());
                    }

                    break;

                case COLUMN_DATE:
                    o = f.getContent().getLastModifiedTime() + "";
                    o = new Date(Long.parseLong(o.toString()));

                    break;
                }
            } catch (Exception e) {
            }

            return o;
        }

        @Override
        public void setValueAt(Object value, int row, int col) {
            if (col == COLUMN_FILENAME) {
                VFSJFileChooser chooser = getFileChooser();
                FileObject f = (FileObject) getValueAt(row, col);

                if (f != null) {
                    String oldDisplayName = chooser.getName(f);
                    String oldFileName = f.getName().getBaseName();
                    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
                        AbstractVFSFileSystemView fsv = chooser.getFileSystemView();
                        FileObject f2 = fsv.createFileObject(VFSUtils.getParentDirectory(f), newFileName);

                        if (!VFSUtils.exists(f2) && VFSFilePane.this.getModel().renameFile(f, f2)) {
                            if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
                                if (chooser.isMultiSelectionEnabled()) {
                                    chooser.setSelectedFiles(new FileObject[] { f2 });
                                } else {
                                    chooser.setSelectedFile(f2);
                                }
                            }
                        }
                    }
                }
            }
        }

        @Override
        public boolean isCellEditable(int row, int column) {
            FileObject currentDirectory = getFileChooser().getCurrentDirectory();

            return (!readOnly && (column == COLUMN_FILENAME) && canWrite(currentDirectory));
        }

        public void contentsChanged(ListDataEvent e) {
            // Update the selection after the model has been updated
            new DelayedSelectionUpdater();
            fireTableDataChanged();
        }

        public void intervalAdded(ListDataEvent e) {
            int i0 = e.getIndex0();
            int i1 = e.getIndex1();

            if (i0 == i1) {
                FileObject file = (FileObject) getModel().getElementAt(i0);

                if (file.getName().equals(newFolderFile.getName())) {
                    new DelayedSelectionUpdater(file);
                    newFolderFile = null;
                }
            }

            fireTableRowsInserted(e.getIndex0(), e.getIndex1());
        }

        public void intervalRemoved(ListDataEvent e) {
            fireTableRowsDeleted(e.getIndex0(), e.getIndex1());
        }

        @Override
        public String getColumnName(int column) {
            String str = columns.get(column);

            if (column == sortCol) {
                str += (isSortAsc ? " >>" : " <<");
            }

            return str;
        }

        public List<String> getColumns() {
            return columns;
        }

        class ColumnListener extends MouseAdapter {
            @Override
            public void mouseClicked(MouseEvent e) {
                TableColumnModel colModel = detailsTable.getColumnModel();
                int columnModelIndex = colModel.getColumnIndexAtX(e.getX());
                int modelIndex = colModel.getColumn(columnModelIndex).getModelIndex();

                if (modelIndex < 0) {
                    return;
                }

                if (sortCol == modelIndex) {
                    isSortAsc = !isSortAsc;
                } else {
                    sortCol = modelIndex;
                }

                for (int i = 0; i < columnsCount; i++) {
                    TableColumn column = colModel.getColumn(i);
                    column.setHeaderValue(getColumnName(column.getModelIndex()));
                }

                detailsTable.getTableHeader().repaint();

                Comparator<FileObject> cpt = FileObjectComparatorFactory.newFileNameComparator(isSortAsc);

                if (modelIndex == 1) {
                    cpt = FileObjectComparatorFactory.newSizeComparator(isSortAsc);
                } else if (modelIndex == 2) {
                    cpt = FileObjectComparatorFactory.newDateComparator(isSortAsc);
                }

                directoryModel.sort(cpt);

                detailsTable.tableChanged(new TableModelEvent(DetailsTableModel.this));
                detailsTable.revalidate();
                detailsTable.repaint();
            }
        }
    }

    private static class AlignableTableHeaderRenderer implements TableCellRenderer {
        TableCellRenderer wrappedRenderer;

        public AlignableTableHeaderRenderer(TableCellRenderer wrappedRenderer) {
            this.wrappedRenderer = wrappedRenderer;
        }

        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int column) {
            Component c = wrappedRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row,
                    column);

            if (c instanceof JLabel) {
                ((JLabel) c).setHorizontalAlignment(SwingConstants.CENTER);
            }

            return c;
        }
    }
}