📄 filesystemtree.java
字号:
/*
* 02/11/2005
*
* FileSystemTree.java - A JTree containing all files in the local host's
* file system.
* Copyright (C) 2005 Robert Futrell
* email@address.com
* www.website.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU 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.
*/
package org.fife.ui.rtextfilechooser;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ResourceBundle;
import javax.swing.*;
import javax.swing.filechooser.FileSystemView;
import javax.swing.tree.*;
import org.fife.ui.ToolTipTree;
/**
* A tree containing all files in the local host's file system. So that you
* can create components that contain a <code>FileSystemTree</code>, the
* following property change events are fired when the tree is manipulated:
*
* <ul>
* <li><code>WILL_EXPAND_PROPERTY</code> - When the tree is about to
* expand one of its nodes. Upon receiving this property change event,
* you could set the cursor to the system wait cursor, for example (as
* the expansion could take some time; Java seems to have some trouble
* calling <code>File.isDirectory()</code> on network files...).</li>
* <li><code>EXPANDED_PROPERTY</code> - When the tree has completed
* expanding the node. Upon receiving this property change event, you
* could set the cursor back to the default.</li>
* </ul>
*
* @author Robert Futrell
* @version 0.8
*/
public class FileSystemTree extends ToolTipTree {
/**
*
*/
private static final long serialVersionUID = -7841970085397169435L;
public static final String EXPANDED_PROPERTY = "FileSystemTree.treeExpanded";
public static final String WILL_EXPAND_PROPERTY = "FileSystemTree.treeWillExpand";
private static final String DUMMY_FILE_NAME = "dummy";
private static final File DUMMY_FILE = new File(DUMMY_FILE_NAME);
private static final String EMPTY = "";
private DefaultTreeModel treeModel;
private FileSystemTreeNode root;
private HashMap rootNameCache; // Cache of root names.
private FileSystemView fileSystemView;
protected FileChooserIconManager iconManager;
private JPopupMenu popup;
private FileSystemTreeRenderer cellRenderer;
private static final String BUNDLE_NAME =
"org.fife.ui.rtextfilechooser.FileSystemTree";
/*****************************************************************************/
/**
* Constructor.
*/
public FileSystemTree() {
super();
// Initialize variables.
fileSystemView = FileSystemView.getFileSystemView();
iconManager = new FileChooserIconManager();
rootNameCache = new HashMap();
// Add all of our "root" nodes.
root = new FileSystemTreeNode();
for (Iterator i=RootManager.getInstance().iterator(); i.hasNext(); ) {
File aRoot = (File)i.next();
// Hack - We "know" all roots are directories, so why query
// via isDirectory()? This is a nice performance boost.
root.add(createTreeNodeForImpl(aRoot, true));
addCachedRootName(aRoot);
}
// Make it so they can only select one node at a time.
getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
// Set the root. Note that this must come BEFORE we
// set the cell renderer (below), otherwise, each tree node's
// width will initially be incorrect (fixed when the node is
// expanded/collapsed).
treeModel = new DefaultTreeModel(root);
setModel(treeModel);
cellRenderer = new FileSystemTreeRenderer();
setCellRenderer(cellRenderer);
// Make everything look nice.
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
setShowsRootHandles(true);
setRootVisible(false);
}
/*****************************************************************************/
/**
* Adds a file name to the cached names (for roots). We cache root
* directories' names because FileSystemView.getSystemDisplayName() can
* be costly, especially for disk drives on Windows (like A:\).
*/
private void addCachedRootName(File aRoot) {
// Check separator character as a quick "hack" to check for Windows.
// We don't call getName() for drives because for some reason it can
// take a long time to get the name if it has one (such as A:\ and
// C:\).
if (File.separatorChar=='\\') {
String absolutePath = aRoot.getAbsolutePath();
if (absolutePath.length()==3 &&
absolutePath.endsWith(":\\")) {
rootNameCache.put(aRoot, absolutePath);
return;
}
}
rootNameCache.put(aRoot, getName(aRoot));
}
/*****************************************************************************/
/**
* Creates the popup menu for this file system tree. Subclasses can
* override this method if they wish to add more menu items to the
* popup menu.
*
* @return The popup menu for this file system tree.
* @see #displayPopupMenu
*/
protected JPopupMenu createPopupMenu() {
JPopupMenu popup = new JPopupMenu();
ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME);
Action a = new RefreshAction(bundle);
JMenuItem item = new JMenuItem(a);
popup.add(item);
return popup;
}
/*****************************************************************************/
/**
* Creates a tree node for the specified file.
*
* @param file The file for which to create a tree node.
* @return The tree node for the file.
*/
private FileSystemTreeNode createTreeNodeFor(File file) {
return createTreeNodeForImpl(file, file.isDirectory());
}
/*****************************************************************************/
/**
* Creates a tree node for the specified file. This version of the method
* is for when you know ahead of time whether the file is a directory
* such as when the file is a "root"). This greatly speeds things up,
* as isDirectory() is notoriously slow.
*
* @param file The file for which to create a tree node.
* @param directory Whether the specified file is a directory.
* @return The tree node for the file.
*/
private FileSystemTreeNode createTreeNodeForImpl(File file,
boolean directory) {
// The node for the file.
FileSystemTreeNode dmtn = new FileSystemTreeNode(file);
// Make it have a "+/-" icon beside it if this node represents a
// directory containing files.
if (directory) {
// NOTE: We're just putting in a dummy file no matter what
// for performance, as any kind of File querying methods in
// Java degrade performance.
//File[] files = fileSystemView.getFiles(file, false);
//if (files!=null && files.length>0)
dmtn.add(new FileSystemTreeNode(DUMMY_FILE));
}
return dmtn;
}
/*****************************************************************************/
/**
* Displays the popup menu at the specified location.
*
* @param p The location at which to display the popup.
* @see #createPopupMenu
*/
protected synchronized void displayPopupMenu(Point p) {
// Create the popup menu if necessary.
if (popup==null)
popup = createPopupMenu();
// Only have the "Refresh" menu item enabled if a directory
// item is selected.
boolean enableRefreshItem = false;
TreePath path = getSelectionPath();
if (path!=null) {
FileSystemTreeNode node = (FileSystemTreeNode)path.
getLastPathComponent();
if (node!=null) {
File file = node.getFile();
if (file.isDirectory())
enableRefreshItem = true;
}
}
// NOTE: We assume refresh item is first in menu...
popup.getComponent(0).setEnabled(enableRefreshItem);
// And display it!
popup.show(this, p.x, p.y);
}
/*****************************************************************************/
/**
* Does any filtering and sorting of an array of files so that they will
* be displayed properly. For example this method sorts the array so
* that directories are all listed before regular files. Subclasses can
* override this method to do other things, such as only display
* directories.
*
* @param files The array of files to filter and sort.
* @return The filtered and sorted array of files.
*/
protected File[] filterAndSort(File[] files) {
int num = files.length;
ArrayList dirList = new ArrayList();
ArrayList fileList = new ArrayList();
// First, separate the directories from regular files so we can
// sort them individually. This part could be made more compact,
// but it isn't just for a tad more speed.
for (int i=0; i<num; i++) {
if (files[i].isDirectory())
dirList.add(files[i]);
else
fileList.add(files[i]);
}
Collections.sort(fileList);
Collections.sort(dirList);
dirList.addAll(fileList);
File[] fileArray = new File[dirList.size()];
return (File[])dirList.toArray(fileArray);
}
/*****************************************************************************/
/**
* Called when a node has expanded.
*/
public void fireTreeExpanded(TreePath e) {
super.fireTreeExpanded(e);
// We fire a property change at the beginning and end of a node
// expanding so that anyone interested only has to register a
// PropertyChangeLister to know when nodes are expanding (so they can
// display a wait cursor, for example). Otherwise, they'd have to add
// both a TreeExpansionListener and a TreeWillExpandListener. Cheap,
// I know, but oh well.
firePropertyChange(EXPANDED_PROPERTY, null, null);
}
/*****************************************************************************/
/**
* Called when a node is about to be expanded. This method is overridden
* so that the node that is being expanded will be populated with its
* subdirectories, if necessary.
*/
public void fireTreeWillExpand(TreePath e) throws ExpandVetoException {
// We fire a property change at the beginning and end of a node
// expanding so that anyone interested only has to register a
// PropertyChangeLister to know when nodes are expanding (so they can
// display a wait cursor, for example). Otherwise, they'd have to add
// both a TreeExpansionListener and a TreeWillExpandListener. Cheap,
// I know, but oh well.
firePropertyChange(WILL_EXPAND_PROPERTY, null, null);
super.fireTreeWillExpand(e);
FileSystemTreeNode dmtn =
(FileSystemTreeNode)e.getLastPathComponent();
// If the only child is the dummy one, we know we haven't populated
// this node with true children yet.
int childCount = dmtn.getChildCount();
if (childCount==1 && ((FileSystemTreeNode)dmtn.getChildAt(0)).
containsFile(DUMMY_FILE)) {
refreshChildren(dmtn);
}
}
/*****************************************************************************/
/**
* Returns the child of the specified node containing the specified file.
*
* @param node The node whose children you want to search.
* @param file The file for which to search.
* @return The child node representing the specified file, or
* <code>null</code> if none of the children specified the file.
*/
private FileSystemTreeNode getChildRepresentingFile(
FileSystemTreeNode node, Object file) {
if (file==null)
return null;
int childCount = node.getChildCount();
for (int i=0; i<childCount; i++) {
FileSystemTreeNode child =
(FileSystemTreeNode)node.getChildAt(i);
if (file.equals(child.getUserObject()))
return child;
}
return null;
}
/*****************************************************************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -