import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.EventObject;
public class Test extends JFrame {
public Test() {
JTree tree = new JTree(createTreeModel());
JScrollPane scrollPane = new JScrollPane(tree);
FileNodeRenderer renderer = new FileNodeRenderer();
FileNodeEditor editor = new FileNodeEditor();
tree.setEditable(true);
tree.setCellRenderer(renderer);
tree.setCellEditor(new ImmediateEditor(tree,
renderer, editor));
getContentPane().add(scrollPane, BorderLayout.CENTER);
tree.addTreeExpansionListener(new TreeExpansionListener(){
public void treeCollapsed(TreeExpansionEvent e) {
}
public void treeExpanded(TreeExpansionEvent e) {
TreePath path = e.getPath();
FileNode node = (FileNode)
path.getLastPathComponent();
if( ! node.isExplored())
node.explore();
}
});
tree.getCellEditor().addCellEditorListener(
new CellEditorListener() {
public void editingCanceled(ChangeEvent e) {
CellEditor cellEditor = (CellEditor)e.getSource();
SelectableFile sf =
(SelectableFile)
cellEditor.getCellEditorValue();
System.out.println("editing canceled: " +
sf.toString());
}
public void editingStopped(ChangeEvent e) {
CellEditor cellEditor = (CellEditor)e.getSource();
SelectableFile sf =
(SelectableFile)
cellEditor.getCellEditorValue();
System.out.println("editing stopped: " +
sf.toString());
}
});
}
private DefaultTreeModel createTreeModel() {
File root = new File("E:/");
FileNode rootNode = new FileNode(root), node;
rootNode.explore();
return new DefaultTreeModel(rootNode);
}
public static void main(String args[]) {
GJApp.launch(new Test(),"JTree File Explorer",
300,300,450,400);
}
}
class SelectableFile {
private File file;
private boolean selected = false;
public SelectableFile(File file) {
this.file = file;
}
public String toString() {
return file.toString() + " selected: " + selected;
}
public void setSelected(boolean s) { selected = s; }
public boolean isSelected() { return selected; }
public File getFile() { return file; }
}
class FileNode extends DefaultMutableTreeNode {
private boolean explored = false;
public FileNode(File file) {
setUserObject(new SelectableFile(file));
}
public boolean getAllowsChildren() { return isDirectory(); }
public boolean isLeaf() { return !isDirectory(); }
public File getFile() {
SelectableFile sf = (SelectableFile)getUserObject();
return sf.getFile();
}
public boolean isSelected() {
SelectableFile sf = (SelectableFile)getUserObject();
return sf.isSelected();
}
public void setSelected(boolean b) {
SelectableFile sf = (SelectableFile)getUserObject();
sf.setSelected(b);
}
public boolean isDirectory() {
File file = getFile();
return file.isDirectory();
}
public String toString() {
File file = getFile();
String filename = file.toString();
int index = filename.lastIndexOf("\\");
return (index != -1 && index != filename.length()-1) ?
filename.substring(index+1) :
filename;
}
public void explore() { explore(false); }
public boolean isExplored() { return explored; }
public void explore(boolean force) {
if(!isExplored() || force) {
File file = getFile();
File[] children = file.listFiles();
for(int i=0; i < children.length; ++i)
add(new FileNode(children[i]));
explored = true;
}
}
}
class FileNodeRenderer extends DefaultTreeCellRenderer {
protected JCheckBox checkBox = new JCheckBox("backup");
private Component strut = Box.createHorizontalStrut(5);
private JPanel panel = new JPanel();
public FileNodeRenderer() {
panel.setBackground(
UIManager.getColor("Tree.textBackground"));
setOpaque(false);
checkBox.setOpaque(false);
panel.setOpaque(false);
panel.setLayout(new FlowLayout(FlowLayout.CENTER,0,0));
panel.add(this);
panel.add(strut);
panel.add(checkBox);
}
public Component getTreeCellRendererComponent(
JTree tree, Object value,
boolean selected, boolean expanded,
boolean leaf, int row,
boolean hasFocus) {
FileNode node = (FileNode)value;
super.getTreeCellRendererComponent(
tree, value, selected, expanded,
leaf, row, hasFocus);
checkBox.setVisible(node.isDirectory());
checkBox.setSelected(node.isSelected());
return panel;
}
public Dimension getCheckBoxOffset() {
Graphics g = panel.getGraphics();
int xoffset = 0;
if(g != null) {
try {
FontMetrics fm = g.getFontMetrics();
xoffset = fm.stringWidth(getText()) +
strut.getPreferredSize().width;
}
finally {
g.dispose();
}
}
return new Dimension(xoffset, 0);
}
}
class FileNodeEditorRenderer extends FileNodeRenderer {
public Component getTreeCellRendererComponent(
JTree tree, Object value,
boolean selected, boolean expanded,
boolean leaf, int row,
boolean hasFocus) {
Component c = super.getTreeCellRendererComponent(tree,
value, selected, expanded,
leaf, row, hasFocus);
setIcon(null);
return c;
}
public JCheckBox getCheckBox() {
return checkBox;
}
}
class FileNodeEditor extends AbstractCellEditor {
FileNodeEditorRenderer renderer;
FileNode lastEditedNode;
JCheckBox checkBox;
public FileNodeEditor() {
renderer = new FileNodeEditorRenderer();
checkBox = renderer.getCheckBox();
checkBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
lastEditedNode.setSelected(checkBox.isSelected());
stopCellEditing();
}
});
}
public Component getTreeCellEditorComponent(
JTree tree, Object value,
boolean selected, boolean expanded,
boolean leaf, int row) {
lastEditedNode = (FileNode)value;
return renderer.getTreeCellRendererComponent(tree,
value, selected, expanded,
leaf, row, true); // hasFocus ignored
}
public Object getCellEditorValue() {
return lastEditedNode.getUserObject();
}
}
class ImmediateEditor extends DefaultTreeCellEditor {
private FileNodeRenderer renderer;
public ImmediateEditor(JTree tree,
FileNodeRenderer renderer,
FileNodeEditor editor) {
super(tree, renderer, editor);
this.renderer = renderer;
}
protected boolean canEditImmediately(EventObject e) {
boolean rv = false; // rv = return value
if(e instanceof MouseEvent) {
MouseEvent me = (MouseEvent)e;
rv = inCheckBoxHitRegion(me);
}
return rv;
}
public boolean shouldSelectCell(EventObject e) {
boolean rv = false; // only mouse events
if(e instanceof MouseEvent) {
MouseEvent me = (MouseEvent)e;
TreePath path = tree.getPathForLocation(me.getX(),
me.getY());
FileNode node = (FileNode)
path.getLastPathComponent();
rv = node.isLeaf() || !inCheckBoxHitRegion(me);
}
return rv;
}
public boolean inCheckBoxHitRegion(MouseEvent e) {
TreePath path = tree.getPathForLocation(e.getX(),
e.getY());
FileNode node = (FileNode)path.getLastPathComponent();
boolean rv = false;
if(node.isDirectory()) {
// offset and lastRow DefaultTreeCellEditor
// protected members
Rectangle bounds = tree.getRowBounds(lastRow);
Dimension checkBoxOffset =
renderer.getCheckBoxOffset();
bounds.translate(offset + checkBoxOffset.width,
checkBoxOffset.height);
rv = bounds.contains(e.getPoint());
}
return rv;
}
}
class GJApp extends WindowAdapter {
static private JPanel statusArea = new JPanel();
static private JLabel status = new JLabel(" ");
public static void launch(final JFrame f, String title,
final int x, final int y,
final int w, int h) {
f.setTitle(title);
f.setBounds(x,y,w,h);
f.setVisible(true);
statusArea.setBorder(BorderFactory.createEtchedBorder());
statusArea.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
statusArea.add(status);
status.setHorizontalAlignment(JLabel.LEFT);
f.setDefaultCloseOperation(
WindowConstants.DISPOSE_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
System.exit(0);
}
});
}
static public JPanel getStatusArea() {
return statusArea;
}
static public void updateStatus(String s) {
status.setText(s);
}
}