You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by rw...@apache.org on 2013/05/24 23:22:40 UTC

svn commit: r1486223 [3/3] - in /pivot/trunk: ./ core/src/org/apache/pivot/io/ tests/src/org/apache/pivot/tests/ wtk-terra/src/org/apache/pivot/wtk/skin/terra/ wtk/lib/ wtk/src/org/apache/pivot/wtk/ wtk/src/org/apache/pivot/wtk/skin/

Added: pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowser.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowser.java?rev=1486223&view=auto
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowser.java (added)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowser.java Fri May 24 21:22:39 2013
@@ -0,0 +1,477 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.commons.vfs2.FileName;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileSystemManager;
+import org.apache.commons.vfs2.FileType;
+import org.apache.commons.vfs2.VFS;
+import org.apache.pivot.collections.ArrayList;
+import org.apache.pivot.collections.Sequence;
+import org.apache.pivot.collections.immutable.ImmutableList;
+import org.apache.pivot.io.FileObjectList;
+import org.apache.pivot.util.Filter;
+import org.apache.pivot.util.ListenerList;
+
+/**
+ * A file browser that uses the Apache Commons VFS (Virtual File System)
+ * to be able to browse local and remote file systems, and browse inside
+ * of .zip, .tar, etc. archives as well.
+ */
+public class VFSBrowser extends Container {
+    /**
+     * Commons VFS browser skin interface.
+     */
+    public interface Skin extends org.apache.pivot.wtk.Skin {
+        public FileObject getFileAt(int x, int y);
+    }
+
+    private static final URI USER_HOME = new File(System.getProperty("user.home")).toURI();
+
+    private static class FileBrowserListenerList extends WTKListenerList<VFSBrowserListener>
+        implements VFSBrowserListener {
+        @Override
+        public void managerChanged(VFSBrowser fileBrowser, FileSystemManager previousManager) {
+            for (VFSBrowserListener listener : this) {
+                listener.managerChanged(fileBrowser, previousManager);
+            }
+        }
+
+        @Override
+        public void rootDirectoryChanged(VFSBrowser fileBrowser, FileObject previousRootDirectory) {
+            for (VFSBrowserListener listener : this) {
+                listener.rootDirectoryChanged(fileBrowser, previousRootDirectory);
+            }
+        }
+
+        @Override
+        public void selectedFileAdded(VFSBrowser fileBrowser, FileObject file) {
+            for (VFSBrowserListener listener : this) {
+                listener.selectedFileAdded(fileBrowser, file);
+            }
+        }
+
+        @Override
+        public void selectedFileRemoved(VFSBrowser fileBrowser, FileObject file) {
+            for (VFSBrowserListener listener : this) {
+                listener.selectedFileRemoved(fileBrowser, file);
+            }
+        }
+
+        @Override
+        public void selectedFilesChanged(VFSBrowser fileBrowser,
+            Sequence<FileObject> previousSelectedFiles) {
+            for (VFSBrowserListener listener : this) {
+                listener.selectedFilesChanged(fileBrowser, previousSelectedFiles);
+            }
+        }
+
+        @Override
+        public void multiSelectChanged(VFSBrowser fileBrowser) {
+            for (VFSBrowserListener listener : this) {
+                listener.multiSelectChanged(fileBrowser);
+            }
+        }
+
+        @Override
+        public void disabledFileFilterChanged(VFSBrowser fileBrowser,
+            Filter<FileObject> previousDisabledFileFilter) {
+            for (VFSBrowserListener listener : this) {
+                listener.disabledFileFilterChanged(fileBrowser, previousDisabledFileFilter);
+            }
+        }
+    }
+
+    private FileSystemManager manager;
+    private FileName baseFileName;
+    private FileObject rootDirectory;
+    private FileObjectList selectedFiles = new FileObjectList();
+    private boolean multiSelect = false;
+    private Filter<FileObject> disabledFileFilter = null;
+
+    private FileBrowserListenerList fileBrowserListeners = new FileBrowserListenerList();
+
+    /**
+     * Creates a new VFSBrowser
+     * <p>
+     * Note that this version set by default mode to open.
+     */
+    public VFSBrowser()
+            throws FileSystemException
+    {
+        this(null, USER_HOME);
+    }
+
+    /**
+     * Creates a new VFSBrowser
+     * <p>
+     * Note that this version of the constructor must be used when a custom root folder (that
+     * may include a completely different URI scheme) has to be set.
+     *
+     * @param manager
+     * The virtual file system we're going to manage.
+     * @param rootFolder
+     * The root folder full name.
+     */
+    public VFSBrowser(FileSystemManager manager, URI rootFolder)
+            throws FileSystemException
+    {
+        if (rootFolder == null) {
+            throw new IllegalArgumentException();
+        }
+
+        if (manager == null) {
+            this.manager = VFS.getManager();
+        } else {
+            this.manager = manager;
+        }
+        FileObject baseFile = this.manager.getBaseFile();
+        if (baseFile != null) {
+            baseFileName = baseFile.getName();
+        }
+        rootDirectory = this.manager.resolveFile(rootFolder);
+        if (rootDirectory.getType() != FileType.FOLDER) {
+            throw new IllegalArgumentException();
+        }
+        installSkin(VFSBrowser.class);
+    }
+
+    /**
+     * Creates a new VFSBrowser
+     * <p>
+     * Note that this version of the constructor must be used when a custom root folder has to be set.
+     *
+     * @param manager
+     * The virtual file system we're going to manage.
+     * @param rootFolder
+     * The root folder full name.
+     */
+    public VFSBrowser(FileSystemManager manager, String rootFolder)
+            throws FileSystemException
+    {
+        if (rootFolder == null) {
+            throw new IllegalArgumentException();
+        }
+
+        if (manager == null) {
+            this.manager = VFS.getManager();
+        } else {
+            this.manager = manager;
+        }
+        FileObject baseFile = this.manager.getBaseFile();
+        if (baseFile != null) {
+            baseFileName = baseFile.getName();
+        }
+        rootDirectory = this.manager.resolveFile(rootFolder);
+        if (rootDirectory.getType() != FileType.FOLDER) {
+            throw new IllegalArgumentException();
+        }
+        installSkin(VFSBrowser.class);
+    }
+
+    /**
+     * Returns the current file system manager.
+     *
+     * @return
+     * The current file system manager.
+     */
+    public FileSystemManager getManager() {
+        return manager;
+    }
+
+    /**
+     * Returns the current root directory.
+     *
+     * @return
+     * The current root directory.
+     */
+    public FileObject getRootDirectory() {
+        return rootDirectory;
+    }
+
+    /**
+     * Sets the root directory from a string.  Clears any
+     * existing file selection.
+     *
+     * @param rootDirectory
+     */
+    public void setRootDirectory(String rootDirectory)
+            throws FileSystemException
+    {
+        setRootDirectory(manager.resolveFile(rootDirectory));
+    }
+
+    /**
+     * Sets the root directory. Clears any existing file selection.
+     *
+     * @param rootDirectory
+     */
+    public void setRootDirectory(FileObject rootDirectory)
+            throws FileSystemException
+    {
+        if (rootDirectory == null
+            || rootDirectory.getType() != FileType.FOLDER) {
+            throw new IllegalArgumentException();
+        }
+
+        if (rootDirectory.exists()) {
+            FileObject previousRootDirectory = this.rootDirectory;
+
+            if (!rootDirectory.equals(previousRootDirectory)) {
+                this.rootDirectory = rootDirectory;
+                selectedFiles.clear();
+                fileBrowserListeners.rootDirectoryChanged(this, previousRootDirectory);
+            }
+        } else {
+            setRootDirectory(rootDirectory.getParent());
+        }
+    }
+
+    /**
+     * Adds a file to the file selection.
+     *
+     * @param file
+     *
+     * @return
+     * <tt>true</tt> if the file was added; <tt>false</tt> if it was already
+     * selected.
+     */
+    public boolean addSelectedFile(FileObject file)
+            throws FileSystemException
+    {
+        if (file == null) {
+            throw new IllegalArgumentException();
+        }
+
+        // TODO: is this a good way to do this?
+        //if (file.isAbsolute()) {
+        if (baseFileName != null && baseFileName.isDescendent(file.getName())) {
+            if (!file.getParent().equals(rootDirectory)) {
+                throw new IllegalArgumentException();
+            }
+        } else {
+            file = manager.resolveFile(rootDirectory, file.getName().getPath());
+        }
+
+        int index = selectedFiles.add(file);
+        if (index != -1) {
+            fileBrowserListeners.selectedFileAdded(this, file);
+        }
+
+        return (index != -1);
+    }
+
+    /**
+     * Removes a file from the file selection.
+     *
+     * @param file
+     *
+     * @return
+     * <tt>true</tt> if the file was removed; <tt>false</tt> if it was not
+     * already selected.
+     */
+    public boolean removeSelectedFile(FileObject file) {
+        if (file == null) {
+            throw new IllegalArgumentException();
+        }
+
+        int index = selectedFiles.remove(file);
+        if (index != -1) {
+            fileBrowserListeners.selectedFileRemoved(this, file);
+        }
+
+        return (index != -1);
+    }
+
+    /**
+     * When in single-select mode, returns the currently selected file.
+     *
+     * @return
+     * The currently selected file.
+     */
+    public FileObject getSelectedFile() {
+        if (multiSelect) {
+            throw new IllegalStateException("File browser is not in single-select mode.");
+        }
+
+        return (selectedFiles.getLength() == 0) ? null : selectedFiles.get(0);
+    }
+
+    /**
+     * Sets the selection to a single file.
+     *
+     * @param file
+     */
+    public void setSelectedFile(FileObject file)
+            throws FileSystemException
+    {
+        if (file == null) {
+            clearSelection();
+        } else {
+            // TODO: adequate replacement for "isAbsolute"?
+            //if (file.isAbsolute()) {
+            if (baseFileName != null && baseFileName.isDescendent(file.getName())) {
+                setRootDirectory(file.getParent());
+            }
+
+            setSelectedFiles(new ArrayList<FileObject>(file));
+        }
+    }
+
+    /**
+     * Returns the currently selected files.
+     *
+     * @return
+     * An immutable list containing the currently selected files. Note that the returned
+     * list is a wrapper around the actual selection, not a copy. Any changes made to the
+     * selection state will be reflected in the list, but events will not be fired.
+     */
+    public ImmutableList<FileObject> getSelectedFiles() {
+        return new ImmutableList<FileObject>(selectedFiles);
+    }
+
+    /**
+     * Sets the selected files.
+     *
+     * @param selectedFiles
+     * The files to select.
+     *
+     * @return
+     * The files that were selected, with duplicates eliminated.
+     */
+    public Sequence<FileObject> setSelectedFiles(Sequence<FileObject> selectedFiles)
+            throws FileSystemException
+    {
+        if (selectedFiles == null) {
+            throw new IllegalArgumentException("selectedFiles is null.");
+        }
+
+        if (!multiSelect
+            && selectedFiles.getLength() > 1) {
+            throw new IllegalArgumentException("Multi-select is not enabled.");
+        }
+
+        // Update the selection
+        Sequence<FileObject> previousSelectedFiles = getSelectedFiles();
+
+        FileObjectList fileList = new FileObjectList();
+        for (int i = 0, n = selectedFiles.getLength(); i < n; i++) {
+            FileObject file = selectedFiles.get(i);
+
+            if (file == null) {
+                throw new IllegalArgumentException("file is null.");
+            }
+
+            // TODO: is this correct?
+            //if (!file.isAbsolute()) {
+            if (baseFileName == null || !baseFileName.isDescendent(file.getName())) {
+                file = manager.resolveFile(rootDirectory, file.getName().getPath());
+            }
+
+            // TODO: don't do this for now -- revisit later
+            //if (!file.getParent().equals(rootDirectory)) {
+            //    throw new IllegalArgumentException();
+            //}
+
+            fileList.add(file);
+        }
+
+        this.selectedFiles = fileList;
+
+        // Notify listeners
+        fileBrowserListeners.selectedFilesChanged(this, previousSelectedFiles);
+
+        return getSelectedFiles();
+    }
+
+    /**
+     * Clears the selection.
+     */
+    public void clearSelection()
+            throws FileSystemException
+    {
+        setSelectedFiles(new ArrayList<FileObject>());
+    }
+
+    public boolean isFileSelected(FileObject file) {
+        return (selectedFiles.indexOf(file) != -1);
+    }
+
+    /**
+     * Returns the file browser's multi-select state.
+     */
+    public boolean isMultiSelect() {
+        return multiSelect;
+    }
+
+    /**
+     * Sets the file browser's multi-select state.
+     *
+     * @param multiSelect
+     * <tt>true</tt> if multi-select is enabled; <tt>false</tt>, otherwise.
+     */
+    public void setMultiSelect(boolean multiSelect) {
+        if (this.multiSelect != multiSelect) {
+            // Clear any existing selection
+            selectedFiles.clear();
+
+            this.multiSelect = multiSelect;
+
+            fileBrowserListeners.multiSelectChanged(this);
+        }
+    }
+
+    /**
+     * Returns the current file filter.
+     *
+     * @return
+     * The current file filter, or <tt>null</tt> if no filter is set.
+     */
+    public Filter<FileObject> getDisabledFileFilter() {
+        return disabledFileFilter;
+    }
+
+    /**
+     * Sets the file filter.
+     *
+     * @param disabledFileFilter
+     * The file filter to use, or <tt>null</tt> for no filter.
+     */
+    public void setDisabledFileFilter(Filter<FileObject> disabledFileFilter) {
+        Filter<FileObject> previousDisabledFileFilter = this.disabledFileFilter;
+
+        if (previousDisabledFileFilter != disabledFileFilter) {
+            this.disabledFileFilter = disabledFileFilter;
+            fileBrowserListeners.disabledFileFilterChanged(this, previousDisabledFileFilter);
+        }
+    }
+
+    public FileObject getFileAt(int x, int y) {
+        VFSBrowser.Skin fileBrowserSkin = (VFSBrowser.Skin)getSkin();
+        return fileBrowserSkin.getFileAt(x, y);
+    }
+
+    public ListenerList<VFSBrowserListener> getFileBrowserListeners() {
+        return fileBrowserListeners;
+    }
+}

Added: pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserListener.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserListener.java?rev=1486223&view=auto
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserListener.java (added)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserListener.java Fri May 24 21:22:39 2013
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk;
+
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemManager;
+import org.apache.pivot.collections.Sequence;
+import org.apache.pivot.util.Filter;
+
+/**
+ * Commons VFS browser listener interface.
+ */
+public interface VFSBrowserListener {
+    /**
+     * Commons VFS browser listener adapter.
+     */
+    public static class Adapter implements VFSBrowserListener {
+        @Override
+        public void managerChanged(VFSBrowser fileBrowser, FileSystemManager previousManager) {
+            // empty block
+        }
+
+        @Override
+        public void rootDirectoryChanged(VFSBrowser fileBrowser, FileObject previousRootDirectory) {
+            // empty block
+        }
+
+        @Override
+        public void selectedFileAdded(VFSBrowser fileBrowser, FileObject file) {
+            // empty block
+        }
+
+        @Override
+        public void selectedFileRemoved(VFSBrowser fileBrowser, FileObject file) {
+            // empty block
+        }
+
+        @Override
+        public void selectedFilesChanged(VFSBrowser fileBrowser,
+            Sequence<FileObject> previousSelectedFiles) {
+            // empty block
+        }
+
+        @Override
+        public void multiSelectChanged(VFSBrowser fileBrowser) {
+            // empty block
+        }
+
+        @Override
+        public void disabledFileFilterChanged(VFSBrowser fileBrowser,
+            Filter<FileObject> previousDisabledFileFilter) {
+            // empty block
+        }
+    }
+
+    /**
+     * Called when a file browser's FileSystemManager has changed,
+     * (such as when a nested VirtualFileSystem is opened).
+     *
+     * @param fileBrowser
+     * @param previousManager
+     */
+    public void managerChanged(VFSBrowser fileBrowser, FileSystemManager previousManager);
+
+    /**
+     * Called when a file browser's root directory has changed.
+     *
+     * @param fileBrowser
+     * @param previousRootDirectory
+     */
+    public void rootDirectoryChanged(VFSBrowser fileBrowser, FileObject previousRootDirectory);
+
+    /**
+     * Called when a file has been added to a file browser's selection.
+     *
+     * @param fileBrowser
+     * @param file
+     */
+    public void selectedFileAdded(VFSBrowser fileBrowser, FileObject file);
+
+    /**
+     * Called when a file has been removed from a file browser's selection.
+     *
+     * @param fileBrowser
+     * @param file
+     */
+    public void selectedFileRemoved(VFSBrowser fileBrowser, FileObject file);
+
+    /**
+     * Called when a file browser's selection state has been reset.
+     *
+     * @param fileBrowser
+     * @param previousSelectedFiles
+     */
+    public void selectedFilesChanged(VFSBrowser fileBrowser,
+        Sequence<FileObject> previousSelectedFiles);
+
+    /**
+     * Called when a file browser's multi-select flag has changed.
+     *
+     * @param fileBrowser
+     */
+    public void multiSelectChanged(VFSBrowser fileBrowser);
+
+    /**
+     * Called when a file browser's file filter has changed.
+     *
+     * @param fileBrowser
+     * @param previousDisabledFileFilter
+     */
+    public void disabledFileFilterChanged(VFSBrowser fileBrowser,
+        Filter<FileObject> previousDisabledFileFilter);
+}

Added: pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserSheet.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserSheet.java?rev=1486223&view=auto
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserSheet.java (added)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserSheet.java Fri May 24 21:22:39 2013
@@ -0,0 +1,400 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk;
+
+import org.apache.commons.vfs2.FileName;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileSystemManager;
+import org.apache.commons.vfs2.FileType;
+import org.apache.commons.vfs2.VFS;
+import org.apache.pivot.collections.ArrayList;
+import org.apache.pivot.collections.Sequence;
+import org.apache.pivot.collections.immutable.ImmutableList;
+import org.apache.pivot.io.FileObjectList;
+import org.apache.pivot.util.Filter;
+import org.apache.pivot.util.ListenerList;
+
+/**
+ * A file browser sheet that uses the Apache Commons VFS (Virtual File System)
+ * to be able to browse local and remote file systems, and browse inside
+ * of .zip, .tar, etc. archives as well.
+ */
+public class VFSBrowserSheet extends Sheet {
+    /**
+     * Enumeration defining supported modes.
+     */
+    public enum Mode {
+        OPEN,
+        OPEN_MULTIPLE,
+        SAVE_AS,
+        SAVE_TO
+    }
+
+    private static final String USER_HOME = System.getProperty("user.home");
+
+    private static class FileBrowserSheetListenerList
+        extends WTKListenerList<VFSBrowserSheetListener>
+        implements VFSBrowserSheetListener {
+        @Override
+        public void managerChanged(VFSBrowserSheet fileBrowserSheet,
+            FileSystemManager previousManager) {
+            for (VFSBrowserSheetListener listener : this) {
+                listener.managerChanged(fileBrowserSheet, previousManager);
+            }
+        }
+
+        @Override
+        public void modeChanged(VFSBrowserSheet fileBrowserSheet,
+            VFSBrowserSheet.Mode previousMode) {
+            for (VFSBrowserSheetListener listener : this) {
+                listener.modeChanged(fileBrowserSheet, previousMode);
+            }
+        }
+
+        @Override
+        public void rootDirectoryChanged(VFSBrowserSheet fileBrowserSheet,
+            FileObject previousRootDirectory) {
+            for (VFSBrowserSheetListener listener : this) {
+                listener.rootDirectoryChanged(fileBrowserSheet, previousRootDirectory);
+            }
+        }
+
+        @Override
+        public void selectedFilesChanged(VFSBrowserSheet fileBrowserSheet,
+            Sequence<FileObject> previousSelectedFiles) {
+            for (VFSBrowserSheetListener listener : this) {
+                listener.selectedFilesChanged(fileBrowserSheet, previousSelectedFiles);
+            }
+        }
+
+        @Override
+        public void disabledFileFilterChanged(VFSBrowserSheet fileBrowserSheet,
+            Filter<FileObject> previousDisabledFileFilter) {
+            for (VFSBrowserSheetListener listener : this) {
+                listener.disabledFileFilterChanged(fileBrowserSheet,
+                    previousDisabledFileFilter);
+            }
+        }
+    }
+
+    private Mode mode;
+    private FileSystemManager manager;
+    private FileName baseFileName;
+    private FileObject rootDirectory;
+    private FileObjectList selectedFiles = new FileObjectList();
+    private Filter<FileObject> disabledFileFilter = null;
+
+    private FileBrowserSheetListenerList fileBrowserSheetListeners = new FileBrowserSheetListenerList();
+
+    /**
+     * Creates a new VFSBrowserSheet
+     * <p>
+     * Note that this version set by default mode to open and user home as root folder.
+     */
+    public VFSBrowserSheet()
+            throws FileSystemException
+    {
+        this(Mode.OPEN);
+    }
+
+    /**
+     * Creates a new VFSBrowserSheet
+     * <p>
+     * Note that this version set by default the user home as root folder.
+     *
+     * @param mode
+     * The mode for opening the sheet.
+     * @see Mode
+     */
+    public VFSBrowserSheet(Mode mode)
+            throws FileSystemException
+    {
+        this(mode, USER_HOME);
+    }
+
+    /**
+     * Creates a new VFSBrowserSheet
+     * <p>
+     * Note that this version of the constructor must be used when a custom root folder has to be set.
+     *
+     * @param mode
+     * The mode for opening the sheet.
+     * @see Mode
+     *
+     * @param rootFolder
+     * The root folder full name.
+     */
+    public VFSBrowserSheet(Mode mode, String rootFolder)
+            throws FileSystemException
+    {
+        this(null, mode, rootFolder);
+    }
+
+    /**
+     * Creates a new VFSBrowserSheet
+     * <p>
+     * Note that this version of the constructor must be used when a custom root folder has to be set.
+     *
+     * @param manager
+     * The VFS FileSystemManager that we will be browsing.  If <tt>null</tt> the default (local) will
+     * be used.
+     *
+     * @param mode
+     * The mode for opening the sheet.
+     * @see Mode
+     *
+     * @param rootFolder
+     * The root folder full name.
+     */
+    public VFSBrowserSheet(FileSystemManager manager, Mode mode, String rootFolder)
+            throws FileSystemException
+    {
+        if (mode == null) {
+            throw new IllegalArgumentException();
+        }
+
+        if (rootFolder == null) {
+            throw new IllegalArgumentException();
+        }
+
+        setManager(manager);
+
+        this.mode = mode;
+
+        setRootFolder(rootFolder);
+
+        installSkin(VFSBrowserSheet.class);
+    }
+
+    public FileSystemManager getManager() {
+        return manager;
+    }
+
+    public void setManager(FileSystemManager manager)
+            throws FileSystemException
+    {
+        FileSystemManager previousManager = this.manager;
+
+        if (manager == null) {
+            this.manager = VFS.getManager();
+        } else {
+            this.manager = manager;
+        }
+        FileObject baseFile = this.manager.getBaseFile();
+        if (baseFile != null) {
+            baseFileName = baseFile.getName();
+        }
+
+        if (previousManager != null && previousManager != this.manager) {
+            fileBrowserSheetListeners.managerChanged(this, previousManager);
+        }
+    }
+
+    public FileName getBaseFileName() {
+        return baseFileName;
+    }
+
+    public Mode getMode() {
+        return mode;
+    }
+
+    public void setMode(Mode mode) {
+        if (mode == null) {
+            throw new IllegalArgumentException();
+        }
+
+        Mode previousMode = this.mode;
+
+        if (previousMode != mode) {
+            this.mode = mode;
+            fileBrowserSheetListeners.modeChanged(this, previousMode);
+        }
+    }
+
+    public FileObject getRootDirectory() {
+        return rootDirectory;
+    }
+
+    // set the root folder but without firing events
+    public void setRootFolder(String rootFolder)
+             throws FileSystemException
+    {
+        if (rootFolder == null) {
+            throw new IllegalArgumentException();
+        }
+
+        rootDirectory = manager.resolveFile(rootFolder);
+        if (rootDirectory.getType() != FileType.FOLDER) {
+            throw new IllegalArgumentException();
+        }
+
+    }
+
+    public void setRootDirectory(String rootDirectory)
+            throws FileSystemException
+    {
+        setRootDirectory(manager.resolveFile(rootDirectory));
+    }
+
+    public void setRootDirectory(FileObject rootDirectory)
+            throws FileSystemException
+    {
+        if (rootDirectory == null
+            || rootDirectory.getType() != FileType.FOLDER) {
+            throw new IllegalArgumentException();
+        }
+
+        if (rootDirectory.exists()) {
+            FileObject previousRootDirectory = this.rootDirectory;
+
+            if (!rootDirectory.equals(previousRootDirectory)) {
+                this.rootDirectory = rootDirectory;
+                selectedFiles.clear();
+                fileBrowserSheetListeners.rootDirectoryChanged(this, previousRootDirectory);
+            }
+        } else {
+            setRootDirectory(rootDirectory.getParent());
+        }
+    }
+
+    /**
+     * When in single-select mode, returns the currently selected file.
+     *
+     * @return
+     * The currently selected file.
+     */
+    public FileObject getSelectedFile() {
+        if (mode == Mode.OPEN_MULTIPLE) {
+            throw new IllegalStateException("File browser sheet is not in single-select mode.");
+        }
+
+        return (selectedFiles.getLength() == 0) ? null : selectedFiles.get(0);
+    }
+
+    /**
+     * Sets the selection to a single file.
+     *
+     * @param file
+     */
+    public void setSelectedFile(FileObject file)
+            throws FileSystemException
+    {
+        if (file == null) {
+            clearSelection();
+        } else {
+            // TODO: will this work right?
+            //if (file.isAbsolute()) {
+            if (baseFileName != null && baseFileName.isDescendent(file.getName())) {
+                setRootDirectory(file.getParent());
+            }
+
+            setSelectedFiles(new ArrayList<FileObject>(file));
+        }
+    }
+
+    /**
+     * Returns the currently selected files.
+     *
+     * @return
+     * An immutable list containing the currently selected files. Note that the returned
+     * list is a wrapper around the actual selection, not a copy. Any changes made to the
+     * selection state will be reflected in the list, but events will not be fired.
+     */
+    public ImmutableList<FileObject> getSelectedFiles() {
+        return new ImmutableList<FileObject>(selectedFiles);
+    }
+
+    /**
+     * Sets the selected files.
+     *
+     * @param selectedFiles
+     * The files to select.
+     *
+     * @return
+     * The files that were selected, with duplicates eliminated.
+     */
+    public Sequence<FileObject> setSelectedFiles(Sequence<FileObject> selectedFiles)
+            throws FileSystemException
+    {
+        if (selectedFiles == null) {
+            throw new IllegalArgumentException("selectedFiles is null.");
+        }
+
+        if (mode != Mode.OPEN_MULTIPLE
+            && selectedFiles.getLength() > 1) {
+            throw new IllegalArgumentException("Multi-select is not enabled.");
+        }
+
+        // Update the selection
+        Sequence<FileObject> previousSelectedFiles = getSelectedFiles();
+
+        FileObjectList fileList = new FileObjectList();
+        for (int i = 0, n = selectedFiles.getLength(); i < n; i++) {
+            FileObject file = selectedFiles.get(i);
+
+            if (file == null) {
+                throw new IllegalArgumentException("file is null.");
+            }
+
+            // TODO: is this correct?
+            //if (!file.isAbsolute()) {
+            if (baseFileName == null || !baseFileName.isDescendent(file.getName())) {
+                file = manager.resolveFile(rootDirectory, file.getName().getPath());
+            }
+
+            if (!file.getParent().equals(rootDirectory)) {
+                throw new IllegalArgumentException();
+            }
+
+            fileList.add(file);
+        }
+
+        this.selectedFiles = fileList;
+
+        // Notify listeners
+        fileBrowserSheetListeners.selectedFilesChanged(this, previousSelectedFiles);
+
+        return getSelectedFiles();
+    }
+
+    /**
+     * Clears the selection.
+     */
+    public void clearSelection()
+            throws FileSystemException
+    {
+        setSelectedFiles(new ArrayList<FileObject>());
+    }
+
+    public Filter<FileObject> getDisabledFileFilter() {
+        return disabledFileFilter;
+    }
+
+    public void setDisabledFileFilter(Filter<FileObject> disabledFileFilter) {
+        Filter<FileObject> previousDisabledFileFilter = this.disabledFileFilter;
+
+        if (previousDisabledFileFilter != disabledFileFilter) {
+            this.disabledFileFilter = disabledFileFilter;
+            fileBrowserSheetListeners.disabledFileFilterChanged(this, previousDisabledFileFilter);
+        }
+    }
+
+    public ListenerList<VFSBrowserSheetListener> getFileBrowserSheetListeners() {
+        return fileBrowserSheetListeners;
+    }
+}

Added: pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserSheetListener.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserSheetListener.java?rev=1486223&view=auto
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserSheetListener.java (added)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/VFSBrowserSheetListener.java Fri May 24 21:22:39 2013
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk;
+
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemManager;
+import org.apache.pivot.collections.Sequence;
+import org.apache.pivot.util.Filter;
+
+/**
+ * Commons VFS browser sheet listener interface.
+ */
+public interface VFSBrowserSheetListener {
+    /**
+     * Commons VFS browser sheet listener adapter.
+     */
+    public static class Adapter implements VFSBrowserSheetListener {
+        @Override
+        public void managerChanged(VFSBrowserSheet fileBrowserSheet,
+            FileSystemManager previousManager) {
+            // empty block
+        }
+
+        @Override
+        public void modeChanged(VFSBrowserSheet fileBrowserSheet,
+            VFSBrowserSheet.Mode previousMode) {
+            // empty block
+        }
+
+        @Override
+        public void rootDirectoryChanged(VFSBrowserSheet fileBrowserSheet,
+            FileObject previousRootDirectory) {
+            // empty block
+        }
+
+        @Override
+        public void selectedFilesChanged(VFSBrowserSheet fileBrowserSheet,
+            Sequence<FileObject> previousSelectedFiles) {
+            // empty block
+        }
+
+        @Override
+        public void disabledFileFilterChanged(VFSBrowserSheet fileBrowserSheet,
+            Filter<FileObject> previousDisabledFileFilter) {
+            // empty block
+        }
+    }
+
+    /**
+     * Called when a file browser's file system manager has changed
+     * (as when browsing into a new virtual file system).
+     *
+     * @param fileBrowserSheet
+     * @param previousManager
+     */
+    public void managerChanged(VFSBrowserSheet fileBrowserSheet, FileSystemManager previousManager);
+
+    /**
+     * Called when a file browser sheet's mode has changed.
+     *
+     * @param fileBrowserSheet
+     * @param previousMode
+     */
+    public void modeChanged(VFSBrowserSheet fileBrowserSheet, VFSBrowserSheet.Mode previousMode);
+
+    /**
+     * Called when a file browser sheet's root directory has changed.
+     *
+     * @param fileBrowserSheet
+     * @param previousRootDirectory
+     */
+    public void rootDirectoryChanged(VFSBrowserSheet fileBrowserSheet,
+        FileObject previousRootDirectory);
+
+    /**
+     * Called when a file browser sheet's selection state has been reset.
+     *
+     * @param fileBrowserSheet
+     * @param previousSelectedFiles
+     */
+    public void selectedFilesChanged(VFSBrowserSheet fileBrowserSheet,
+        Sequence<FileObject> previousSelectedFiles);
+
+    /**
+     * Called when a file browser sheet's disabled file filter has changed.
+     *
+     * @param fileBrowserSheet
+     * @param previousDisabledFileFilter
+     */
+    public void disabledFileFilterChanged(VFSBrowserSheet fileBrowserSheet,
+        Filter<FileObject> previousDisabledFileFilter);
+}

Added: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/VFSBrowserSkin.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/VFSBrowserSkin.java?rev=1486223&view=auto
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/VFSBrowserSkin.java (added)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/VFSBrowserSkin.java Fri May 24 21:22:39 2013
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.pivot.wtk.skin;
+
+import org.apache.pivot.wtk.Component;
+import org.apache.pivot.wtk.VFSBrowser;
+import org.apache.pivot.wtk.VFSBrowserListener;
+import org.apache.pivot.wtk.skin.ContainerSkin;
+
+/**
+ * Abstract base class for Commons VFS browser skins.
+ */
+public abstract class VFSBrowserSkin extends ContainerSkin
+    implements VFSBrowser.Skin, VFSBrowserListener {
+    @Override
+    public void install(Component component) {
+        super.install(component);
+
+        VFSBrowser fileBrowser = (VFSBrowser)component;
+        fileBrowser.getFileBrowserListeners().add(this);
+    }
+}