This program demonstrates cell rendering and listening to tree selection events.
/*
This program is a part of the companion code for Core Java 8th ed.
(http://horstmann.com/corejava)
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 3 of the License, or
(at your option) 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, see <http://www.gnu.org/licenses/>.
*/
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Enumeration;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
/**
* This program demonstrates cell rendering and listening to tree selection events.
* @version 1.03 2007-08-01
* @author Cay Horstmann
*/
public class ClassTree
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new ClassTreeFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* This frame displays the class tree, a text field and add button to add more classes into the
* tree.
*/
class ClassTreeFrame extends JFrame
{
public ClassTreeFrame()
{
setTitle("ClassTree");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// the root of the class tree is Object
root = new DefaultMutableTreeNode(java.lang.Object.class);
model = new DefaultTreeModel(root);
tree = new JTree(model);
// add this class to populate the tree with some data
addClass(getClass());
// set up node icons
ClassNameTreeCellRenderer renderer = new ClassNameTreeCellRenderer();
renderer.setClosedIcon(new ImageIcon("red-ball.gif"));
renderer.setOpenIcon(new ImageIcon("yellow-ball.gif"));
renderer.setLeafIcon(new ImageIcon("blue-ball.gif"));
tree.setCellRenderer(renderer);
// set up selection mode
tree.addTreeSelectionListener(new TreeSelectionListener()
{
public void valueChanged(TreeSelectionEvent event)
{
// the user selected a different node--update description
TreePath path = tree.getSelectionPath();
if (path == null) return;
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) path
.getLastPathComponent();
Class<?> c = (Class<?>) selectedNode.getUserObject();
String description = getFieldDescription(c);
textArea.setText(description);
}
});
int mode = TreeSelectionModel.SINGLE_TREE_SELECTION;
tree.getSelectionModel().setSelectionMode(mode);
// this text area holds the class description
textArea = new JTextArea();
// add tree and text area
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(1, 2));
panel.add(new JScrollPane(tree));
panel.add(new JScrollPane(textArea));
add(panel, BorderLayout.CENTER);
addTextField();
}
/**
* Add the text field and "Add" button to add a new class.
*/
public void addTextField()
{
JPanel panel = new JPanel();
ActionListener addListener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// add the class whose name is in the text field
try
{
String text = textField.getText();
addClass(Class.forName(text)); // clear text field to indicate success
textField.setText("");
}
catch (ClassNotFoundException e)
{
JOptionPane.showMessageDialog(null, "Class not found");
}
}
};
// new class names are typed into this text field
textField = new JTextField(20);
textField.addActionListener(addListener);
panel.add(textField);
JButton addButton = new JButton("Add");
addButton.addActionListener(addListener);
panel.add(addButton);
add(panel, BorderLayout.SOUTH);
}
/**
* Finds an object in the tree.
* @param obj the object to find
* @return the node containing the object or null if the object is not present in the tree
*/
@SuppressWarnings("unchecked")
public DefaultMutableTreeNode findUserObject(Object obj)
{
// find the node containing a user object
Enumeration<TreeNode> e = (Enumeration<TreeNode>) root.breadthFirstEnumeration();
while (e.hasMoreElements())
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
if (node.getUserObject().equals(obj)) return node;
}
return null;
}
/**
* Adds a new class and any parent classes that aren't yet part of the tree
* @param c the class to add
* @return the newly added node.
*/
public DefaultMutableTreeNode addClass(Class<?> c)
{
// add a new class to the tree
// skip non-class types
if (c.isInterface() || c.isPrimitive()) return null;
// if the class is already in the tree, return its node
DefaultMutableTreeNode node = findUserObject(c);
if (node != null) return node;
// class isn't present--first add class parent recursively
Class<?> s = c.getSuperclass();
DefaultMutableTreeNode parent;
if (s == null) parent = root;
else parent = addClass(s);
// add the class as a child to the parent
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(c);
model.insertNodeInto(newNode, parent, parent.getChildCount());
// make node visible
TreePath path = new TreePath(model.getPathToRoot(newNode));
tree.makeVisible(path);
return newNode;
}
/**
* Returns a description of the fields of a class.
* @param the class to be described
* @return a string containing all field types and names
*/
public static String getFieldDescription(Class<?> c)
{
// use reflection to find types and names of fields
StringBuilder r = new StringBuilder();
Field[] fields = c.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
Field f = fields[i];
if ((f.getModifiers() & Modifier.STATIC) != 0) r.append("static ");
r.append(f.getType().getName());
r.append(" ");
r.append(f.getName());
r.append("\n");
}
return r.toString();
}
private DefaultMutableTreeNode root;
private DefaultTreeModel model;
private JTree tree;
private JTextField textField;
private JTextArea textArea;
private static final int DEFAULT_WIDTH = 400;
private static final int DEFAULT_HEIGHT = 300;
}
/**
* This class renders a class name either in plain or italic. Abstract classes are italic.
*/
class ClassNameTreeCellRenderer extends DefaultTreeCellRenderer
{
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
boolean expanded, boolean leaf, int row, boolean hasFocus)
{
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
// get the user object
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
Class<?> c = (Class<?>) node.getUserObject();
// the first time, derive italic font from plain font
if (plainFont == null)
{
plainFont = getFont();
// the tree cell renderer is sometimes called with a label that has a null font
if (plainFont != null) italicFont = plainFont.deriveFont(Font.ITALIC);
}
// set font to italic if the class is abstract, plain otherwise
if ((c.getModifiers() & Modifier.ABSTRACT) == 0) setFont(plainFont);
else setFont(italicFont);
return this;
}
private Font plainFont = null;
private Font italicFont = null;
}
Related examples in the same category