/*
* Copyright (c) 2004 David Flanagan. All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book,
* please visit http://www.davidflanagan.com/javaexamples3.
*/
package je3.gui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
/**
* This class is a JTree subclass that displays the tree of AWT or Swing
* component that make up a GUI.
**/
public class ComponentTree extends JTree {
/**
* All this constructor method has to do is set the TreeModel and
* TreeCellRenderer objects for the tree. It is these classes (defined
* below) that do all the real work.
**/
public ComponentTree(Component c) {
super(new ComponentTreeModel(c));
setCellRenderer(new ComponentCellRenderer(getCellRenderer()));
}
/**
* The TreeModel class puts hierarchical data in a form that the JTree
* can display. This implementation interprets the containment hierarchy
* of a Component for display by the ComponentTree class. Note that any
* kind of Object can be a node in the tree, as long as the TreeModel knows
* how to handle it.
**/
static class ComponentTreeModel implements TreeModel {
Component root; // The root object of the tree
// Constructor: just remember the root object
public ComponentTreeModel(Component root) { this.root = root; }
// Return the root of the tree
public Object getRoot() { return root; }
// Is this node a leaf? (Leaf nodes are displayed differently by JTree)
// Any node that isn't a container is a leaf, since they cannot have
// children. We also define containers with no children as leaves.
public boolean isLeaf(Object node) {
if (!(node instanceof Container)) return true;
Container c = (Container) node;
return c.getComponentCount() == 0;
}
// How many children does this node have?
public int getChildCount(Object node) {
if (node instanceof Container) {
Container c = (Container) node;
return c.getComponentCount();
}
return 0;
}
// Return the specified child of a parent node.
public Object getChild(Object parent, int index) {
if (parent instanceof Container) {
Container c = (Container) parent;
return c.getComponent(index);
}
return null;
}
// Return the index of the child node in the parent node
public int getIndexOfChild(Object parent, Object child) {
if (!(parent instanceof Container)) return -1;
Container c = (Container) parent;
Component[] children = c.getComponents();
if (children == null) return -1;
for(int i = 0; i < children.length; i++) {
if (children[i] == child) return i;
}
return -1;
}
// This method is only required for editable trees, so it is not
// implemented here.
public void valueForPathChanged(TreePath path, Object newvalue) {}
// This TreeModel never fires any events (since it is not editable)
// so event listener registration methods are left unimplemented
public void addTreeModelListener(TreeModelListener l) {}
public void removeTreeModelListener(TreeModelListener l) {}
}
/**
* A TreeCellRenderer displays each node of a tree. The default renderer
* displays arbitrary Object nodes by calling their toString() method.
* The Component.toString() method returns long strings with extraneous
* information. Therefore, we use this "wrapper" implementation of
* TreeCellRenderer to convert nodes from Component objects to useful
* String values before passing those String values on to the default
* renderer.
**/
static class ComponentCellRenderer implements TreeCellRenderer {
TreeCellRenderer renderer; // The renderer we are a wrapper for
// Constructor: just remember the renderer
public ComponentCellRenderer(TreeCellRenderer renderer) {
this.renderer = renderer;
}
// This is the only TreeCellRenderer method.
// Compute the string to display, and pass it to the wrapped renderer
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected,
boolean expanded,
boolean leaf, int row,
boolean hasFocus) {
String newvalue = value.getClass().getName(); // Component type
String name = ((Component)value).getName(); // Component name
if (name != null) newvalue += " (" + name + ")"; // unless null
// Use the wrapped renderer object to do the real work
return renderer.getTreeCellRendererComponent(tree, newvalue,
selected, expanded,
leaf, row, hasFocus);
}
}
/**
* This main() method demonstrates the use of the ComponentTree class: it
* puts a ComponentTree component in a Frame, and uses the ComponentTree
* to display its own GUI hierarchy. It also adds a TreeSelectionListener
* to display additional information about each component as it is selected
**/
public static void main(String[] args) {
// Create a frame for the demo, and handle window close requests
JFrame frame = new JFrame("ComponentTree Demo");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }
});
// Create a scroll pane and a "message line" and add them to the
// center and bottom of the frame.
JScrollPane scrollpane = new JScrollPane();
final JLabel msgline = new JLabel(" ");
frame.getContentPane().add(scrollpane, BorderLayout.CENTER);
frame.getContentPane().add(msgline, BorderLayout.SOUTH);
// Now create the ComponentTree object, specifying the frame as the
// component whose tree is to be displayed. Also set the tree's font.
JTree tree = new ComponentTree(frame);
tree.setFont(new Font("SansSerif", Font.BOLD, 12));
// Only allow a single item in the tree to be selected at once
tree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
// Add an event listener for notifications when
// the tree selection state changes.
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
// Tree selections are referred to by "path"
// We only care about the last node in the path
TreePath path = e.getPath();
Component c = (Component) path.getLastPathComponent();
// Now we know what component was selected, so
// display some information about it in the message line
if (c.isShowing()) {
Point p = c.getLocationOnScreen();
msgline.setText("x: " + p.x + " y: " + p.y +
" width: " + c.getWidth() +
" height: " + c.getHeight());
}
else {
msgline.setText("component is not showing");
}
}
});
// Now that we've set up the tree, add it to the scrollpane
scrollpane.setViewportView(tree);
// Finally, set the size of the main window, and pop it up.
frame.setSize(600, 400);
frame.setVisible(true);
}
}
|