/*
* @(#)BasicDirectoryModel.java 1.31 04/05/05
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package javax.swing.plaf.basic;
import java.io.File;
import java.util.*;
import javax.swing.*;
import javax.swing.filechooser.*;
import javax.swing.event.*;
import java.beans.*;
import sun.awt.shell.ShellFolder;
/**
* Basic implementation of a file list.
*
* @version %i% %g%
* @author Jeff Dinkins
*/
public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener {
private JFileChooser filechooser = null;
// PENDING(jeff) pick the size more sensibly
private Vector fileCache = new Vector(50);
private LoadFilesThread loadThread = null;
private Vector files = null;
private Vector directories = null;
private int fetchID = 0;
public BasicDirectoryModel(JFileChooser filechooser) {
this.filechooser = filechooser;
validateFileCache();
}
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if(prop == JFileChooser.DIRECTORY_CHANGED_PROPERTY ||
prop == JFileChooser.FILE_VIEW_CHANGED_PROPERTY ||
prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY ||
prop == JFileChooser.FILE_HIDING_CHANGED_PROPERTY ||
prop == JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) {
validateFileCache();
}
}
/**
* Obsolete - not used.
*/
public void invalidateFileCache() {
}
public Vector<File> getDirectories() {
synchronized(fileCache) {
if (directories != null) {
return directories;
}
Vector fls = getFiles();
return directories;
}
}
public Vector<File> getFiles() {
synchronized(fileCache) {
if (files != null) {
return files;
}
files = new Vector();
directories = new Vector();
directories.addElement(filechooser.getFileSystemView().createFileObject(
filechooser.getCurrentDirectory(), "..")
);
for (int i = 0; i < getSize(); i++) {
File f = (File)fileCache.get(i);
if (filechooser.isTraversable(f)) {
directories.add(f);
} else {
files.add(f);
}
}
return files;
}
}
public void validateFileCache() {
File currentDirectory = filechooser.getCurrentDirectory();
if (currentDirectory == null) {
return;
}
if (loadThread != null) {
loadThread.interrupt();
loadThread.cancelRunnables();
}
fetchID++;
loadThread = new LoadFilesThread(currentDirectory, fetchID);
loadThread.start();
}
/**
* Renames a file in the underlying file system.
*
* @param oldFile a <code>File</code> object representing
* the existing file
* @param newFile a <code>File</code> object representing
* the desired new file name
* @return <code>true</code> if rename succeeded,
* otherwise <code>false</code>
* @since 1.4
*/
public boolean renameFile(File oldFile, File newFile) {
synchronized(fileCache) {
if (oldFile.renameTo(newFile)) {
validateFileCache();
return true;
}
return false;
}
}
public void fireContentsChanged() {
// System.out.println("BasicDirectoryModel: firecontentschanged");
fireContentsChanged(this, 0, getSize()-1);
}
public int getSize() {
return fileCache.size();
}
public boolean contains(Object o) {
return fileCache.contains(o);
}
public int indexOf(Object o) {
return fileCache.indexOf(o);
}
public Object getElementAt(int index) {
return fileCache.get(index);
}
/**
* Obsolete - not used.
*/
public void intervalAdded(ListDataEvent e) {
}
/**
* Obsolete - not used.
*/
public void intervalRemoved(ListDataEvent e) {
}
protected void sort(Vector<? extends File> v){
ShellFolder.sortFiles(v);
}
// Obsolete - not used
protected boolean lt(File a, File b) {
// First ignore case when comparing
int diff = a.getName().toLowerCase().compareTo(b.getName().toLowerCase());
if (diff != 0) {
return diff < 0;
} else {
// May differ in case (e.g. "mail" vs. "Mail")
return a.getName().compareTo(b.getName()) < 0;
}
}
class LoadFilesThread extends Thread {
File currentDirectory = null;
int fid;
Vector runnables = new Vector(10);
public LoadFilesThread(File currentDirectory, int fid) {
super("Basic L&F File Loading Thread");
this.currentDirectory = currentDirectory;
this.fid = fid;
}
private void invokeLater(Runnable runnable) {
runnables.addElement(runnable);
SwingUtilities.invokeLater(runnable);
}
public void run() {
FileSystemView fileSystem = filechooser.getFileSystemView();
File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
Vector<File> acceptsList = new Vector<File>();
if (isInterrupted()) {
return;
}
// run through the file list, add directories and selectable files to fileCache
for (int i = 0; i < list.length; i++) {
if(filechooser.accept(list[i])) {
acceptsList.addElement(list[i]);
}
}
if (isInterrupted()) {
return;
}
// First sort alphabetically by filename
sort(acceptsList);
Vector newDirectories = new Vector(50);
Vector newFiles = new Vector();
// run through list grabbing directories in chunks of ten
for(int i = 0; i < acceptsList.size(); i++) {
File f = (File) acceptsList.elementAt(i);
boolean isTraversable = filechooser.isTraversable(f);
if (isTraversable) {
newDirectories.addElement(f);
} else if (!isTraversable && filechooser.isFileSelectionEnabled()) {
newFiles.addElement(f);
}
if(isInterrupted()) {
return;
}
}
Vector newFileCache = new Vector(newDirectories);
newFileCache.addAll(newFiles);
int newSize = newFileCache.size();
int oldSize = fileCache.size();
if (newSize > oldSize) {
//see if interval is added
int start = oldSize;
int end = newSize;
for (int i = 0; i < oldSize; i++) {
if (!newFileCache.get(i).equals(fileCache.get(i))) {
start = i;
for (int j = i; j < newSize; j++) {
if (newFileCache.get(j).equals(fileCache.get(i))) {
end = j;
break;
}
}
break;
}
}
if (start >= 0 && end > start
&& newFileCache.subList(end, newSize).equals(fileCache.subList(start, oldSize))) {
if(isInterrupted()) {
return;
}
invokeLater(new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid));
newFileCache = null;
}
} else if (newSize < oldSize) {
//see if interval is removed
int start = -1;
int end = -1;
for (int i = 0; i < newSize; i++) {
if (!newFileCache.get(i).equals(fileCache.get(i))) {
start = i;
end = i + oldSize - newSize;
break;
}
}
if (start >= 0 && end > start
&& fileCache.subList(end, oldSize).equals(newFileCache.subList(start, newSize))) {
if(isInterrupted()) {
return;
}
invokeLater(new DoChangeContents(null, 0, new Vector(fileCache.subList(start, end)),
start, fid));
newFileCache = null;
}
}
if (newFileCache != null && !fileCache.equals(newFileCache)) {
if (isInterrupted()) {
cancelRunnables(runnables);
}
invokeLater(new DoChangeContents(newFileCache, 0, fileCache, 0, fid));
}
}
public void cancelRunnables(Vector runnables) {
for(int i = 0; i < runnables.size(); i++) {
((DoChangeContents)runnables.elementAt(i)).cancel();
}
}
public void cancelRunnables() {
cancelRunnables(runnables);
}
}
class DoChangeContents implements Runnable {
private List addFiles;
private List remFiles;
private boolean doFire = true;
private int fid;
private int addStart = 0;
private int remStart = 0;
private int change;
public DoChangeContents(List addFiles, int addStart, List remFiles, int remStart, int fid) {
this.addFiles = addFiles;
this.addStart = addStart;
this.remFiles = remFiles;
this.remStart = remStart;
this.fid = fid;
}
synchronized void cancel() {
doFire = false;
}
public synchronized void run() {
if (fetchID == fid && doFire) {
int remSize = (remFiles == null) ? 0 : remFiles.size();
int addSize = (addFiles == null) ? 0 : addFiles.size();
synchronized(fileCache) {
if (remSize > 0) {
fileCache.removeAll(remFiles);
}
if (addSize > 0) {
fileCache.addAll(addStart, addFiles);
}
files = null;
directories = null;
}
if (remSize > 0 && addSize == 0) {
fireIntervalRemoved(BasicDirectoryModel.this, remStart, remStart + remSize - 1);
} else if (addSize > 0 && remSize == 0 && fileCache.size() > addSize) {
fireIntervalAdded(BasicDirectoryModel.this, addStart, addStart + addSize - 1);
} else {
fireContentsChanged();
}
}
}
}
}
|