editor
. This will update
* the tree accordingly.
*/
public void setEditor(JTextComponent editor) {
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);
}
}
// PropertyChangeListener
/**
* Invoked when a property changes. We are only interested in when the
* Document changes to reset the DocumentListener.
*/
public void propertyChange(PropertyChangeEvent e) {
if (e.getSource() == getEditor() && e.getPropertyName().equals(
"document")) {
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);
}
}
// DocumentListener
/**
* Gives notification that there was an insert into the document. The
* given range bounds the freshly inserted region.
*
* @param e the document event
*/
public void insertUpdate(DocumentEvent e) {
updateTree(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
*/
public void removeUpdate(DocumentEvent e) {
updateTree(e);
}
/**
* Gives notification that an attribute or set of attributes changed.
*
* @param e the document event
*/
public void changedUpdate(DocumentEvent e) {
updateTree(e);
}
// CaretListener
/**
* Messaged when the selection in the editor has changed. Will update
* the selection in the tree.
*/
public void caretUpdate(CaretEvent e) {
if (!updatingSelection) {
int selBegin = Math.min(e.getDot(), e.getMark());
int end = Math.max(e.getDot(), e.getMark());
Listposition
.
*/
protected TreePath getPathForIndex(int position, Object root,
Element rootElement) {
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;
}
/**
* ElementTreeModel is an implementation of TreeModel to handle displaying
* the Elements from a Document. AbstractDocument.AbstractElement is
* the default implementation used by the swing text package to implement
* Element, and it implements TreeNode. This makes it trivial to create
* a DefaultTreeModel rooted at a particular Element from the Document.
* Unfortunately each Document can have more than one root Element.
* Implying that to display all the root elements as a child of another
* root a fake node has be created. This class creates a fake node as
* the root with the children being the root elements of the Document
* (getRootElements).
* This subclasses DefaultTreeModel. The majority of the TreeModel * methods have been subclassed, primarily to special case the root. */ public static class ElementTreeModel extends DefaultTreeModel { protected Element[] rootElements; public ElementTreeModel(Document document) { super(new DefaultMutableTreeNode("root"), false); rootElements = document.getRootElements(); } /** * Returns the child of parent at index index in * the parent's child array. parent must be a node * previously obtained from this data source. This should * not return null if index is a valid index for * parent (that is index >= 0 && index * < getChildCount(parent)). * * @param parent a node in the tree, obtained from this data source * @return the child of parent at index index */ @Override public Object getChild(Object parent, int index) { if (parent == root) { return rootElements[index]; } return super.getChild(parent, index); } /** * Returns the number of children of parent. Returns 0 * if the node is a leaf or if it has no children. * parent must be a node previously obtained from this * data source. * * @param parent a node in the tree, obtained from this data source * @return the number of children of the node parent */ @Override public int getChildCount(Object parent) { if (parent == root) { return rootElements.length; } return super.getChildCount(parent); } /** * Returns true if node is a leaf. It is possible for * this method to return false even if node has no * children. A directory in a filesystem, for example, may * contain no files; the node representing the directory is * not a leaf, but it also has no children. * * @param node a node in the tree, obtained from this data source * @return true if node is a leaf */ @Override public boolean isLeaf(Object node) { if (node == root) { return false; } return super.isLeaf(node); } /** * Returns the index of child in parent. */ @Override public int getIndexOfChild(Object parent, Object child) { if (parent == root) { for (int counter = rootElements.length - 1; counter >= 0; counter--) { if (rootElements[counter] == child) { return counter; } } return -1; } return super.getIndexOfChild(parent, child); } /** * Invoke this method after you've changed how node is to be * represented in the tree. */ @Override public void nodeChanged(TreeNode node) { if (listenerList != null && node != null) { TreeNode parent = node.getParent(); if (parent == null && node != root) { parent = root; } if (parent != null) { int anIndex = getIndexOfChild(parent, node); if (anIndex != -1) { int[] cIndexs = new int[1]; cIndexs[0] = anIndex; nodesChanged(parent, cIndexs); } } } } /** * Returns the path to a particluar node. This is recursive. */ @Override protected TreeNode[] getPathToRoot(TreeNode aNode, int depth) { TreeNode[] retNodes; /* Check for null, in case someone passed in a null node, or they passed in an element that isn't rooted at root. */ if (aNode == null) { if (depth == 0) { return null; } else { retNodes = new TreeNode[depth]; } } else { depth++; if (aNode == root) { retNodes = new TreeNode[depth]; } else { TreeNode parent = aNode.getParent(); if (parent == null) { parent = root; } retNodes = getPathToRoot(parent, depth); } retNodes[retNodes.length - depth] = aNode; } return retNodes; } } }