ElementTreePanelpublic class ElementTreePanel extends JPanel implements DocumentListener, CaretListener, PropertyChangeListener, TreeSelectionListenerDisplays 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. |
Fields Summary |
---|
protected JTree | treeTree showing the documents element structure. | protected JTextComponent | editorText component showing elemenst for. | protected ElementTreeModel | treeModelModel for the tree. | protected boolean | updatingSelectionSet 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 void | caretUpdate(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 void | changedUpdate(javax.swing.event.DocumentEvent e)Gives notification that an attribute or set of attributes changed.
updateTree(e);
| protected javax.swing.text.JTextComponent | getEditor()
return editor;
| protected javax.swing.tree.TreePath | getPathForIndex(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.JTree | getTree()
return tree;
| public javax.swing.tree.DefaultTreeModel | getTreeModel()
return treeModel;
| public void | insertUpdate(javax.swing.event.DocumentEvent e)Gives notification that there was an insert into the document. The
given range bounds the freshly inserted region.
updateTree(e);
| public void | propertyChange(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 void | removeUpdate(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).
updateTree(e);
| public void | setEditor(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 void | updateTree(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 void | updateTree(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.
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 void | valueChanged(javax.swing.event.TreeSelectionEvent e)Called whenever the value of the selection changes.
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;
}
}
}
|
|