/* * (C) 2004 - Geotechnical Software Services This code is free * software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your * option) any later version. This code is distributed in the hope * that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. You should have received a copy of the GNU Lesser General * Public License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Some modifications (C) 2004, Chris Smith */ package muvis.view.directories; import java.io.File; import java.text.Collator; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.TreeSet; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.filechooser.FileSystemView; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; /** * A TreeModel implementation for a disk directory structure. Typical * usage: * * <pre> * FileSystemTreeModel model = new FileSystemTreeModel(); * FileSystemTreeRenderer renderer = new FileSystemTreeRenderer(); * JTree tree = new JTree (model); * tree.setCellRenderer(renderer); * tree.setRootVisible(false); * tree.setShowsRootHandles(true); * </pre> * * @author <a href="private.php?do=newpm&u=">Jacob Dreyer </a> * @author <a href="private.php?do=newpm&u=">Chris Smith </a> */ public class FileSystemTreeModel implements TreeModel { /* * Define a better ordering for sorting files. */ private Comparator<File> sortComparator = new Comparator<File>() { @Override public int compare(File a, File b) { Collator collator = Collator.getInstance(); if (a.isDirectory() && b.isFile()) { return -1; } else if (a.isFile() && b.isDirectory()) { return +1; } int result = collator.compare(a.getName(), b.getName()); if (result != 0) { return result; } result = collator.compare( a.getAbsolutePath(), b.getAbsolutePath()); return result; } }; private Collection<TreeModelListener> listeners; private Object falseRoot = new Object(); private File[] roots; private FileSystemView fsv; private boolean hiddenVisible; private HashMap<File, List<File>> sortedChildren; private HashMap<File, Long> lastModifiedTimes; /** * Create a tree model using the specified file system view and * the specified roots. This results in displaying a subset of * the actual filesystem. There need not be any specific * relationship between the roots specified. * * @param fsv The FileSystemView implementation * @param roots Root files */ public FileSystemTreeModel(FileSystemView fsv, File[] roots) { this.fsv = fsv; this.roots = roots; listeners = new ArrayList<TreeModelListener>(); sortedChildren = new HashMap<File, List<File>>(); lastModifiedTimes = new HashMap<File, Long>(); } /** * Create a tree model using the specified file system view. * * @param fsv The FileSystemView implementation */ public FileSystemTreeModel(FileSystemView fsv) { this(fsv, fsv.getRoots()); } /** * Create a tree model using the default file system view for this * platform. */ public FileSystemTreeModel() { this(FileSystemView.getFileSystemView()); } @Override public Object getRoot() { return falseRoot; } @Override public Object getChild(Object parent, int index) { if (parent == falseRoot) { return roots[index]; } else { List children = (List) sortedChildren.get(parent); return children == null ? null : children.get(index); } } @Override public int getChildCount(Object parent) { if (parent == falseRoot) { return roots.length; } else { File file = (File) parent; if (!fsv.isTraversable(file)) { return 0; } File[] children = fsv.getFiles(file, !hiddenVisible); int nChildren = children == null ? 0 : children.length; long lastModified = file.lastModified(); boolean isFirstTime = lastModifiedTimes.get(file) == null; boolean isChanged = false; if (!isFirstTime) { Long modified = (Long) lastModifiedTimes.get(file); long diff = Math.abs(modified.longValue() - lastModified); // MS/Win or Samba HACK. Check this! isChanged = diff > 4000; } // Sort and register children info if (isFirstTime || isChanged) { lastModifiedTimes.put(file, new Long(lastModified)); TreeSet<File> sorted = new TreeSet<File>(sortComparator); for (int i = 0; i < nChildren; i++) { sorted.add(children[i]); } sortedChildren.put(file, new ArrayList<File>(sorted)); } // Notify listeners (visual tree typically) if changes if (isChanged) { TreeModelEvent event = new TreeModelEvent( this, getTreePath(file)); fireTreeStructureChanged(event); } return nChildren; } } private Object[] getTreePath(Object obj) { List<Object> path = new ArrayList<Object>(); while (obj != falseRoot) { path.add(obj); obj = fsv.getParentDirectory((File) obj); } path.add(falseRoot); int nElements = path.size(); Object[] treePath = new Object[nElements]; for (int i = 0; i < nElements; i++) { treePath[i] = path.get(nElements - i - 1); } return treePath; } @Override public boolean isLeaf(Object node) { if (node == falseRoot) { return false; } else { return !fsv.isTraversable((File) node); } } @Override public void valueForPathChanged(TreePath path, Object newValue) { } @Override public int getIndexOfChild(Object parent, Object child) { List children = (List) sortedChildren.get(parent); return children.indexOf(child); } @Override public void addTreeModelListener(TreeModelListener listener) { if (listener != null && !listeners.contains(listener)) { listeners.add(listener); } } @Override public void removeTreeModelListener(TreeModelListener listener) { if (listener != null) { listeners.remove(listener); } } public void fireTreeNodesChanged(TreeModelEvent event) { for (Iterator i = listeners.iterator(); i.hasNext();) { TreeModelListener listener = (TreeModelListener) i.next(); listener.treeNodesChanged(event); } } public void fireTreeNodesInserted(TreeModelEvent event) { for (Iterator i = listeners.iterator(); i.hasNext();) { TreeModelListener listener = (TreeModelListener) i.next(); listener.treeNodesInserted(event); } } public void fireTreeNodesRemoved(TreeModelEvent event) { for (Iterator i = listeners.iterator(); i.hasNext();) { TreeModelListener listener = (TreeModelListener) i.next(); listener.treeNodesRemoved(event); } } public void fireTreeStructureChanged(TreeModelEvent event) { for (Iterator i = listeners.iterator(); i.hasNext();) { TreeModelListener listener = (TreeModelListener) i.next(); listener.treeStructureChanged(event); } } }