All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.muntjak.tinylookandfeel.TinyDirectoryModel Maven / Gradle / Ivy

Go to download

This is the Tiny look-and-feel packaged to be distributed with the SQuirreLSQL client. The Tiny look-and-feel is a (mostly painted) look-and-feel for Java 1.4 and higher.

The newest version!
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *	This file is part of the Tiny Look and Feel                                *
 *  Copyright 2003 - 2008  Hans Bickel                                         *
 *                                                                             *
 *  For licensing information and credits, please refer to the                 *
 *  comment in file de.muntjak.tinylookandfeel.TinyLookAndFeel                 *
 *                                                                             *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

package de.muntjak.tinylookandfeel;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.util.List;
import java.util.Vector;

import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataEvent;
import javax.swing.filechooser.FileSystemView;
import javax.swing.plaf.basic.BasicDirectoryModel;
import javax.swing.plaf.basic.BasicFileChooserUI;

import sun.awt.shell.ShellFolder;

/**
 * A copy of BasicDirectoryModel with access to the fileCache.
 * @author Hans Bickel
 *
 */
public class TinyDirectoryModel extends BasicDirectoryModel {

	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;

	private PropertyChangeSupport changeSupport;

	private boolean busy = false;

	public TinyDirectoryModel(JFileChooser fc) {
		super(fc);
		
		this.filechooser = fc;
		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();
		}
		else if("UI".equals(prop)) {
			Object old = e.getOldValue();
			if(old instanceof BasicFileChooserUI) {
				BasicFileChooserUI ui = (BasicFileChooserUI)old;
				BasicDirectoryModel model = ui.getModel();
				if(model != null) {
					model.invalidateFileCache();
				}
			}
		}
		else if("JFileChooserDialogIsClosingProperty".equals(prop)) {
			invalidateFileCache();
		}
	}

	/**
	 * This method is used to interrupt file loading thread.
	 */
	public void invalidateFileCache() {
		if(loadThread != null) {
			loadThread.interrupt();
			loadThread.cancelRunnables();
			loadThread = null;
		}
	}

	public Vector getDirectories() {
		synchronized (fileCache) {
			if(directories != null) {
				return directories;
			}
			Vector fls = getFiles();
			return directories;
		}
	}

	public Vector 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() {
		// Note: The super constructor will call this method
		// but at this time filechooser is null
		if(filechooser == null) return;
		
		File currentDirectory = filechooser.getCurrentDirectory();
		if(currentDirectory == null) {
			return;
		}
		if(loadThread != null) {
			loadThread.interrupt();
			loadThread.cancelRunnables();
		}

		setBusy(true, ++fetchID);

		loadThread = new LoadFilesThread(currentDirectory, fetchID);
		loadThread.start();
	}

	/**
	 * Renames a file in the underlying file system.
	 *
	 * @param oldFile a File object representing
	 *        the existing file
	 * @param newFile a File object representing
	 *        the desired new file name
	 * @return true if rename succeeded,
	 *        otherwise false
	 * @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("TinyDirectoryModel: 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);
	}
	
	public Vector getFileCache() {
		return fileCache;
	}

	/**
	 * Obsolete - not used.
	 */
	public void intervalAdded(ListDataEvent e) {
	}

	/**
	 * Obsolete - not used.
	 */
	public void intervalRemoved(ListDataEvent e) {
	}

	protected void sort(Vector 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() {
			run0();
			setBusy(false, fid);
		}

		public void run0() {
			FileSystemView fileSystem = filechooser.getFileSystemView();

			File[] list = fileSystem.getFiles(currentDirectory, filechooser
				.isFileHidingEnabled());

			Vector acceptsList = new Vector();

			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);
		}
	}

	/**
	 * Adds a PropertyChangeListener to the listener list. The listener is
	 * registered for all bound properties of this class.
	 * 

* If listener is null, * no exception is thrown and no action is performed. * * @param listener the property change listener to be added * * @see #removePropertyChangeListener * @see #getPropertyChangeListeners * * @since 1.6 */ public void addPropertyChangeListener(PropertyChangeListener listener) { if(changeSupport == null) { changeSupport = new PropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(listener); } /** * Removes a PropertyChangeListener from the listener list. *

* If listener is null, no exception is thrown and no action is performed. * * @param listener the PropertyChangeListener to be removed * * @see #addPropertyChangeListener * @see #getPropertyChangeListeners * * @since 1.6 */ public void removePropertyChangeListener(PropertyChangeListener listener) { if(changeSupport != null) { changeSupport.removePropertyChangeListener(listener); } } /** * Returns an array of all the property change listeners * registered on this component. * * @return all of this component's PropertyChangeListeners * or an empty array if no property change * listeners are currently registered * * @see #addPropertyChangeListener * @see #removePropertyChangeListener * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners * * @since 1.6 */ public PropertyChangeListener[] getPropertyChangeListeners() { if(changeSupport == null) { return new PropertyChangeListener[0]; } return changeSupport.getPropertyChangeListeners(); } /** * Support for reporting bound property changes for boolean properties. * This method can be called when a bound property has changed and it will * send the appropriate PropertyChangeEvent to any registered * PropertyChangeListeners. * * @param propertyName the property whose value has changed * @param oldValue the property's previous value * @param newValue the property's new value * * @since 1.6 */ protected void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { if(changeSupport != null) { changeSupport.firePropertyChange(propertyName, oldValue, newValue); } } /** * Set the busy state for the model. The model is considered * busy when it is running a separate (interruptable) * thread in order to load the contents of a directory. */ private synchronized void setBusy(final boolean busy, int fid) { if(fid == fetchID) { boolean oldValue = this.busy; this.busy = busy; if(changeSupport != null && busy != oldValue) { SwingUtilities.invokeLater(new Runnable() { public void run() { firePropertyChange("busy", !busy, busy); } }); } } } 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(TinyDirectoryModel.this, remStart, remStart + remSize - 1); } else if(addSize > 0 && remSize == 0 && fileCache.size() > addSize) { fireIntervalAdded(TinyDirectoryModel.this, addStart, addStart + addSize - 1); } else { fireContentsChanged(); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy