pl.otros.vfs.browser.VfsBrowser.java Source code

Java tutorial

Introduction

Here is the source code for pl.otros.vfs.browser.VfsBrowser.java

Source

/*
 * Copyright 2013 Krzysztof Otrebski (otros.systems@gmail.com)
 *
 * 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.
 */

package pl.otros.vfs.browser;

import net.miginfocom.swing.MigLayout;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.DataConfiguration;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileType;
import org.jdesktop.swingx.prompt.PromptSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.otros.vfs.browser.actions.*;
import pl.otros.vfs.browser.favorit.Favorite;
import pl.otros.vfs.browser.favorit.FavoritesUtils;
import pl.otros.vfs.browser.i18n.Messages;
import pl.otros.vfs.browser.list.MutableListDragListener;
import pl.otros.vfs.browser.list.MutableListDropHandler;
import pl.otros.vfs.browser.list.MutableListModel;
import pl.otros.vfs.browser.list.SelectFirstElementFocusAdapter;
import pl.otros.vfs.browser.preview.PreviewComponent;
import pl.otros.vfs.browser.preview.PreviewListener;
import pl.otros.vfs.browser.table.*;
import pl.otros.vfs.browser.util.GuiUtils;
import pl.otros.vfs.browser.util.SwingUtils;
import pl.otros.vfs.browser.util.VFSUtils;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Vector;

public class VfsBrowser extends JPanel {

    public static final String MULTI_SELECTION_ENABLED_CHANGED_PROPERTY = "MultiSelectionEnabledChangedProperty";
    public static final String MULTI_SELECTION_MODE_CHANGED_PROPERTY = "SelectionModeChangedProperty";
    private static final Logger LOGGER = LoggerFactory.getLogger(VfsBrowser.class);
    private static final Icon COMPUTER_ICON = Icons.getInstance().getComputer();
    private static final String ACTION_GO_UP = "GO_UP";
    private static final String ACTION_OPEN = "OPEN";
    private static final Object ACTION_ADD_CURRENT_LOCATION_TO_FAVORITES = "ADD CURRENT LOCATION TO FAVORITES";
    private static final String ACTION_DELETE = "DELETE";
    private static final String ACTION_APPROVE = "ACTION APPROVE";
    private static final String ACTION_FOCUS_ON_TABLE = "FOCUS ON TABLE";
    private static final String ACTION_CLEAR_REGEX_FILTER = "CLEAR REGEX FILTER";
    private static final String ACTION_FOCUS_ON_REGEX_FILTER = "FOCUS ON REGEX FILTER";
    private static final String ACTION_EDIT = "EDIT";
    private static final String ACTION_SWITCH_SHOW_HIDDEN = "SWITCH SHOW HIDDEN";
    private static final String ACTION_FOCUS_ON_PATH = "FOCUS ON PATH";
    private static final String ACTION_REFRESH = "REFRESH";
    private static final String TABLE = "TABLE";
    private static final String LOADING = "LOADING";
    protected JTextField pathField;
    protected JTable tableFiles;
    protected JScrollPane tableScrollPane;
    protected JList favoritesUserList;
    protected VfsTableModel vfsTableModel;
    protected JPanel tablePanel;
    private PreviewComponent previewComponent;
    private JCheckBox showHidCheckBox;
    private JButton goUpButton;
    private JLabel statusLabel;
    private FileObject currentLocation;
    private CardLayout cardLayout;
    private MutableListModel<Favorite> favoritesUserListModel;
    private SelectionMode selectionMode = SelectionMode.DIRS_AND_FILES;
    private Action actionApproveDelegate;
    private Action actionCancelDelegate;
    private boolean multiSelectionEnabled = false;
    private JButton actionApproveButton;
    private JButton actionCancelButton;
    private DataConfiguration configuration;
    private JProgressBar loadingProgressBar;
    private JLabel loadingIconLabel;
    private TaskContext taskContext;
    private JToggleButton skipCheckingLinksButton;
    private JTextField filterField;
    private TableRowSorter<VfsTableModel> sorter;
    private boolean showHidden = false;
    private AbstractAction actionFocusOnTable;
    private boolean targetFileSelected;

    public VfsBrowser() {
        this(new BaseConfiguration());
    }

    public VfsBrowser(Configuration configuration) {
        this(configuration, null);
    }

    public VfsBrowser(Configuration configuration, final String initialPath) {
        super();
        this.configuration = new DataConfiguration(configuration);
        initGui(initialPath);
        VFSUtils.loadAuthStore();
    }

    public void goToUrl(String url) {
        LOGGER.info("Going to URL: " + url);
        try {
            FileObject resolveFile = VFSUtils.resolveFileObject(url);
            String type = "?";
            if (resolveFile != null) {
                type = resolveFile.getType().toString();
            }
            LOGGER.info("URL: " + url + " is resolved " + type);
            goToUrl(resolveFile);
        } catch (FileSystemException e) {
            LOGGER.error("Can't go to URL " + url, e);
            final String message = ExceptionsUtils.getRootCause(e).getClass().getName() + ": "
                    + ExceptionsUtils.getRootCause(e).getLocalizedMessage();

            Runnable runnable = new Runnable() {
                public void run() {
                    JOptionPane.showMessageDialog(VfsBrowser.this, message,
                            Messages.getMessage("browser.badlocation"), JOptionPane.ERROR_MESSAGE);
                }
            };
            SwingUtils.runInEdt(runnable);
        }
    }

    public void goToUrl(final FileObject fileObject) {

        if (taskContext != null) {
            taskContext.setStop(true);
        }

        try {
            final FileObject[] files = VFSUtils.getFiles(fileObject);
            LOGGER.info("Have {} files in {}", files.length, fileObject.getName().getFriendlyURI());
            this.currentLocation = fileObject;

            taskContext = new TaskContext(Messages.getMessage("browser.checkingSFtpLinksTask"), files.length);
            taskContext.setIndeterminate(false);
            SwingWorker<Void, Void> refreshWorker = new SwingWorker<Void, Void>() {
                int icon = 0;
                Icon[] icons = new Icon[] { Icons.getInstance().getNetworkStatusOnline(),
                        Icons.getInstance().getNetworkStatusAway(), Icons.getInstance().getNetworkStatusOffline() };

                @Override
                protected void process(List<Void> chunks) {
                    loadingProgressBar.setIndeterminate(taskContext.isIndeterminate());
                    loadingProgressBar.setMaximum(taskContext.getMax());
                    loadingProgressBar.setValue(taskContext.getCurrentProgress());
                    loadingProgressBar.setString(String.format("%s [%d of %d]", taskContext.getName(),
                            taskContext.getCurrentProgress(), taskContext.getMax()));
                    loadingIconLabel.setIcon(icons[++icon % icons.length]);
                }

                @Override
                protected Void doInBackground() throws Exception {
                    try {
                        while (!taskContext.isStop()) {
                            publish();
                            Thread.sleep(300);
                        }
                    } catch (InterruptedException ignore) {
                        //ignore
                    }
                    return null;
                }
            };
            new Thread(refreshWorker).start();

            if (!skipCheckingLinksButton.isSelected()) {
                VFSUtils.checkForSftpLinks(files, taskContext);
            }
            taskContext.setStop(true);

            final FileObject[] fileObjectsWithParent = addParentToFiles(files);
            Runnable r = new

            Runnable() {

                @Override
                public void run() {
                    vfsTableModel.setContent(fileObjectsWithParent);
                    try {
                        pathField.setText(fileObject.getURL().toString());
                    } catch (FileSystemException e) {
                        LOGGER.error("Can't get URL", e);
                    }
                    if (tableFiles.getRowCount() > 0) {
                        tableFiles.getSelectionModel().setSelectionInterval(0, 0);
                    }
                    updateStatusText();
                }
            };
            SwingUtils.runInEdt(r);
        } catch (Exception e) {
            LOGGER.error("Can't go to URL for " + fileObject, e);
            final String message = ExceptionsUtils.getRootCause(e).getClass().getName() + ": "
                    + ExceptionsUtils.getRootCause(e).getLocalizedMessage();

            Runnable runnable = new Runnable() {
                public void run() {
                    JOptionPane.showMessageDialog(VfsBrowser.this, message,
                            Messages.getMessage("browser.badlocation"), JOptionPane.ERROR_MESSAGE);
                }
            };
            SwingUtils.runInEdt(runnable);
        }
    }

    /**
     * Caller responsible for ensuring called from EDT.
     * We purposefully do not update UI other than indirectly reflecting
     * the selection.
     * Current use case is that we are finished with the browser window.
     */
    private void loadAndSelSingleFile(FileObject fileObject) throws FileSystemException {
        vfsTableModel.setContent(new FileObject[] { new ParentFileObject(fileObject.getParent()), fileObject, });
        tableFiles.getSelectionModel().setSelectionInterval(1, 1);
    }

    private FileObject[] addParentToFiles(FileObject[] files) {
        FileObject[] newFiles = new FileObject[files.length + 1];
        try {
            FileObject parent = currentLocation.getParent();
            if (parent != null) {
                newFiles[0] = new ParentFileObject(parent);
                System.arraycopy(files, 0, newFiles, 1, files.length);
            } else {
                newFiles = files;
            }
        } catch (FileSystemException e) {
            LOGGER.warn("Can't add parent", e);
            newFiles = files;
        }
        return newFiles;
    }

    private void initGui(final String initialPath) {
        this.setLayout(new BorderLayout());
        JLabel pathLabel = new JLabel(Messages.getMessage("browser.location"));
        pathLabel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
        pathField = new JTextField(80);
        pathField.setFont(pathLabel.getFont().deriveFont(pathLabel.getFont().getSize() * 1.2f));
        pathField.setToolTipText(Messages.getMessage("nav.pathTooltip"));
        GuiUtils.addBlinkOnFocusGain(pathField);
        pathLabel.setLabelFor(pathField);
        pathLabel.setDisplayedMnemonic(Messages.getMessage("browser.location.mnemonic").charAt(0));

        InputMap inputMapPath = pathField.getInputMap(JComponent.WHEN_FOCUSED);
        inputMapPath.put(KeyStroke.getKeyStroke("ENTER"), "OPEN_PATH");
        inputMapPath.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), ACTION_FOCUS_ON_TABLE);
        pathField.getActionMap().put("OPEN_PATH", new BaseNavigateAction(this) {

            @Override
            protected void performLongOperation(CheckBeforeActionResult actionResult) {
                try {
                    FileObject resolveFile = VFSUtils.resolveFileObject(pathField.getText().trim());
                    if (resolveFile != null && resolveFile.getType() == FileType.FILE) {
                        loadAndSelSingleFile(resolveFile);
                        pathField.setText(resolveFile.getURL().toString());
                        actionApproveDelegate.actionPerformed(
                                // TODO:  Does actionResult provide an ID for 2nd param here,
                                // or should use a Random number?
                                new ActionEvent(actionResult, (int) new java.util.Date().getTime(),
                                        "SELECTED_FILE"));
                        return;
                    }
                } catch (FileSystemException fse) {
                    // Intentionally empty
                }
                goToUrl(pathField.getText().trim());
            }

            @Override
            protected boolean canGoUrl() {
                return true;
            }

            @Override
            protected boolean canExecuteDefaultAction() {
                return false;
            }

        });
        actionFocusOnTable = new

        AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                tableFiles.requestFocusInWindow();
                if (tableFiles.getSelectedRow() < 0 && tableFiles.getRowCount() == 0) {
                    tableFiles.getSelectionModel().setSelectionInterval(0, 0);
                }
            }
        };
        pathField.getActionMap().put(ACTION_FOCUS_ON_TABLE, actionFocusOnTable);

        BaseNavigateActionGoUp goUpAction = new BaseNavigateActionGoUp(this);
        goUpButton = new JButton(goUpAction);

        BaseNavigateActionRefresh refreshAction = new BaseNavigateActionRefresh(this);
        JButton refreshButton = new JButton(refreshAction);

        JToolBar upperPanel = new JToolBar(Messages.getMessage("nav.ToolBarName"));
        upperPanel.setRollover(true);
        upperPanel.add(pathLabel);
        upperPanel.add(pathField, "growx");
        upperPanel.add(goUpButton);
        upperPanel.add(refreshButton);

        AddCurrentLocationToFavoriteAction addCurrentLocationToFavoriteAction = new AddCurrentLocationToFavoriteAction(
                this);
        JButton addCurrentLocationToFavoriteButton = new JButton(addCurrentLocationToFavoriteAction);
        addCurrentLocationToFavoriteButton.setText("");
        upperPanel.add(addCurrentLocationToFavoriteButton);

        previewComponent = new PreviewComponent();

        vfsTableModel = new VfsTableModel();

        tableFiles = new JTable(vfsTableModel);
        tableFiles.setFillsViewportHeight(true);
        tableFiles.getColumnModel().getColumn(0).setMinWidth(140);
        tableFiles.getColumnModel().getColumn(1).setMaxWidth(80);
        tableFiles.getColumnModel().getColumn(2).setMaxWidth(80);
        tableFiles.getColumnModel().getColumn(3).setMaxWidth(180);
        tableFiles.getColumnModel().getColumn(3).setMinWidth(120);

        sorter = new TableRowSorter<VfsTableModel>(vfsTableModel);
        final FileNameWithTypeComparator fileNameWithTypeComparator = new FileNameWithTypeComparator();
        sorter.addRowSorterListener(new RowSorterListener() {
            @Override
            public void sorterChanged(RowSorterEvent e) {
                RowSorterEvent.Type type = e.getType();
                if (type.equals(RowSorterEvent.Type.SORT_ORDER_CHANGED)) {
                    List<? extends RowSorter.SortKey> sortKeys = e.getSource().getSortKeys();
                    for (RowSorter.SortKey sortKey : sortKeys) {
                        if (sortKey.getColumn() == VfsTableModel.COLUMN_NAME) {
                            fileNameWithTypeComparator.setSortOrder(sortKey.getSortOrder());
                        }
                    }
                }
            }
        });
        sorter.setComparator(VfsTableModel.COLUMN_NAME, fileNameWithTypeComparator);

        tableFiles.setRowSorter(sorter);
        tableFiles.setShowGrid(false);
        tableFiles.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                try {
                    selectionChanged();
                } catch (FileSystemException e1) {
                    LOGGER.error("Error during update state", e);
                }
            }
        });
        tableFiles.setColumnSelectionAllowed(false);
        vfsTableModel.addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                updateStatusText();
            }
        });

        tableFiles.setDefaultRenderer(FileSize.class, new FileSizeTableCellRenderer());
        tableFiles.setDefaultRenderer(FileNameWithType.class, new FileNameWithTypeTableCellRenderer());
        tableFiles.setDefaultRenderer(Date.class, new MixedDateTableCellRenderer());
        tableFiles.setDefaultRenderer(FileType.class, new FileTypeTableCellRenderer());

        tableFiles.getSelectionModel().addListSelectionListener(new PreviewListener(this, previewComponent));

        JPanel favoritesPanel = new JPanel(new MigLayout("wrap, fillx", "[grow]"));
        favoritesUserListModel = new MutableListModel<Favorite>();

        List<Favorite> favSystemLocations = FavoritesUtils.getSystemLocations();
        List<Favorite> favUser = FavoritesUtils.loadFromProperties(configuration);
        List<Favorite> favJVfsFileChooser = FavoritesUtils.getJvfsFileChooserBookmarks();
        for (Favorite favorite : favUser) {
            favoritesUserListModel.add(favorite);
        }
        favoritesUserListModel.addListDataListener(new ListDataListener() {
            @Override
            public void intervalAdded(ListDataEvent e) {
                saveFavorites();

            }

            @Override
            public void intervalRemoved(ListDataEvent e) {
                saveFavorites();
            }

            @Override
            public void contentsChanged(ListDataEvent e) {
                saveFavorites();
            }

            protected void saveFavorites() {
                FavoritesUtils.storeFavorites(configuration, favoritesUserListModel.getList());
            }
        });

        favoritesUserList = new JList(favoritesUserListModel);
        favoritesUserList.setTransferHandler(new MutableListDropHandler(favoritesUserList));
        new MutableListDragListener(favoritesUserList);
        favoritesUserList.setCellRenderer(new FavoriteListCellRenderer());
        favoritesUserList.addFocusListener(new SelectFirstElementFocusAdapter());

        addOpenActionToList(favoritesUserList);
        addEditActionToList(favoritesUserList, favoritesUserListModel);

        favoritesUserList.getActionMap().put(ACTION_DELETE, new AbstractAction(
                Messages.getMessage("favorites.deleteButtonText"), Icons.getInstance().getMinusButton()) {

            @Override
            public void actionPerformed(ActionEvent e) {
                Favorite favorite = favoritesUserListModel.getElementAt(favoritesUserList.getSelectedIndex());
                if (!Favorite.Type.USER.equals(favorite.getType())) {
                    return;
                }
                int response = JOptionPane.showConfirmDialog(VfsBrowser.this,
                        Messages.getMessage("favorites.areYouSureToDeleteConnections"),
                        Messages.getMessage("favorites.confirm"), JOptionPane.YES_NO_OPTION);

                if (response == JOptionPane.YES_OPTION) {
                    favoritesUserListModel.remove(favoritesUserList.getSelectedIndex());
                }
            }
        });
        InputMap favoritesListInputMap = favoritesUserList.getInputMap(JComponent.WHEN_FOCUSED);
        favoritesListInputMap.put(KeyStroke.getKeyStroke("DELETE"), ACTION_DELETE);

        ActionMap actionMap = tableFiles.getActionMap();
        actionMap.put(ACTION_OPEN, new BaseNavigateActionOpen(this));
        actionMap.put(ACTION_GO_UP, goUpAction);
        actionMap.put(ACTION_APPROVE, new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (actionApproveButton.isEnabled()) {
                    actionApproveDelegate.actionPerformed(e);
                }
            }
        });

        InputMap inputMap = tableFiles.getInputMap(JComponent.WHEN_FOCUSED);
        inputMap.put(KeyStroke.getKeyStroke("ENTER"), ACTION_OPEN);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_MASK), ACTION_APPROVE);

        inputMap.put(KeyStroke.getKeyStroke("BACK_SPACE"), ACTION_GO_UP);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), ACTION_GO_UP);
        addPopupMenu(favoritesUserList, ACTION_OPEN, ACTION_EDIT, ACTION_DELETE);

        JList favoriteSystemList = new JList(new Vector<Object>(favSystemLocations));
        favoriteSystemList.setCellRenderer(new FavoriteListCellRenderer());
        addOpenActionToList(favoriteSystemList);
        addPopupMenu(favoriteSystemList, ACTION_OPEN);
        favoriteSystemList.addFocusListener(new SelectFirstElementFocusAdapter());

        JList favoriteJVfsList = new JList(new Vector<Object>(favJVfsFileChooser));
        addOpenActionToList(favoriteJVfsList);
        favoriteJVfsList.setCellRenderer(new FavoriteListCellRenderer());
        addPopupMenu(favoriteJVfsList, ACTION_OPEN);
        favoriteJVfsList.addFocusListener(new SelectFirstElementFocusAdapter());

        JLabel favoritesSystemLocationsLabel = getTitleListLabel(Messages.getMessage("favorites.systemLocations"),
                COMPUTER_ICON);
        favoritesSystemLocationsLabel.setLabelFor(favoriteSystemList);
        favoritesSystemLocationsLabel
                .setDisplayedMnemonic(Messages.getMessage("favorites.systemLocations.mnemonic").charAt(0));
        favoritesPanel.add(favoritesSystemLocationsLabel, "gapleft 16");
        favoritesPanel.add(favoriteSystemList, "growx");
        JLabel favoritesFavoritesLabel = getTitleListLabel(Messages.getMessage("favorites.favorites"),
                Icons.getInstance().getStar());
        favoritesFavoritesLabel.setLabelFor(favoritesUserList);
        favoritesFavoritesLabel.setDisplayedMnemonic(Messages.getMessage("favorites.favorites.mnemonic").charAt(0));
        favoritesPanel.add(favoritesFavoritesLabel, "gapleft 16");
        favoritesPanel.add(favoritesUserList, "growx");

        if (favoriteJVfsList.getModel().getSize() > 0) {
            JLabel favoritesJVfsFileChooser = getTitleListLabel(
                    Messages.getMessage("favorites.JVfsFileChooserBookmarks"), null);
            favoritesJVfsFileChooser.setDisplayedMnemonic(
                    Messages.getMessage("favorites.JVfsFileChooserBookmarks.mnemonic").charAt(0));
            favoritesJVfsFileChooser.setLabelFor(favoriteJVfsList);
            favoritesPanel.add(favoritesJVfsFileChooser, "gapleft 16");
            favoritesPanel.add(favoriteJVfsList, "growx");
        }

        tableFiles.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                    tableFiles.getActionMap().get(ACTION_OPEN).actionPerformed(null);
                }
            }
        });
        tableFiles.addKeyListener(new QuickSearchKeyAdapter());

        cardLayout = new CardLayout();
        tablePanel = new JPanel(cardLayout);
        loadingProgressBar = new JProgressBar();
        loadingProgressBar.setStringPainted(true);
        loadingProgressBar.setString(Messages.getMessage("browser.loading"));
        loadingProgressBar.setIndeterminate(true);
        loadingIconLabel = new JLabel(Icons.getInstance().getNetworkStatusOnline());
        skipCheckingLinksButton = new JToggleButton(Messages.getMessage("browser.skipCheckingLinks"));
        skipCheckingLinksButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                if (taskContext != null) {
                    taskContext.setStop(skipCheckingLinksButton.isSelected());
                }
            }
        });

        showHidCheckBox = new JCheckBox(Messages.getMessage("browser.showHidden.label"), showHidden);
        showHidCheckBox.setToolTipText(Messages.getMessage("browser.showHidden.tooltip"));
        showHidCheckBox.setMnemonic(Messages.getMessage("browser.showHidden.mnemonic").charAt(0));
        Font tmpFont = showHidCheckBox.getFont();
        showHidCheckBox.setFont(tmpFont.deriveFont(tmpFont.getSize() * 0.9f));
        showHidCheckBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                updateUiFilters();
            }
        });

        final String defaultFilterText = Messages.getMessage("browser.nameFilter.defaultText");
        filterField = new JTextField("", 16);
        filterField.setForeground(filterField.getDisabledTextColor());
        filterField.setToolTipText(Messages.getMessage("browser.nameFilter.tooltip"));
        PromptSupport.setPrompt(defaultFilterText, filterField);
        filterField.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                documentChanged();
            }

            void documentChanged() {
                if (filterField.getText().length() == 0) {
                    updateUiFilters();
                }
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                documentChanged();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                documentChanged();
            }
        });
        filterField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                updateUiFilters();
            }
        });

        AbstractAction actionClearRegexFilter = new

        AbstractAction(Messages.getMessage("browser.nameFilter.clearFilterText")) {

            @Override
            public void actionPerformed(ActionEvent e) {
                filterField.setText("");
            }
        };
        filterField.getActionMap().put(ACTION_FOCUS_ON_TABLE, actionFocusOnTable);
        filterField.getActionMap().put(ACTION_CLEAR_REGEX_FILTER, actionClearRegexFilter);

        filterField.getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke("ENTER"), ACTION_FOCUS_ON_TABLE);
        filterField.getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), ACTION_FOCUS_ON_TABLE);
        filterField.getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
                ACTION_FOCUS_ON_TABLE);
        filterField.getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
                ACTION_FOCUS_ON_TABLE);
        filterField.getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
                ACTION_CLEAR_REGEX_FILTER);

        JLabel nameFilterLabel = new JLabel(Messages.getMessage("browser.nameFilter"));
        nameFilterLabel.setLabelFor(filterField);
        nameFilterLabel.setDisplayedMnemonic(Messages.getMessage("browser.nameFilter.mnemonic").charAt(0));

        sorter.setRowFilter(createFilter());
        statusLabel = new JLabel();

        actionApproveButton = new JButton();
        actionApproveButton.setFont(actionApproveButton.getFont().deriveFont(Font.BOLD));
        actionCancelButton = new JButton();

        ActionMap browserActionMap = this.getActionMap();
        browserActionMap.put(ACTION_FOCUS_ON_REGEX_FILTER, new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                filterField.requestFocus();
                filterField.selectAll();
                GuiUtils.blinkComponent(filterField);
            }
        });

        browserActionMap.put(ACTION_FOCUS_ON_PATH, new SetFocusOnAction(pathField));
        browserActionMap.put(ACTION_SWITCH_SHOW_HIDDEN, new ClickOnJComponentAction(showHidCheckBox));
        browserActionMap.put(ACTION_REFRESH, refreshAction);
        browserActionMap.put(ACTION_ADD_CURRENT_LOCATION_TO_FAVORITES, addCurrentLocationToFavoriteAction);
        browserActionMap.put(ACTION_GO_UP, goUpAction);
        browserActionMap.put(ACTION_FOCUS_ON_TABLE, new SetFocusOnAction(tableFiles));

        InputMap browserInputMap = this.getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        browserInputMap.put(KeyStroke.getKeyStroke("control F"), ACTION_FOCUS_ON_REGEX_FILTER);
        browserInputMap.put(KeyStroke.getKeyStroke("control L"), ACTION_FOCUS_ON_PATH);
        browserInputMap.put(KeyStroke.getKeyStroke("F4"), ACTION_FOCUS_ON_PATH);
        browserInputMap.put(KeyStroke.getKeyStroke("control H"), ACTION_SWITCH_SHOW_HIDDEN);
        browserInputMap.put(KeyStroke.getKeyStroke("control R"), ACTION_REFRESH);
        browserInputMap.put(KeyStroke.getKeyStroke("F5"), ACTION_REFRESH);
        browserInputMap.put(KeyStroke.getKeyStroke("control D"), ACTION_ADD_CURRENT_LOCATION_TO_FAVORITES);
        browserInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.ALT_DOWN_MASK), ACTION_GO_UP);
        browserInputMap.put(KeyStroke.getKeyStroke("control T"), ACTION_FOCUS_ON_TABLE);

        //DO layout
        // create the layer for the panel using our custom layerUI
        tableScrollPane = new JScrollPane(tableFiles);

        JPanel tableScrollPaneWithFilter = new JPanel(new BorderLayout());
        tableScrollPaneWithFilter.add(tableScrollPane);
        JToolBar filtersToolbar = new JToolBar("Filters");
        filtersToolbar.setFloatable(false);
        filtersToolbar.setBorderPainted(true);
        tableScrollPaneWithFilter.add(filtersToolbar, BorderLayout.SOUTH);
        filtersToolbar.add(nameFilterLabel);
        filtersToolbar.add(filterField);
        filtersToolbar.add(showHidCheckBox);
        JSplitPane tableWithPreviewPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true,
                tableScrollPaneWithFilter, previewComponent);
        tableWithPreviewPane.setOneTouchExpandable(true);

        JPanel loadingPanel = new JPanel(new MigLayout());
        loadingPanel.add(loadingIconLabel, "right");
        loadingPanel.add(loadingProgressBar, "left, w 420:420:500,wrap");
        loadingPanel.add(skipCheckingLinksButton, "span, right");
        tablePanel.add(loadingPanel, LOADING);
        tablePanel.add(tableWithPreviewPane, TABLE);

        JSplitPane jSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(favoritesPanel),
                tablePanel);
        jSplitPane.setOneTouchExpandable(true);
        jSplitPane.setDividerLocation(180);

        JPanel southPanel = new JPanel(new MigLayout("", "[]push[][]", ""));
        southPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        southPanel.add(statusLabel);
        southPanel.add(actionApproveButton);
        southPanel.add(actionCancelButton);

        this.add(upperPanel, BorderLayout.NORTH);
        this.add(jSplitPane, BorderLayout.CENTER);
        this.add(southPanel, BorderLayout.SOUTH);

        try {
            selectionChanged();
        } catch (FileSystemException e) {
            LOGGER.error("Can't initialize default selection mode", e);
        }
        // Why this not done in EDT?
        // Is it assume that constructor is invoked from an  EDT?
        try {
            if (initialPath == null) {
                goToUrl(VFSUtils.getUserHome());
            } else {
                try {
                    FileObject resolveFile = VFSUtils.resolveFileObject(initialPath);
                    if (resolveFile != null && resolveFile.getType() == FileType.FILE) {
                        loadAndSelSingleFile(resolveFile);
                        pathField.setText(resolveFile.getURL().toString());
                        targetFileSelected = true;
                        return;
                    }
                } catch (FileSystemException fse) {
                    // Intentionally empty
                }
                goToUrl(initialPath);
            }
        } catch (FileSystemException e1) {
            LOGGER.error("Can't initialize default location", e1);
        }
        showTable();
    }

    private void updateStatusText() {
        int tableFilesRowCount = tableFiles.getRowCount() - 1;
        int modelCount = vfsTableModel.getRowCount() - 1;
        statusLabel.setText(
                Messages.getMessage("browser.folderContainsXElementsShowingY", modelCount, tableFilesRowCount));
    }

    private void updateUiFilters() {
        showHidden = showHidCheckBox.isSelected();
        sorter.setRowFilter(createFilter());
        updateStatusText();
    }

    private RowFilter<VfsTableModel, Integer> createFilter() {
        RowFilter<VfsTableModel, Integer> regexFilter = new VfsTableModelFileNameRowFilter(filterField);
        RowFilter<VfsTableModel, Integer> hiddenFilter = new VfsTableModelHiddenFileRowFilter(showHidden);
        RowFilter<VfsTableModel, Integer> alwaysShowParent = new VfsTableModelShowParentRowFilter();
        @SuppressWarnings("unchecked")
        RowFilter<VfsTableModel, Integer> filters = RowFilter.andFilter(Arrays.asList(regexFilter, hiddenFilter));
        filters = RowFilter.orFilter(Arrays.asList(filters, alwaysShowParent));
        return filters;
    }

    private JLabel getTitleListLabel(String text, Icon icon) {
        JLabel jLabel = new JLabel(text, icon, SwingConstants.CENTER);
        Font font = jLabel.getFont();
        jLabel.setFont(font.deriveFont(Font.ITALIC | Font.BOLD, font.getSize() * 1.1f));
        jLabel.setBorder(BorderFactory.createEmptyBorder(10, 3, 0, 3));

        return jLabel;
    }

    private JPopupMenu addPopupMenu(JList list, String... actions) {
        JPopupMenu favoritesPopupMenu = new JPopupMenu();
        for (String action : actions) {
            favoritesPopupMenu.add(list.getActionMap().get(action));
        }
        list.addKeyListener(new PopupListener(favoritesPopupMenu));
        list.addMouseListener(new PopupListener(favoritesPopupMenu));
        return favoritesPopupMenu;
    }

    private void addOpenActionToList(final JList favoritesList) {
        favoritesList.getActionMap().put(ACTION_OPEN, new OpenSelectedFavorite(this, favoritesList));
        favoritesList.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                    favoritesList.getActionMap().get(ACTION_OPEN).actionPerformed(null);
                }
            }
        });
        InputMap favoritesListInputMap = favoritesList.getInputMap(JComponent.WHEN_FOCUSED);
        favoritesListInputMap.put(KeyStroke.getKeyStroke("ENTER"), ACTION_OPEN);
    }

    private void addEditActionToList(final JList favoritesList, final MutableListModel<Favorite> listModel) {
        favoritesList.getActionMap().put(ACTION_EDIT, new EditFavorite(favoritesList, listModel));

        InputMap favoritesListInputMap = favoritesList.getInputMap(JComponent.WHEN_FOCUSED);
        favoritesListInputMap.put(KeyStroke.getKeyStroke("F2"), ACTION_EDIT);
    }

    private void selectionChanged() throws FileSystemException {
        LOGGER.debug("Updating selection");
        boolean acceptEnabled = false;
        if (getSelectedFiles().length == 0) {
            acceptEnabled = false;
        } else if (isMultiSelectionEnabled()) {
            boolean filesSelected = false;
            boolean folderSelected = false;

            for (FileObject fo : getSelectedFiles()) {
                FileType fileType = fo.getType();
                if (fileType == FileType.FILE) {
                    filesSelected = true;
                } else if (fileType == FileType.FOLDER) {
                    folderSelected = true;
                }
            }
            if (selectionMode == SelectionMode.FILES_ONLY && filesSelected && !folderSelected) {
                acceptEnabled = true;
            } else if (selectionMode == SelectionMode.DIRS_ONLY && !filesSelected && folderSelected) {
                acceptEnabled = true;
            } else if (selectionMode == SelectionMode.DIRS_AND_FILES) {
                acceptEnabled = true;
            }
        } else {
            FileObject selectedFileObject = getSelectedFileObject();
            FileType type = selectedFileObject.getType();
            if (selectionMode == SelectionMode.FILES_ONLY && type == FileType.FILE
                    || selectionMode == SelectionMode.DIRS_ONLY && type == FileType.FOLDER) {
                acceptEnabled = true;
            } else if (SelectionMode.DIRS_AND_FILES == selectionMode) {
                acceptEnabled = true;
            }
        }

        if (actionApproveDelegate != null) {
            actionApproveDelegate.setEnabled(acceptEnabled);
        }
        actionApproveButton.setEnabled(acceptEnabled);
    }

    public FileObject getCurrentLocation() {
        return currentLocation;
    }

    public MutableListModel getFavoritesUserListModel() {
        return favoritesUserListModel;
    }

    public void showLoading() {
        LOGGER.trace("Showing loading panel");
        loadingProgressBar.setIndeterminate(true);
        loadingProgressBar.setString(Messages.getMessage("browser.loading..."));
        skipCheckingLinksButton.setSelected(false);
        cardLayout.show(tablePanel, LOADING);
    }

    public void showTable() {
        LOGGER.trace("Showing result table");
        tableScrollPane.getVerticalScrollBar().setValue(0);
        cardLayout.show(tablePanel, TABLE);
    }

    public boolean isMultiSelectionEnabled() {
        return multiSelectionEnabled;
    }

    public void setMultiSelectionEnabled(boolean b) {
        int selectionMode = b ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
                : ListSelectionModel.SINGLE_SELECTION;
        tableFiles.getSelectionModel().setSelectionMode(selectionMode);
        if (multiSelectionEnabled == b) {
            return;
        }
        boolean oldValue = multiSelectionEnabled;
        multiSelectionEnabled = b;
        firePropertyChange(MULTI_SELECTION_ENABLED_CHANGED_PROPERTY, oldValue, multiSelectionEnabled);
        try {
            selectionChanged();
        } catch (FileSystemException e) {
            LOGGER.error("Error during update state", e);
        }
    }

    public SelectionMode getSelectionMode() {
        return selectionMode;
    }

    public void setSelectionMode(SelectionMode mode) {
        if (selectionMode == mode) {
            return;
        }
        SelectionMode oldValue = selectionMode;
        this.selectionMode = mode;
        firePropertyChange(MULTI_SELECTION_MODE_CHANGED_PROPERTY, oldValue, selectionMode);
        try {
            selectionChanged();
        } catch (FileSystemException e) {
            LOGGER.error("Error during update state", e);
        }
    }

    public void setApproveAction(Action action) {
        actionApproveDelegate = action;
        actionApproveButton.setAction(actionApproveDelegate);
        if (action != null) {
            actionApproveButton
                    .setText(String.format("%s [Ctrl+Enter]", actionApproveDelegate.getValue(Action.NAME)));
        }
        if (targetFileSelected) {
            actionApproveDelegate.actionPerformed(
                    // TODO:  Does actionResult provide an ID for 2nd param here,
                    // or should use a Random number?
                    new ActionEvent(action, (int) new java.util.Date().getTime(), "SELECTED_FILE"));
        } else
            try {
                selectionChanged();
            } catch (FileSystemException e) {
                LOGGER.warn("Problem with checking selection conditions", e);
            }
    }

    public void setCancelAction(Action cancelAction) {
        actionCancelDelegate = cancelAction;
        actionCancelButton.setAction(actionCancelDelegate);
        try {
            selectionChanged();
        } catch (FileSystemException e) {
            LOGGER.warn("Problem with checking selection conditions", e);
        }

    }

    public FileObject getSelectedFileObject() {
        int selectedRow = tableFiles.getSelectedRow();
        if (selectedRow > -1) {
            return vfsTableModel.get(tableFiles.convertRowIndexToModel(selectedRow));
        }
        return null;
    }

    public FileObject[] getSelectedFiles() {
        int[] selectedRows = tableFiles.getSelectedRows();
        FileObject[] fileObjects = new FileObject[selectedRows.length];
        for (int i = 0; i < selectedRows.length; i++) {
            fileObjects[i] = vfsTableModel.get(tableFiles.convertRowIndexToModel(selectedRows[i]));
        }
        return fileObjects;
    }

    public void selectNextFileStarting(String string) {
        LOGGER.debug("Looking for file starting with {}", string);
        int selectedRow = tableFiles.getSelectedRow();
        selectedRow = selectedRow < 0 ? 0 : selectedRow;
        LOGGER.debug("Starting search with row {}", selectedRow);
        boolean fullLoop;
        int started = selectedRow;
        do {
            LOGGER.debug("Checking table row {}", selectedRow);
            int convertRowIndexToModel = tableFiles.convertRowIndexToModel(selectedRow);
            LOGGER.debug("Table row {} is row {} from model", selectedRow, convertRowIndexToModel);
            FileObject fileObject = vfsTableModel.get(convertRowIndexToModel);
            LOGGER.debug("Checking {} if begins with {}", fileObject.getName().getBaseName(), string);
            if (fileObject.getName().getBaseName().toLowerCase().startsWith(string.toLowerCase())) {
                tableFiles.getSelectionModel().setSelectionInterval(selectedRow, selectedRow);
                tableFiles.scrollRectToVisible(new Rectangle(tableFiles.getCellRect(selectedRow, 0, true)));
                break;
            }
            selectedRow++;
            selectedRow = selectedRow >= tableFiles.getRowCount() ? 0 : selectedRow;
            fullLoop = selectedRow == started;
        } while (!fullLoop);
    }

    private final class QuickSearchKeyAdapter extends KeyAdapter {

        private static final String LETTERS = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
        private static final String DIGITS = "0123456789";
        private static final String OTHER_CHARS = "!@#$%^&*()()-_=+[];:'\",./ ";
        private static final String ALLOWED_CHARS = LETTERS + DIGITS + OTHER_CHARS;
        private long lastTimeTyped = 0;
        private long typeTimeout = 500;
        private StringBuilder sb;

        public QuickSearchKeyAdapter() {
            sb = new StringBuilder();
        }

        @Override
        public void keyTyped(KeyEvent e) {
            char keyChar = e.getKeyChar();
            if (ALLOWED_CHARS.indexOf(keyChar) > -1) {
                if (System.currentTimeMillis() > lastTimeTyped + typeTimeout) {
                    sb.setLength(0);
                }
                sb.append(keyChar);
                selectNextFileStarting(sb.toString());
                lastTimeTyped = System.currentTimeMillis();
            }

        }
    }

    private final class BaseNavigateActionGoUp extends BaseNavigateAction {
        private BaseNavigateActionGoUp(VfsBrowser browser) {
            super(browser);
            putValue(SMALL_ICON, Icons.getInstance().getArrowTurn90());
            putValue(SHORT_DESCRIPTION, Messages.getMessage("nav.goFolderUp"));
        }

        @Override
        public void performLongOperation(CheckBeforeActionResult actionResult) {
            LOGGER.info("Executing going up");
            try {
                goToUrl(currentLocation.getParent());
            } catch (FileSystemException e) {
                LOGGER.error("Error go UP", e);
            }
        }

        @Override
        protected boolean canGoUrl() {
            try {
                FileObject parent = currentLocation.getParent();
                return parent != null && VFSUtils.canGoUrl(parent);
            } catch (FileSystemException e) {
                LOGGER.error("Can't get parent of current location", e);
            }
            return false;
        }

        @Override
        protected boolean canExecuteDefaultAction() {
            return false;
        }
    }

    private final class BaseNavigateActionOpen extends BaseNavigateAction {
        private BaseNavigateActionOpen(VfsBrowser browser) {
            super(browser);
        }

        @Override
        public void performLongOperation(CheckBeforeActionResult checkBeforeActionResult) {
            int selectedRow = tableFiles.getSelectedRow();
            FileObject fileObject = vfsTableModel.get(tableFiles.convertRowIndexToModel(selectedRow));
            if (canExecuteDefaultAction() && actionApproveButton.isEnabled()) {
                actionApproveButton.doClick();
            } else {
                goToUrl(fileObject);
            }
        }

        @Override
        protected boolean canGoUrl() {
            int selectedRow = tableFiles.getSelectedRow();
            if (selectedRow > -1) {
                FileObject fileObject = vfsTableModel.get(tableFiles.convertRowIndexToModel(selectedRow));
                return VFSUtils.canGoUrl(fileObject);
            }
            return false;

        }

        @Override
        protected boolean canExecuteDefaultAction() {
            int selectedRow = tableFiles.getSelectedRow();
            if (SelectionMode.FILES_ONLY.equals(selectionMode)
                    || SelectionMode.DIRS_AND_FILES.equals(selectionMode)) {
                if (selectedRow > -1) {
                    FileObject fileObject = vfsTableModel.get(tableFiles.convertRowIndexToModel(selectedRow));
                    try {
                        return FileType.FILE.equals(fileObject.getType())
                                || FileType.FILE_OR_FOLDER.equals(fileObject.getType());
                    } catch (FileSystemException e) {
                        LOGGER.warn("Cant' get file type", e);
                    }
                }
            }
            return false;
        }
    }

    private final class BaseNavigateActionRefresh extends BaseNavigateAction {
        private BaseNavigateActionRefresh(VfsBrowser browser) {
            super(browser);
            putValue(SMALL_ICON, Icons.getInstance().getArrowCircleDouble());
            putValue(SHORT_DESCRIPTION, Messages.getMessage("nav.refreshActionLabelText"));
        }

        @Override
        public void performLongOperation(CheckBeforeActionResult checkBeforeActionResult) {
            try {
                currentLocation.refresh();
            } catch (FileSystemException e) {
                LOGGER.error("Can't refresh location", e);
            }
            goToUrl(currentLocation);
        }

        @Override
        protected boolean canGoUrl() {
            return true;
        }

        @Override
        protected boolean canExecuteDefaultAction() {
            return false;
        }
    }

}