FileDocCategorySizeDatePackage
ComponentTree.javaAPI DocExample7299Sat Jan 24 10:44:34 GMT 2004je3.gui

ComponentTree.java

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