eu.geclipse.ui.dialogs.GridFileDialog.java Source code

Java tutorial

Introduction

Here is the source code for eu.geclipse.ui.dialogs.GridFileDialog.java

Source

/*****************************************************************************
 * Copyright (c) 2008 g-Eclipse Consortium
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Initial development of the original code was made for the
 * g-Eclipse project founded by European Union
 * project number: FP6-IST-034327  http://www.geclipse.eu/
 *
 * Contributors:
 *    Mathias Stuempert - initial API and implementation
 *****************************************************************************/

package eu.geclipse.ui.dialogs;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.PlatformUI;

import eu.geclipse.core.filesystem.GEclipseURI;
import eu.geclipse.core.model.GridModel;
import eu.geclipse.core.model.IGridConnectionElement;
import eu.geclipse.core.model.IGridContainer;
import eu.geclipse.core.model.IGridElement;
import eu.geclipse.core.model.IGridModelEvent;
import eu.geclipse.core.model.IGridModelListener;
import eu.geclipse.ui.comparators.TreeColumnComparator;
import eu.geclipse.ui.internal.Activator;
import eu.geclipse.ui.listeners.TreeColumnListener;
import eu.geclipse.ui.providers.DecoratingGridModelLabelProvider;
import eu.geclipse.ui.providers.FileStoreLabelProvider;
import eu.geclipse.ui.providers.GridFileDialogContentProvider;
import eu.geclipse.ui.providers.NewGridModelLabelProvider;
import eu.geclipse.ui.providers.ProgressTreeNode;
import eu.geclipse.ui.widgets.StoredCombo;

/**
 * This is an implementation of a file dialog for both local
 * and remote files that are made available via EFS-implementations.
 * The remote parts make use of the Grid connections that are defined
 * within the user's. The dialog is highly configurable and allows to
 * only show the remote or local parts or both. Furthermore it can be
 * used to select only files or directories or both and only existing
 * or to specify even new files/directories. Last but not least multi-
 * selection may also be possible.
 */
public class GridFileDialog extends TitleAreaDialog {

    /**
     * Private interface used to listen to mode changes.
     */
    private interface IModeChangeListener {

        /**
         * Invoked whenever the dialog's mode has changed.
         *
         * @param mode The new mode.
         */
        public void modeChanged(final int mode);

    }

    /**
     * The <code>ModeManager</code> does manage the dialogs modes.
     * On the one hand it manages the activation states of the
     * provided {@link ToolItem}s, on the other hand it informs
     * listeners about mode changes.
     */
    private static class ModeManager extends SelectionAdapter {

        /**
         * Mode constant for the remote mode.
         */
        public static final int CONNECTION_MODE = 1;

        /**
         * Mode constant for the local mode with the user's home
         * as root directory.
         */
        public static final int HOME_MODE = 2;

        /**
         * Mode constant for the local mode with the workspace
         * as root directory.
         */
        public static final int WS_MODE = 3;

        /**
         * Mode constant for the local mode with the system's root
         * as root directory.
         */
        public static final int ROOT_MODE = 4;

        /**
         * Identifier used to tag the {@link ToolItem}s.
         */
        private static final String MODE_KEY = "button.mode"; //$NON-NLS-1$

        /**
         * List of all available {@link ToolItem}s.
         */
        private List<ToolItem> modeItems = new ArrayList<ToolItem>();

        /**
         * List of all registered listeners.
         */
        private List<IModeChangeListener> listeners = new ArrayList<IModeChangeListener>();

        /**
         * Standard constructor.
         */
        public ModeManager() {
            // empty implementation
        }

        /**
         * Add a new {@link IModeChangeListener} to the list of listeners.
         *
         * @param l The new listener.
         */
        public void addModeChangeListener(final IModeChangeListener l) {
            if (!this.listeners.contains(l)) {
                this.listeners.add(l);
            }
        }

        /**
         * Add a new mode item to the list of items. Any formerly defined
         * item with the same mode will be overwritten.
         *
         * @param item The new item used to select the specified mode.
         * @param mode The mode that refers to the specified item.
         */
        public void addModeItem(final ToolItem item, final int mode) {
            item.setData(MODE_KEY, Integer.valueOf(mode));
            this.modeItems.add(item);
            item.addSelectionListener(this);
        }

        /**
         * Set the mode, i.e. update the selection states of the mode items.
         *
         * @param mode The new mode.
         */
        public void setMode(final int mode) {
            for (ToolItem item : this.modeItems) {
                Integer m = (Integer) item.getData(MODE_KEY);
                item.setSelection((m != null) && (m.intValue() == mode));
            }
        }

        /* (non-Javadoc)
         * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
         */
        @Override
        public void widgetSelected(final SelectionEvent e) {
            for (ToolItem item : this.modeItems) {
                item.setSelection(item == e.widget);
            }
            Object mode = e.widget.getData(MODE_KEY);
            if (mode instanceof Integer) {
                fireModeChanged(((Integer) mode).intValue());
            }
        }

        /**
         * Inform all registered {@link IModeChangeListener}s about a mode
         * changed event.
         *
         * @param mode The new mode to be reported to the listeners.
         */
        private void fireModeChanged(final int mode) {
            for (IModeChangeListener l : this.listeners) {
                l.modeChanged(mode);
            }
        }

    }

    /**
     * {@link ViewerFilter} that filters out all non-folders.
     */
    private static class FolderFilter extends ViewerFilter {

        /**
         * Standard constructor.
         */
        public FolderFilter() {
            // empty implementation
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer,
         *        java.lang.Object, java.lang.Object)
         */
        @Override
        public boolean select(final Viewer viewer, final Object parentElement, final Object element) {
            boolean result = false;
            if (element instanceof IGridElement) {
                if (element instanceof IGridConnectionElement) {
                    result = ((IGridConnectionElement) element).isFolder();
                } else if (element instanceof IGridContainer) {
                    result = true;
                }
            } else if (element instanceof IFileStore) {
                IFileInfo info = ((IFileStore) element).fetchInfo();
                result = info.isDirectory();
            } else if (element instanceof ProgressTreeNode) {
                result = true;
            }
            return result;
        }

    }

    /**
     * {@link ViewerFilter} that filters out all non-remote elements.
     */
    private static class RemoteConnectionFilter extends ViewerFilter {

        /**
         * Standard constructor.
         */
        public RemoteConnectionFilter() {
            // empty implementation
        }

        @Override
        public boolean select(final Viewer viewer, final Object parentElement, final Object element) {
            boolean result = false;
            if (element instanceof IGridConnectionElement) {
                result = !((IGridConnectionElement) element).isLocal();
            } else if (element instanceof ProgressTreeNode) {
                result = true;
            }
            return result;
        }

    }

    /**
     * {@link ViewerFilter} that filters out all files without a specific
     * file extension.
     */
    private static class FileTypeFilter extends ViewerFilter {

        /**
         * Constant for the wildcard filter.
         */
        private static final String WILDCARD = "*"; //$NON-NLS-1$

        /**
         * Separator used to separate filename and prefix.
         */
        private static final String PREFIX_SEPARATOR = "."; //$NON-NLS-1$

        /**
         * The file extension for allowed files.
         */
        private String prefix;

        /**
         * Construct a new standard filter, i.e. a filter that allows all
         * files (*.*).
         */
        public FileTypeFilter() {
            this(null);
        }

        /**
         * Construct a new filter that filters all files with other extensions
         * than the specified. Folders are not filtered.
         *
         * @param prefix Prefix for all non-filtered files.
         */
        public FileTypeFilter(final String prefix) {
            this.prefix = prefix;
        }

        /**
         * Get the prefix of the filtered files.
         *
         * @return The file's prefix.
         */
        public String getPrefix() {
            return this.prefix == null ? WILDCARD : this.prefix;
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer,
         *        java.lang.Object, java.lang.Object)
         */
        @Override
        public boolean select(final Viewer viewer, final Object parentElement, final Object element) {

            boolean result = false;

            if (this.prefix != null) {

                if (element instanceof IFileStore) {
                    boolean isDir = ((IFileStore) element).fetchInfo().isDirectory();
                    String name = ((IFileStore) element).getName();
                    result = isDir || name.endsWith(PREFIX_SEPARATOR + this.prefix);
                }

                else if (element instanceof IGridConnectionElement) {
                    boolean isDir = ((IGridConnectionElement) element).isFolder();
                    String name = ((IGridConnectionElement) element).getName();
                    result = isDir || name.endsWith(PREFIX_SEPARATOR + this.prefix);
                }

                else if (element instanceof IGridContainer) {
                    IResource resource = ((IGridContainer) element).getResource();
                    if ((resource != null) && (resource.getType() == IResource.FILE)) {
                        String name = resource.getName();
                        result = name.endsWith(PREFIX_SEPARATOR + this.prefix);
                    } else {
                        result = true;
                    }
                }

                else if (element instanceof IGridElement) {
                    String name = ((IGridElement) element).getName();
                    result = name.endsWith(PREFIX_SEPARATOR + this.prefix);
                }

                else if (element instanceof ProgressTreeNode) {
                    result = true;
                }

            }

            else {
                result = true;
            }

            return result;

        }

    }

    /**
     * Style constant for none styles.
     */
    public static final int STYLE_NONE = 0x00;

    /**
     * Style constant for a dialog that allows only local files,
     * i.e. no Grid connections.
     */
    public static final int STYLE_ALLOW_ONLY_LOCAL = 0x01;

    /**
     * Style constant for a dialog that allows Grid connections.
     */
    public static final int STYLE_ALLOW_ONLY_CONNECTIONS = 0x02;

    /**
     * Style constant for a dialog that allows only remote files,
     * i.e. only Grid connections that are not referring to local files.
     */
    public static final int STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS = 0x04;

    /**
     * Style constant for a dialog that allows only the selection of
     * files.
     */
    public static final int STYLE_ALLOW_ONLY_FILES = 0x08;

    /**
     * Style constant for a dialog that allows only the selection of
     * directories.
     */
    public static final int STYLE_ALLOW_ONLY_FOLDERS = 0x10;

    /**
     * Style constant for a dialog that allows only the selection of
     * existing files/directories. The filename and url combos can not
     * be edited.
     */
    public static final int STYLE_ALLOW_ONLY_EXISTING = 0x20;

    /**
     * Style contant for a file dialog that allows multi selection.
     */
    public static final int STYLE_MULTI_SELECTION = 0x40;

    /**
     * Empty string constant.
     */
    private static final String EMPTY_STRING = ""; //$NON-NLS-1$

    /**
     * Path separator.
     */
    private static final String PATH_SEPARATOR = "/"; //$NON-NLS-1$

    /**
     * System property for the user's home directory.
     */
    private static final String HOME_PROPERTY = "user.home"; //$NON-NLS-1$

    /**
     * Root folder constant.
     */
    private static final String ROOT_FOLDER = "/"; //$NON-NLS-1$

    private static final Pattern POSIX_FILENAME = Pattern.compile("^[\\w\\.][\\w\\.-]*"); //$NON-NLS-1$

    /**
     * The dialog's tree viewer.
     */
    protected TreeViewer treeViewer;

    /**
     * The combo for selecting the file type.
     */
    protected Combo filetypeCombo;

    /**
     * The combo for selecting and editing the URI directly.
     */
    protected StoredCombo uriCombo;

    /**
     * Map of all available {@link FileTypeFilter}s.
     */
    protected Hashtable<String, FileTypeFilter> filetypeFilters = new Hashtable<String, FileTypeFilter>();

    /**
     * The combo for selecting and editing the filename directly.
     */
    private StoredCombo filenameCombo;

    /**
     * {@link ModeManager} used to manage the tree viewer's mode.
     */
    private ModeManager modeManager;

    /**
     * Listener used to listen to changes in the Grid model.
     */
    private IGridModelListener modelListener;

    /**
     * Listener used to listen to modifications in the uri combo.
     */
    private ModifyListener uriListener;

    /**
     * Listener used to listen to modifications in the filename combo.
     */
    private VerifyListener filenameListener;

    /**
     * Listener used to listen to selections in the tree viewer.
     */
    private ISelectionChangedListener selectionListener;

    /**
     * The style of the dialog.
     */
    private int style;

    /**
     * The current selection of the tree viewer.
     */
    private IFileStore[] currentSelection;

    /**
     * Create a new dialog with the specified style constant.
     *
     * @param parent The dialog's parent {@link Shell}.
     * @param style The dialog's style, i.e. a bitwise or of style
     * constants.
     */
    public GridFileDialog(final Shell parent, final int style) {
        super(parent);
        this.style = style;
        assertStyle();
        setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.RESIZE);
        URL imgURL = Activator.getDefault().getBundle().getResource("icons/wizban/newconn_wiz.gif"); //$NON-NLS-1$
        ImageDescriptor imgDesc = ImageDescriptor.createFromURL(imgURL);
        setTitleImage(imgDesc.createImage());
    }

    /**
     * Convenience method to open a file dialog. If filters have to be
     * specified this method may not be used.
     *
     * @param parent The dialog's parent {@link Shell}.
     * @param style The dialog's style, i.e. a bitwise or of style
     * constants.
     * @return Array containing the {@link URI}s of all selected elements
     * or <code>null</code> if no selection is available.
     */
    public static URI[] openFileDialog(final Shell parent, final int style) {
        URI[] result = null;
        GridFileDialog dialog = new GridFileDialog(parent, style);
        if (dialog.open() == Window.OK) {
            result = dialog.getSelectedURIs();
        }
        return result;
    }

    /**
     * Add a file type filter to this dialog. The filter will appear in
     * the file type combo.
     *
     * @param prefix The prefix of files that will be shown.
     * @param description A description of the filter. This description
     * will be shown in the file type combo.
     */
    public void addFileTypeFilter(final String prefix, final String description) {
        FileTypeFilter filter = new FileTypeFilter(prefix);
        addFileTypeFilter(filter, description);
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.dialogs.TrayDialog#close()
     */
    @Override
    public boolean close() {
        if (this.modelListener != null) {
            GridModel.getRoot().removeGridModelListener(this.modelListener);
        }
        return super.close();
    }

    /**
     * Get all currently selected {@link IFileStore}s that meet the style of
     * this dialog.
     *
     * @return Array containing the {@link IFileStore}s of all selected elements
     * or <code>null</code> if no selection is available.
     */
    public IFileStore[] getSelectedFileStores() {

        IFileStore[] result = null;

        if ((this.currentSelection != null) && (this.currentSelection.length > 0)) {
            result = new IFileStore[this.currentSelection.length];
            System.arraycopy(this.currentSelection, 0, result, 0, result.length);
        }

        return result;

    }

    /**
     * Get all currently selected {@link URI}s.
     *
     * @return Array containing the {@link URI}s of all selected elements
     * or <code>null</code> if no selection is available.
     */
    public URI[] getSelectedURIs() {

        URI[] result = null;
        IFileStore[] stores = getSelectedFileStores();

        if ((stores != null) && (stores.length > 0)) {
            result = new URI[stores.length];
            for (int i = 0; i < stores.length; i++) {
                URI uri = stores[i].toURI();
                GEclipseURI gUri = new GEclipseURI(uri);
                result[i] = gUri.toSlaveURI();
            }
        }

        return result;

    }

    /**
     * Configure the tree viewer's filters according to the dialog's style.
     */
    protected void configureViewerFilters() {

        if (this.treeViewer != null) {

            this.treeViewer.resetFilters();

            if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS)) {
                this.treeViewer.addFilter(new FolderFilter());
            }

            if (hasStyle(STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS)) {
                this.treeViewer.addFilter(new RemoteConnectionFilter());
            }

            if (this.filetypeCombo != null) {

                if (this.filetypeCombo.getItemCount() != this.filetypeFilters.size()) {
                    initializeFileTypeCombo();
                }

                String key = this.filetypeCombo.getText();
                FileTypeFilter filter = this.filetypeFilters.get(key);
                if (filter != null) {
                    this.treeViewer.addFilter(filter);
                }

            }

        }

    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
     */
    @Override
    protected Control createDialogArea(final Composite parent) {

        this.modeManager = new ModeManager();
        this.modeManager.addModeChangeListener(new IModeChangeListener() {
            public void modeChanged(final int mode) {
                setMode(mode);
            }
        });

        GridData gData;

        Label topRule = new Label(parent, SWT.HORIZONTAL | SWT.SEPARATOR);
        topRule.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        Composite mainComp = new Composite(parent, SWT.NONE);
        mainComp.setLayout(new GridLayout(2, false));
        gData = new GridData(GridData.FILL_BOTH);
        gData.grabExcessHorizontalSpace = true;
        gData.grabExcessVerticalSpace = true;
        gData.widthHint = 500;
        gData.heightHint = 400;
        mainComp.setLayoutData(gData);

        Label bottomRule = new Label(parent, SWT.HORIZONTAL | SWT.SEPARATOR);
        bottomRule.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        if (!hasStyle(STYLE_MULTI_SELECTION)) {

            Composite uriComp = new Composite(mainComp, SWT.NONE);
            uriComp.setLayout(new GridLayout(2, false));
            gData = new GridData(GridData.FILL_HORIZONTAL);
            gData.grabExcessHorizontalSpace = true;
            gData.horizontalSpan = 2;
            uriComp.setLayoutData(gData);

            Label uriLabel = new Label(uriComp, SWT.NONE);
            uriLabel.setText(Messages.getString("GridFileDialog.label_URI")); //$NON-NLS-1$
            gData = new GridData();
            uriLabel.setLayoutData(gData);

            this.uriCombo = new StoredCombo(uriComp, SWT.NONE);
            gData = new GridData(GridData.FILL_HORIZONTAL);
            gData.grabExcessHorizontalSpace = true;
            this.uriCombo.setLayoutData(gData);
            this.uriCombo.setEnabled(!hasStyle(STYLE_ALLOW_ONLY_EXISTING));

        }

        if (!hasStyle(STYLE_ALLOW_ONLY_CONNECTIONS | STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS)) {

            ToolBar modeBar = new ToolBar(mainComp, SWT.VERTICAL | SWT.BORDER);
            modeBar.setBackground(getShell().getDisplay().getSystemColor(SWT.COLOR_WHITE));
            gData = new GridData(GridData.FILL_VERTICAL);
            gData.grabExcessVerticalSpace = true;
            modeBar.setLayoutData(gData);

            if (!hasStyle(STYLE_ALLOW_ONLY_LOCAL)) {

                URL connURL = Activator.getDefault().getBundle()
                        .getResource("icons/extras/grid_file_dialog_conn_mode.gif"); //$NON-NLS-1$
                ImageDescriptor connDesc = ImageDescriptor.createFromURL(connURL);

                ToolItem connItem = new ToolItem(modeBar, SWT.CHECK);
                connItem.setImage(connDesc.createImage());
                connItem.setToolTipText(Messages.getString("GridFileDialog.switch_to_connections")); //$NON-NLS-1$
                this.modeManager.addModeItem(connItem, ModeManager.CONNECTION_MODE);

            }

            if (!hasStyle(STYLE_ALLOW_ONLY_CONNECTIONS | STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS)) {

                URL wsURL = Activator.getDefault().getBundle()
                        .getResource("icons/extras/grid_file_dialog_ws_mode.gif"); //$NON-NLS-1$
                ImageDescriptor wsDesc = ImageDescriptor.createFromURL(wsURL);

                ToolItem wsItem = new ToolItem(modeBar, SWT.CHECK);
                wsItem.setImage(wsDesc.createImage());
                wsItem.setToolTipText(Messages.getString("GridFileDialog.switch_to_workspace")); //$NON-NLS-1$
                this.modeManager.addModeItem(wsItem, ModeManager.WS_MODE);

                URL homeURL = Activator.getDefault().getBundle()
                        .getResource("icons/extras/grid_file_dialog_home_mode.gif"); //$NON-NLS-1$
                ImageDescriptor homeDesc = ImageDescriptor.createFromURL(homeURL);

                ToolItem homeItem = new ToolItem(modeBar, SWT.CHECK);
                homeItem.setImage(homeDesc.createImage());
                homeItem.setToolTipText(Messages.getString("GridFileDialog.switch_to_home")); //$NON-NLS-1$
                this.modeManager.addModeItem(homeItem, ModeManager.HOME_MODE);

                URL rootURL = Activator.getDefault().getBundle()
                        .getResource("icons/extras/grid_file_dialog_root_mode.gif"); //$NON-NLS-1$
                ImageDescriptor rootDesc = ImageDescriptor.createFromURL(rootURL);

                ToolItem rootItem = new ToolItem(modeBar, SWT.CHECK);
                rootItem.setImage(rootDesc.createImage());
                rootItem.setToolTipText(Messages.getString("GridFileDialog.switch_to_root")); //$NON-NLS-1$
                this.modeManager.addModeItem(rootItem, ModeManager.ROOT_MODE);

            }

        }

        Composite browserComp = new Composite(mainComp, SWT.NONE);
        GridLayout browserLayout = new GridLayout(1, false);
        browserLayout.marginWidth = 0;
        browserLayout.marginHeight = 0;
        browserComp.setLayout(browserLayout);
        gData = new GridData(GridData.FILL_BOTH);
        gData.grabExcessHorizontalSpace = true;
        gData.grabExcessVerticalSpace = true;
        browserComp.setLayoutData(gData);

        int treeStyle = SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER;
        if (hasStyle(STYLE_MULTI_SELECTION)) {
            treeStyle |= SWT.MULTI;
        } else {
            treeStyle |= SWT.SINGLE;
        }

        this.treeViewer = new TreeViewer(browserComp, treeStyle);
        this.treeViewer.setContentProvider(new GridFileDialogContentProvider());
        NewGridModelLabelProvider lProvider = new NewGridModelLabelProvider();
        lProvider.addColumn(0, FileStoreLabelProvider.COLUMN_TYPE_NAME);
        lProvider.addColumn(1, FileStoreLabelProvider.COLUMN_TYPE_SIZE);
        lProvider.addColumn(2, FileStoreLabelProvider.COLUMN_TYPE_MOD_DATE);
        ILabelDecorator decorator = PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator();
        DecoratingGridModelLabelProvider dProvider = new DecoratingGridModelLabelProvider(lProvider, decorator);
        this.treeViewer.setLabelProvider(dProvider);

        Tree tree = this.treeViewer.getTree();
        tree.setHeaderVisible(true);
        gData = new GridData(GridData.FILL_BOTH);
        gData.grabExcessHorizontalSpace = true;
        gData.grabExcessVerticalSpace = true;
        tree.setLayoutData(gData);

        TreeColumn nameColumn = new TreeColumn(tree, SWT.NONE);
        nameColumn.setText(Messages.getString("GridFileDialog.column_title_name")); //$NON-NLS-1$
        nameColumn.setAlignment(SWT.LEFT);
        nameColumn.setWidth(300);

        TreeColumn sizeColumn = new TreeColumn(tree, SWT.NONE);
        sizeColumn.setText(Messages.getString("GridFileDialog.column_title_size")); //$NON-NLS-1$
        sizeColumn.setAlignment(SWT.RIGHT);
        sizeColumn.setWidth(100);

        TreeColumn modColumn = new TreeColumn(tree, SWT.NONE);
        modColumn.setText(Messages.getString("GridFileDialog.column_title_last_modification")); //$NON-NLS-1$
        modColumn.setAlignment(SWT.CENTER);
        modColumn.setWidth(200);

        TreeColumnListener columnListener = new TreeColumnListener(this.treeViewer);
        for (TreeColumn column : tree.getColumns()) {
            column.addSelectionListener(columnListener);
        }

        tree.setSortColumn(nameColumn);
        tree.setSortDirection(SWT.UP);
        this.treeViewer.setComparator(new TreeColumnComparator(nameColumn));

        Composite fileComp = new Composite(browserComp, SWT.NONE);
        fileComp.setLayout(new GridLayout(2, false));
        gData = new GridData(GridData.FILL_HORIZONTAL);
        gData.grabExcessHorizontalSpace = true;
        fileComp.setLayoutData(gData);

        if (!hasStyle(STYLE_MULTI_SELECTION)) {

            Label filenameLabel = new Label(fileComp, SWT.NONE);
            filenameLabel.setText(
                    hasStyle(STYLE_ALLOW_ONLY_FOLDERS) ? Messages.getString("GridFileDialog.label_foldername") //$NON-NLS-1$
                            : hasStyle(STYLE_ALLOW_ONLY_FILES) ? Messages.getString("GridFileDialog.label_filename") //$NON-NLS-1$
                                    : Messages.getString("GridFileDialog.label_name") //$NON-NLS-1$
            );
            gData = new GridData();
            gData.horizontalAlignment = GridData.BEGINNING;
            filenameLabel.setLayoutData(gData);

            this.filenameCombo = new StoredCombo(fileComp, SWT.BORDER);
            gData = new GridData(GridData.FILL_HORIZONTAL);
            gData.grabExcessHorizontalSpace = true;
            this.filenameCombo.setLayoutData(gData);
            this.filenameCombo.setEnabled(!hasStyle(STYLE_ALLOW_ONLY_EXISTING));

        }

        if (!hasStyle(STYLE_ALLOW_ONLY_FOLDERS)) {

            Label filetypeLabel = new Label(fileComp, SWT.NONE);
            filetypeLabel.setText(Messages.getString("GridFileDialog.label_filetype")); //$NON-NLS-1$
            gData = new GridData();
            gData.horizontalAlignment = GridData.BEGINNING;
            filetypeLabel.setLayoutData(gData);

            this.filetypeCombo = new Combo(fileComp, SWT.BORDER | SWT.READ_ONLY);
            gData = new GridData(GridData.FILL_HORIZONTAL);
            gData.grabExcessHorizontalSpace = true;
            this.filetypeCombo.setLayoutData(gData);

        }

        int mode = !hasStyle(STYLE_ALLOW_ONLY_LOCAL) ? ModeManager.CONNECTION_MODE : ModeManager.WS_MODE;
        this.modeManager.setMode(mode);
        setMode(mode);

        this.treeViewer.addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(final DoubleClickEvent event) {
                handleDoubleClick();
            }
        });

        this.selectionListener = new ISelectionChangedListener() {
            public void selectionChanged(final SelectionChangedEvent event) {
                setNotificationEnabled(false);
                handleSelectionChanged();
                setNotificationEnabled(true);
            }
        };
        this.treeViewer.addSelectionChangedListener(this.selectionListener);

        if (this.uriCombo != null) {
            this.uriListener = new ModifyListener() {
                public void modifyText(final ModifyEvent e) {
                    setNotificationEnabled(false);
                    handleUriChanged();
                    setNotificationEnabled(true);
                }
            };
            this.uriCombo.addModifyListener(this.uriListener);
        }

        if (this.filenameCombo != null) {

            this.filenameListener = new VerifyListener() {
                public void verifyText(final VerifyEvent e) {
                    setNotificationEnabled(false);
                    handleFilenameChanged(e);
                    setNotificationEnabled(true);
                }
            };
            this.filenameCombo.addVerifyListener(this.filenameListener);
        }

        if (this.filetypeCombo != null) {
            this.filetypeCombo.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    configureViewerFilters();
                }
            });
        }

        this.modelListener = new IGridModelListener() {
            public void gridModelChanged(final IGridModelEvent event) {
                if ((event.getType() == IGridModelEvent.ELEMENTS_ADDED)
                        || (event.getType() == IGridModelEvent.ELEMENTS_REMOVED)) {
                    refreshViewer(event.getSource());
                }
            }
        };

        GridModel.getRoot().addGridModelListener(this.modelListener);

        Shell shell = getShell();
        if (hasStyle(STYLE_ALLOW_ONLY_FILES)) {
            if (hasStyle(STYLE_MULTI_SELECTION)) {
                shell.setText(Messages.getString("GridFileDialog.shell_title_files")); //$NON-NLS-1$
            } else {
                shell.setText(Messages.getString("GridFileDialog.shell_title_file")); //$NON-NLS-1$
            }
        } else if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS)) {
            if (hasStyle(STYLE_MULTI_SELECTION)) {
                shell.setText(Messages.getString("GridFileDialog.shell_title_folders")); //$NON-NLS-1$
            } else {
                shell.setText(Messages.getString("GridFileDialog.shell_title_folder")); //$NON-NLS-1$
            }
        } else {
            if (hasStyle(STYLE_MULTI_SELECTION)) {
                shell.setText(Messages.getString("GridFileDialog.shell_title_files_folders")); //$NON-NLS-1$
            } else {
                shell.setText(Messages.getString("GridFileDialog.shell_title_file_folder")); //$NON-NLS-1$
            }
        }

        setTitle(Messages.getString("GridFileDialog.title")); //$NON-NLS-1$

        if (hasStyle(STYLE_ALLOW_ONLY_FILES)) {
            if (hasStyle(STYLE_MULTI_SELECTION)) {
                setMessage(Messages.getString("GridFileDialog.title_files")); //$NON-NLS-1$
            } else {
                setMessage(Messages.getString("GridFileDialog.title_file")); //$NON-NLS-1$
            }
        } else if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS)) {
            if (hasStyle(STYLE_MULTI_SELECTION)) {
                setMessage(Messages.getString("GridFileDialog.title_folders")); //$NON-NLS-1$
            } else {
                setMessage(Messages.getString("GridFileDialog.title_folder")); //$NON-NLS-1$
            }
        } else {
            if (hasStyle(STYLE_MULTI_SELECTION)) {
                setMessage(Messages.getString("GridFileDialog.title_files_folders")); //$NON-NLS-1$
            } else {
                setMessage(Messages.getString("GridFileDialog.title_file_folder")); //$NON-NLS-1$
            }
        }

        addFileTypeFilter(new FileTypeFilter(), Messages.getString("GridFileDialog.label_all_files")); //$NON-NLS-1$

        if (this.filenameCombo != null && this.filenameCombo.getItemCount() != 0) {
            this.filetypeCombo.select(0);
        }

        return mainComp;

    }

    /**
     * Handler method for handling double clicks in the {@link TreeViewer}. This
     * handler expands or collapses tree nodes if the associated
     * element is a folder or selects an element and
     * closes the dialog if the associated element is a file.
     */
    protected void handleDoubleClick() {
        IStructuredSelection selection = (IStructuredSelection) this.treeViewer.getSelection();
        Object object = selection.getFirstElement();
        if (this.treeViewer.isExpandable(object)) {
            boolean state = this.treeViewer.getExpandedState(object);
            this.treeViewer.setExpandedState(object, !state);
        } else {
            setReturnCode(IDialogConstants.OK_ID);
            close();
        }
    }

    /**
     * Handler that handles changes in the filename combo.
     */
    protected void handleFilenameChanged(final VerifyEvent e) {

        String errMsg = null;
        URI uri = getURI();

        if (uri != null) {

            IPath path = new Path(uri.getPath());
            String lastSegment = path.lastSegment();

            String filename = getFilename();
            if (filename == null) {
                filename = EMPTY_STRING;
            }

            if ((lastSegment != null) && lastSegment.equals(filename)) {
                path = path.removeLastSegments(1);
            }

            String newFilename = filename.substring(0, e.start) + e.text + filename.substring(e.end);
            if ((newFilename.length() > 0) && (!validateFilename(newFilename))) {
                e.doit = false;
            } else {
                path = path.append(newFilename);
                String spath = path.toString();
                if (!spath.startsWith(PATH_SEPARATOR)) {
                    spath = PATH_SEPARATOR + spath;
                }

                try {

                    uri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), spath,
                            uri.getQuery(), uri.getFragment());
                    setURI(uri);

                    try {

                        IFileStore fileStore = EFS.getStore(uri);
                        IFileInfo fileInfo = fileStore.fetchInfo();

                        if (hasStyle(STYLE_ALLOW_ONLY_FILES) && fileInfo.isDirectory()) {
                            errMsg = Messages.getString("GridFileDialog.file_only_mode_warning"); //$NON-NLS-1$
                        } else if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS) && !fileInfo.isDirectory()) {
                            errMsg = Messages.getString("GridFileDialog.folder_only_mode_warning"); //$NON-NLS-1$
                        } else {
                            setCurrentSelection(new IFileStore[] { fileStore });
                        }

                    } catch (CoreException cExc) {
                        errMsg = String.format(Messages.getString("GridFileDialog.create_file_store_error"), //$NON-NLS-1$
                                cExc.getLocalizedMessage());
                    }

                } catch (URISyntaxException uriExc) {
                    errMsg = String.format(Messages.getString("GridFileDialog.invalid_file_name_error"), //$NON-NLS-1$
                            newFilename);
                }

            }

        }

        if (errMsg != null) {
            setCurrentSelection(null);
        }

        setErrorMessage(errMsg);

    }

    protected void handleSelectionChanged() {

        String errMsg = null;
        IFileStore[] selection = getSelection();

        if ((selection == null) || (selection.length == 0)) {
            setFilename(null);
            setURI(null);
        }

        else if (hasStyle(STYLE_ALLOW_ONLY_FILES) || hasStyle(STYLE_ALLOW_ONLY_FOLDERS)) {
            for (IFileStore store : selection) {
                IFileInfo info = store.fetchInfo();
                if (hasStyle(STYLE_ALLOW_ONLY_FILES) && info.isDirectory()) {
                    errMsg = Messages.getString("GridFileDialog.file_only_mode_warning"); //$NON-NLS-1$
                    break;
                } else if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS) && !info.isDirectory()) {
                    errMsg = Messages.getString("GridFileDialog.folder_only_mode_warning"); //$NON-NLS-1$
                    break;
                }
            }
        }

        if ((selection != null) && (selection.length == 1)) {
            setURI(selection[0].toURI());
            if (errMsg == null) {
                setFilename(selection[0].getName());
            } else {
                setFilename(null);
            }
        } else {
            setURI(null);
            setFilename(null);
        }

        if (errMsg == null) {
            setCurrentSelection(selection);
        } else {
            setCurrentSelection(null);
        }

        setErrorMessage(errMsg);

    }

    /**
     * Handler that handles changes in the uri combo.
     */
    protected void handleUriChanged() {

        URI uri = getURI();

        if (uri != null) {

            String errMsg = null;

            try {

                IFileStore fileStore = EFS.getStore(uri);
                IFileInfo fileInfo = fileStore.fetchInfo();

                if (hasStyle(STYLE_ALLOW_ONLY_FILES) && fileInfo.isDirectory()) {
                    errMsg = Messages.getString("GridFileDialog.file_only_mode_warning"); //$NON-NLS-1$
                } else if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS) && !fileInfo.isDirectory()) {
                    errMsg = Messages.getString("GridFileDialog.folder_only_mode_warning"); //$NON-NLS-1$
                } else {
                    setFilename(fileInfo.getName());
                    setCurrentSelection(new IFileStore[] { fileStore });
                }

            } catch (CoreException cExc) {
                errMsg = String.format(Messages.getString("GridFileDialog.create_file_store_error"), //$NON-NLS-1$
                        cExc.getLocalizedMessage());
            }

            if (errMsg != null) {
                setFilename(null);
                setCurrentSelection(null);
            }

            setErrorMessage(errMsg);

        } else {
            setFilename(null);
            setCurrentSelection(null);
        }

    }

    protected void setMode(final int mode) {
        switch (mode) {
        case ModeManager.CONNECTION_MODE:
            this.treeViewer.setInput(GridModel.getConnectionManager());
            break;
        case ModeManager.ROOT_MODE:
            this.treeViewer.setInput(EFS.getLocalFileSystem().getStore(new Path(ROOT_FOLDER)));
            break;
        case ModeManager.HOME_MODE:
            String home = System.getProperty(HOME_PROPERTY);
            this.treeViewer.setInput(EFS.getLocalFileSystem().getStore(new Path(home)));
            break;
        case ModeManager.WS_MODE:
            this.treeViewer.setInput(GridModel.getRoot());
            break;
        }
    }

    /**
     * Refreshes the {@link TreeViewer} starting with the specified element. If
     * the element is <code>null</code> the whole {@link TreeViewer} will be
     * refreshed.
     *
     * @param element The {@link IGridElement} that will be refreshed. This also
     *            includes the element's children.
     */
    protected void refreshViewer(final IGridElement element) {
        Control control = this.treeViewer.getControl();
        if (!control.isDisposed()) {
            Display display = control.getDisplay();
            display.asyncExec(new Runnable() {
                public void run() {
                    if (!GridFileDialog.this.treeViewer.getControl().isDisposed()) {
                        if (element == null) {
                            GridFileDialog.this.treeViewer.refresh(false);
                        } else {
                            if (element instanceof IGridContainer) {
                                IGridContainer container = (IGridContainer) element;
                                if (container.isLazy() && container.isDirty()) {
                                    GridFileDialog.this.treeViewer.setChildCount(container,
                                            container.getChildCount());
                                }
                            }
                            GridFileDialog.this.treeViewer.refresh(element, false);
                        }
                    }
                }
            });
        }
    }

    /**
     * Add the specified {@link FileTypeFilter} to this dialog's filters.
     *
     * @param filter The filter to be added.
     * @param description A description of the filter set as refering text
     * in the type filter combo.
     */
    private void addFileTypeFilter(final FileTypeFilter filter, final String description) {
        this.filetypeFilters.put(description, filter);
        configureViewerFilters();
    }

    /**
     * Helper method to determine if this dialog was constructed with
     * the specified style.
     *
     * @param bit One of the style constants.
     * @return True if the specified style constant was specified for
     * this dialog.
     */
    private boolean hasStyle(final int bit) {
        return (this.style & bit) != 0;
    }

    /**
     * Initialize the file type combo with the available file type filters.
     */
    private void initializeFileTypeCombo() {
        if (this.filetypeCombo != null) {
            this.filetypeCombo.removeAll();
            Set<String> keySet = this.filetypeFilters.keySet();
            String[] keyArray = keySet.toArray(new String[keySet.size()]);
            Arrays.sort(keyArray, new Comparator<String>() {
                public int compare(final String s1, final String s2) {
                    String p1 = GridFileDialog.this.filetypeFilters.get(s1).getPrefix();
                    String p2 = GridFileDialog.this.filetypeFilters.get(s2).getPrefix();
                    return p1.compareToIgnoreCase(p2);
                }
            });
            this.filetypeCombo.setItems(keyArray);
            this.filetypeCombo.select(0);
        }
    }

    /**
     * Check if there are not ambiguities in the dialog's style.
     */
    private void assertStyle() {
        Assert.isTrue(
                !((hasStyle(STYLE_ALLOW_ONLY_LOCAL) && hasStyle(STYLE_ALLOW_ONLY_CONNECTIONS))
                        || (hasStyle(STYLE_ALLOW_ONLY_LOCAL) && hasStyle(STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS))
                        || (hasStyle(STYLE_ALLOW_ONLY_CONNECTIONS)
                                && hasStyle(STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS))),
                "Only one of STYLE_ALLOW_ONLY_LOCAL, STYLE_ALLOW_ONLY_CONNECTIONS" //$NON-NLS-1$
                        + " and STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS is allowed" //$NON-NLS-1$
        );
        Assert.isTrue(!(hasStyle(STYLE_ALLOW_ONLY_FILES) && hasStyle(STYLE_ALLOW_ONLY_FOLDERS)),
                "Only one of STYLE_ALLOW_ONLY_FILES and STYLE_ALLOW_ONLY_FOLDERS is allowed" //$NON-NLS-1$
        );
    }

    private String getFilename() {

        String result = null;

        if ((this.filenameCombo != null) && !this.filenameCombo.isDisposed()) {
            result = this.filenameCombo.getText();
        }

        return result;

    }

    private IFileStore[] getSelection() {

        IFileStore[] result = null;

        if ((this.treeViewer != null) && !this.treeViewer.getTree().isDisposed()) {

            List<IFileStore> list = new ArrayList<IFileStore>();
            IStructuredSelection selection = (IStructuredSelection) this.treeViewer.getSelection();
            Iterator<?> iterator = selection.iterator();

            while (iterator.hasNext()) {

                Object o = iterator.next();

                if (o instanceof IGridConnectionElement) {
                    try {
                        o = ((IGridConnectionElement) o).getConnectionFileStore();
                    } catch (CoreException cExc) {
                        // Silently ignored
                    }
                } else if (o instanceof IGridElement) {
                    o = ((IGridElement) o).getFileStore();
                }

                if (o instanceof IFileStore) {
                    list.add((IFileStore) o);
                }

            }

            if (!list.isEmpty()) {
                result = list.toArray(new IFileStore[list.size()]);
            }

        }

        return result;

    }

    private URI getURI() {

        URI result = null;
        setErrorMessage(null);

        if ((this.uriCombo != null) && !this.uriCombo.isDisposed()) {
            String text = this.uriCombo.getText();
            try {
                result = new URI(text);
            } catch (URISyntaxException uriExc) {
                setErrorMessage(String.format(Messages.getString("GridFileDialog.invalid_uri_error"), //$NON-NLS-1$
                        uriExc.getLocalizedMessage()));
            }
        }

        return result;

    }

    private void setCurrentSelection(final IFileStore[] selection) {
        if ((selection != null) && (selection.length > 0)) {
            this.currentSelection = new IFileStore[selection.length];
            System.arraycopy(selection, 0, this.currentSelection, 0, selection.length);
            getButton(IDialogConstants.OK_ID).setEnabled(true);
        } else {
            this.currentSelection = null;
            getButton(IDialogConstants.OK_ID).setEnabled(false);
        }
    }

    private void setFilename(final String filename) {
        if ((this.filenameCombo != null) && !this.filenameCombo.isDisposed()) {
            if (filename == null) {
                this.filenameCombo.setText(EMPTY_STRING);
            } else {
                this.filenameCombo.setText(filename);
            }
        }
    }

    private void setURI(final URI uri) {
        if ((this.uriCombo != null) && !this.uriCombo.isDisposed()) {
            if (uri == null) {
                this.uriCombo.setText(EMPTY_STRING);
            } else {
                GEclipseURI geclURI = new GEclipseURI(uri);
                this.uriCombo.setText(geclURI.toSlaveURI().toString());
            }
        }
    }

    /**
     * Enables or disables notifications about modifications of the uri
     * and filename combos and changes in the selection of the tree viewer.
     *
     * @param b If <code>true</code> the notifications will be switched
     * on, if <code>false</code> they will be switched of.
     */
    protected void setNotificationEnabled(final boolean b) {

        if (b) {

            if (this.uriCombo != null) {
                this.uriCombo.addModifyListener(this.uriListener);
            }

            if (this.filenameCombo != null) {
                this.filenameCombo.addVerifyListener(this.filenameListener);
            }

            this.treeViewer.addSelectionChangedListener(this.selectionListener);

        }

        else {

            if (this.uriCombo != null) {
                this.uriCombo.removeModifyListener(this.uriListener);
            }

            if (this.filenameCombo != null) {
                this.filenameCombo.removeVerifyListener(this.filenameListener);
            }

            this.treeViewer.removeSelectionChangedListener(this.selectionListener);

        }

    }

    private boolean validateFilename(final String filename) {
        return POSIX_FILENAME.matcher(filename).matches();
    }

}