blob: 9317384b3eafd7b3044feee26d1d9da9adae3cc7 [file] [log] [blame]
/*
* 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 homeDirectoryChanged(VFSBrowserSheet fileBrowserSheet,
FileObject previousHomeDirectory) {
for (VFSBrowserSheetListener listener : this) {
listener.homeDirectoryChanged(fileBrowserSheet, previousHomeDirectory);
}
}
@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 FileObject homeDirectory;
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.
*
* @throws FileSystemException if there are any problems.
*/
public VFSBrowserSheet() throws FileSystemException {
this(Mode.OPEN);
}
/**
* Creates a new VFSBrowserSheet <p> Note that this version sets by default
* the user home as root folder, which is probably not that useful.
*
* @param mode The mode for opening the sheet.
* @throws FileSystemException if there are any problems.
* @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.
* @param rootFolder The root folder full name.
* @throws FileSystemException if there are any problems.
* @see Mode
*/
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.
* @param rootFolder The root folder full name.
* @throws FileSystemException if there are any problems.
* @see Mode
*/
public VFSBrowserSheet(FileSystemManager manager, Mode mode, String rootFolder)
throws FileSystemException {
this(manager, mode, rootFolder, null);
}
/**
* Creates a new VFSBrowserSheet.
* <p> Note that this version of the constructor must be used when a
* custom home 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.
* @param rootFolder The root folder full name.
* @param homeFolder The default for the "home" folder (full name).
* @throws FileSystemException if there are any problems.
* @see Mode
*/
public VFSBrowserSheet(FileSystemManager manager, Mode mode, String rootFolder, String homeFolder)
throws FileSystemException
{
if (mode == null) {
throw new IllegalArgumentException("Mode is null.");
}
if (rootFolder == null) {
throw new IllegalArgumentException("Root folder is null.");
}
this.mode = mode;
// Note: these three methods all could trigger events, but since we're
// in the constructor and the skin isn't set yet, there will not be any
// listeners registered yet
setManager(manager);
setRootDirectory(rootFolder);
setHomeDirectory(homeFolder == null ? USER_HOME : homeFolder);
installSkin(VFSBrowserSheet.class);
}
/**
* Creates a new VFSBrowserSheet.
* <p> Note that this version of the constructor must be used when a
* custom home 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.
* @param rootFolder The root folder object.
* @param homeFolder The default for the "home" folder.
* @throws FileSystemException if there are any problems.
* @see Mode
*/
public VFSBrowserSheet(FileSystemManager manager, Mode mode, FileObject rootFolder, FileObject homeFolder)
throws FileSystemException
{
if (mode == null) {
throw new IllegalArgumentException("Mode is null.");
}
if (rootFolder == null) {
throw new IllegalArgumentException("Root folder is null.");
}
this.mode = mode;
// Note: these three methods all could trigger events, but since we're
// in the constructor and the skin isn't set yet, there will not be any
// listeners registered yet
setManager(manager);
setRootDirectory(rootFolder);
setHomeDirectory(homeFolder == null ? rootFolder : homeFolder);
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 is null.");
}
Mode previousMode = this.mode;
if (previousMode != mode) {
this.mode = mode;
fileBrowserSheetListeners.modeChanged(this, previousMode);
}
}
public FileObject getRootDirectory() {
return rootDirectory;
}
public void setRootDirectory(String rootDirectory) throws FileSystemException {
setRootDirectory(manager.resolveFile(rootDirectory));
}
public void setRootDirectory(FileObject rootDirectory) throws FileSystemException {
if (rootDirectory == null) {
throw new IllegalArgumentException("Root directory is null.");
}
// Give some grace to set the root folder to an actual file and
// have it work (by using the parent folder instead)
if (rootDirectory.getType() != FileType.FOLDER) {
rootDirectory = rootDirectory.getParent();
if (rootDirectory == null || rootDirectory.getType() != FileType.FOLDER) {
throw new IllegalArgumentException("Root file is not a directory.");
}
}
if (rootDirectory.exists()) {
FileObject previousRootDirectory = this.rootDirectory;
if (!rootDirectory.equals(previousRootDirectory)) {
this.rootDirectory = rootDirectory;
selectedFiles.clear();
fileBrowserSheetListeners.rootDirectoryChanged(this, previousRootDirectory);
}
} else {
setRootDirectory(rootDirectory.getParent());
}
}
public FileObject getHomeDirectory() {
return homeDirectory;
}
public void setHomeDirectory(String homeDirectory)
throws FileSystemException
{
setHomeDirectory(manager.resolveFile(homeDirectory));
}
public void setHomeDirectory(FileObject homeDirectory)
throws FileSystemException
{
if (homeDirectory == null) {
throw new IllegalArgumentException("Home file is null.");
}
// Give some grace to set the home folder to an actual file and
// have it work (by using the parent folder instead)
if (homeDirectory.getType() != FileType.FOLDER) {
homeDirectory = homeDirectory.getParent();
if (homeDirectory == null || homeDirectory.getType() != FileType.FOLDER) {
throw new IllegalArgumentException("Root file is not a directory.");
}
}
if (homeDirectory.exists()) {
FileObject previousHomeDirectory = this.homeDirectory;
if (!homeDirectory.equals(previousHomeDirectory)) {
this.homeDirectory = homeDirectory;
fileBrowserSheetListeners.homeDirectoryChanged(this, previousHomeDirectory);
}
} else {
setHomeDirectory(homeDirectory.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 The new file to be selected (or {@code null} to clear the selection).
* @throws FileSystemException if there are any problems.
*/
public void setSelectedFile(FileObject file) throws FileSystemException {
if (file == null) {
clearSelection();
} else {
// TODO: will this work right?
// if (file.isAbsolute()) {
if (baseFileName != null && baseFileName.isAncestor(file.getName())) {
setRootDirectory(file.getParent());
}
setSelectedFiles(new ArrayList<>(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<>(selectedFiles);
}
/**
* Sets the selected files.
*
* @param selectedFiles The files to select.
* @return The files that were selected, with duplicates eliminated.
* @throws FileSystemException if there are any problems.
*/
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("Selected file is null.");
}
// TODO: is this correct?
// if (!file.isAbsolute()) {
if (baseFileName == null || !baseFileName.isAncestor(file.getName())) {
file = manager.resolveFile(rootDirectory, file.getName().getBaseName());
}
if (!file.getParent().equals(rootDirectory)) {
throw new IllegalArgumentException(
"Selected file doesn't appear to belong to the current directory.");
}
fileList.add(file);
}
this.selectedFiles = fileList;
// Notify listeners
fileBrowserSheetListeners.selectedFilesChanged(this, previousSelectedFiles);
return getSelectedFiles();
}
/**
* Clears the selection.
*
* @throws FileSystemException if there are any problems.
*/
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;
}
}