FileDocCategorySizeDatePackage
ElementTreePanel.javaAPI DocSun JDK 1.4.2 Example18457Thu May 12 00:35:27 BST 2005None

ElementTreePanel

public class ElementTreePanel extends JPanel implements DocumentListener, CaretListener, PropertyChangeListener, TreeSelectionListener
Displays a tree showing all the elements in a text Document. Selecting a node will result in reseting the selection of the JTextComponent. This also becomes a CaretListener to know when the selection has changed in the text to update the selected item in the tree.
author
Scott Violet
version
1.14 01/23/03

Fields Summary
protected JTree
tree
Tree showing the documents element structure.
protected JTextComponent
editor
Text component showing elemenst for.
protected ElementTreeModel
treeModel
Model for the tree.
protected boolean
updatingSelection
Set to true when updatin the selection.
Constructors Summary
public ElementTreePanel(JTextComponent editor)

	this.editor = editor;

	Document document = editor.getDocument();

	// Create the tree.
	treeModel = new ElementTreeModel(document);
	tree = new JTree(treeModel) {
	    public String convertValueToText(Object value, boolean selected,
					     boolean expanded, boolean leaf,
					     int row, boolean hasFocus) {
		// Should only happen for the root
		if(!(value instanceof Element))
		    return value.toString();

		Element        e = (Element)value;
		AttributeSet   as = e.getAttributes().copyAttributes();
		String         asString;

		if(as != null) {
		    StringBuffer       retBuffer = new StringBuffer("[");
		    Enumeration        names = as.getAttributeNames();

		    while(names.hasMoreElements()) {
			Object        nextName = names.nextElement();

			if(nextName != StyleConstants.ResolveAttribute) {
			    retBuffer.append(" ");
			    retBuffer.append(nextName);
			    retBuffer.append("=");
			    retBuffer.append(as.getAttribute(nextName));
			}
		    }
		    retBuffer.append(" ]");
		    asString = retBuffer.toString();
		}
		else
		    asString = "[ ]";

		if(e.isLeaf())
		    return e.getName() + " [" + e.getStartOffset() +
			", " + e.getEndOffset() +"] Attributes: " + asString;
		return e.getName() + " [" + e.getStartOffset() +
		    ", " + e.getEndOffset() + "] Attributes: " +
 		        asString;
	    }
	};
	tree.addTreeSelectionListener(this);
	tree.setDragEnabled(true);
	// Don't show the root, it is fake.
	tree.setRootVisible(false);
	// Since the display value of every node after the insertion point
	// changes every time the text changes and we don't generate a change
	// event for all those nodes the display value can become off.
	// This can be seen as '...' instead of the complete string value.
	// This is a temporary workaround, increase the needed size by 15,
	// hoping that will be enough.
	tree.setCellRenderer(new DefaultTreeCellRenderer() {
	    public Dimension getPreferredSize() {
		Dimension retValue = super.getPreferredSize();
		if(retValue != null)
		    retValue.width += 15;
		return retValue;
	    }
	});
	// become a listener on the document to update the tree.
	document.addDocumentListener(this);

	// become a PropertyChangeListener to know when the Document has
	// changed.
	editor.addPropertyChangeListener(this);

	// Become a CaretListener
	editor.addCaretListener(this);

	// configure the panel and frame containing it.
	setLayout(new BorderLayout());
	add(new JScrollPane(tree), BorderLayout.CENTER);

	// Add a label above tree to describe what is being shown
	JLabel     label = new JLabel("Elements that make up the current document", SwingConstants.CENTER);

	label.setFont(new Font("Dialog", Font.BOLD, 14));
	add(label, BorderLayout.NORTH);

	setPreferredSize(new Dimension(400, 400));
    
Methods Summary
public voidcaretUpdate(javax.swing.event.CaretEvent e)
Messaged when the selection in the editor has changed. Will update the selection in the tree.

	if(!updatingSelection) {
	    JTextComponent     editor = getEditor();
	    int                selBegin = Math.min(e.getDot(), e.getMark());
	    int                end = Math.max(e.getDot(), e.getMark());
	    Vector             paths = new Vector();
	    TreeModel          model = getTreeModel();
	    Object             root = model.getRoot();
	    int                rootCount = model.getChildCount(root);

	    // Build an array of all the paths to all the character elements
	    // in the selection.
	    for(int counter = 0; counter < rootCount; counter++) {
		int            start = selBegin;

		while(start <= end) {
		    TreePath    path = getPathForIndex(start, root,
				       (Element)model.getChild(root, counter));
		    Element     charElement = (Element)path.
			                       getLastPathComponent();

		    paths.addElement(path);
		    if(start >= charElement.getEndOffset())
			start++;
		    else
			start = charElement.getEndOffset();
		}
	    }

	    // If a path was found, select it (them).
	    int               numPaths = paths.size();

	    if(numPaths > 0) {
		TreePath[]    pathArray = new TreePath[numPaths];

		paths.copyInto(pathArray);
		updatingSelection = true;
		try {
		    getTree().setSelectionPaths(pathArray);
		    getTree().scrollPathToVisible(pathArray[0]);
		}
		finally {
		    updatingSelection = false;
		}
	    }
	}
    
public voidchangedUpdate(javax.swing.event.DocumentEvent e)
Gives notification that an attribute or set of attributes changed.

param
e the document event

	updateTree(e);
    
protected javax.swing.text.JTextComponentgetEditor()

return
JTextComponent showing elements for.

	return editor;
    
protected javax.swing.tree.TreePathgetPathForIndex(int position, java.lang.Object root, javax.swing.text.Element rootElement)
Returns a TreePath to the element at position.

	TreePath         path = new TreePath(root);
	Element          child = rootElement.getElement
	                            (rootElement.getElementIndex(position));

	path = path.pathByAddingChild(rootElement);
	path = path.pathByAddingChild(child);
	while(!child.isLeaf()) {
	    child = child.getElement(child.getElementIndex(position));
	    path = path.pathByAddingChild(child);
	}
	return path;
    
protected javax.swing.JTreegetTree()

return
tree showing elements.

	return tree;
    
public javax.swing.tree.DefaultTreeModelgetTreeModel()

return
TreeModel implementation used to represent the elements.

	return treeModel;
    
public voidinsertUpdate(javax.swing.event.DocumentEvent e)
Gives notification that there was an insert into the document. The given range bounds the freshly inserted region.

param
e the document event

	updateTree(e);
    
public voidpropertyChange(java.beans.PropertyChangeEvent e)
Invoked when a property changes. We are only interested in when the Document changes to reset the DocumentListener.

	if (e.getSource() == getEditor() &&
	    e.getPropertyName().equals("document")) {
	    JTextComponent      editor = getEditor();
	    Document            oldDoc = (Document)e.getOldValue();
	    Document            newDoc = (Document)e.getNewValue();

	    // Reset the DocumentListener
	    oldDoc.removeDocumentListener(this);
	    newDoc.addDocumentListener(this);

	    // Recreate the TreeModel.
	    treeModel = new ElementTreeModel(newDoc);
	    tree.setModel(treeModel);
	}
    
public voidremoveUpdate(javax.swing.event.DocumentEvent e)
Gives notification that a portion of the document has been removed. The range is given in terms of what the view last saw (that is, before updating sticky positions).

param
e the document event

	updateTree(e);
    
public voidsetEditor(javax.swing.text.JTextComponent editor)
Resets the JTextComponent to editor. This will update the tree accordingly.

	if (this.editor == editor) {
	    return;
	}

	if (this.editor != null) {
	    Document      oldDoc = this.editor.getDocument();

	    oldDoc.removeDocumentListener(this);
	    this.editor.removePropertyChangeListener(this);
	    this.editor.removeCaretListener(this);
	}
	this.editor = editor;
	if (editor == null) {
	    treeModel = null;
	    tree.setModel(null);
	}
	else {
	    Document   newDoc = editor.getDocument();

	    newDoc.addDocumentListener(this);
	    editor.addPropertyChangeListener(this);
	    editor.addCaretListener(this);
	    treeModel = new ElementTreeModel(newDoc);
	    tree.setModel(treeModel);
	}
    
protected voidupdateTree(javax.swing.event.DocumentEvent event)
Updates the tree based on the event type. This will invoke either updateTree with the root element, or handleChange.

	updatingSelection = true;
	try {
	    TreeModel        model = getTreeModel();
	    Object           root = model.getRoot();

	    for(int counter = model.getChildCount(root) - 1; counter >= 0;
		counter--) {
		updateTree(event, (Element)model.getChild(root, counter));
	    }
	}
	finally {
	    updatingSelection = false;
	}
    
protected voidupdateTree(javax.swing.event.DocumentEvent event, javax.swing.text.Element element)
Creates TreeModelEvents based on the DocumentEvent and messages the treemodel. This recursively invokes this method with children elements.

param
event indicates what elements in the tree hierarchy have changed.
param
element Current element to check for changes against.

        DocumentEvent.ElementChange ec = event.getChange(element);

        if (ec != null) {
	    Element[]       removed = ec.getChildrenRemoved();
	    Element[]       added = ec.getChildrenAdded();
	    int             startIndex = ec.getIndex();

	    // Check for removed.
	    if(removed != null && removed.length > 0) {
		int[]            indices = new int[removed.length];

		for(int counter = 0; counter < removed.length; counter++) {
		    indices[counter] = startIndex + counter;
		}
		getTreeModel().nodesWereRemoved((TreeNode)element, indices,
						removed);
	    }
	    // check for added
	    if(added != null && added.length > 0) {
		int[]            indices = new int[added.length];

		for(int counter = 0; counter < added.length; counter++) {
		    indices[counter] = startIndex + counter;
		}
		getTreeModel().nodesWereInserted((TreeNode)element, indices);
	    }
        }
	if(!element.isLeaf()) {
	    int        startIndex = element.getElementIndex
		                       (event.getOffset());
	    int        elementCount = element.getElementCount();
	    int        endIndex = Math.min(elementCount - 1,
					   element.getElementIndex
				     (event.getOffset() + event.getLength()));

	    if(startIndex > 0 && startIndex < elementCount &&
	       element.getElement(startIndex).getStartOffset() ==
	       event.getOffset()) {
		// Force checking the previous element.
		startIndex--;
	    }
	    if(startIndex != -1 && endIndex != -1) {
		for(int counter = startIndex; counter <= endIndex; counter++) {
		    updateTree(event, element.getElement(counter));
		}
	    }
	}
	else {
	    // Element is a leaf, assume it changed
	    getTreeModel().nodeChanged((TreeNode)element);
	}
    
public voidvalueChanged(javax.swing.event.TreeSelectionEvent e)
Called whenever the value of the selection changes.

param
e the event that characterizes the change.

	JTree       tree = getTree();

	if(!updatingSelection && tree.getSelectionCount() == 1) {
	    TreePath      selPath = tree.getSelectionPath();
	    Object        lastPathComponent = selPath.getLastPathComponent();

	    if(!(lastPathComponent instanceof DefaultMutableTreeNode)) {
		Element       selElement = (Element)lastPathComponent;

		updatingSelection = true;
		try {
		    getEditor().select(selElement.getStartOffset(),
				       selElement.getEndOffset());
		}
		finally {
		    updatingSelection = false;
		}
	    }
	}