Get a component from a JTextPane through javax.swing.text.Element?
element, java, jcomponent, jtextpane, swing
Solution
You can traverse the text pane's `StyledDocument` to find elements that represent components or icons, as shown below.
BranchElement(section) 0,7
BranchElement(paragraph) 0,7
LeafElement(content) 0,4
LeafElement(icon) 4,5
class javax.swing.plaf.IconUIResource
LeafElement(component) 5,6
class javax.swing.JLabel
LeafElement(content) 6,7
SSCCE:
/**
* @see http://stackoverflow.com/a/15669307/230513
* @see http://stackoverflow.com/questions/2883413
*/
public class DocumentParse {
private static final String ELEM = AbstractDocument.ElementNameAttribute;
private static final String ICON = StyleConstants.IconElementName;
private static final String COMP = StyleConstants.ComponentElementName;
public static void main(String args[]) throws Exception {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextPane jtp = new JTextPane();
StyledDocument doc = (StyledDocument) jtp.getDocument();
SimpleAttributeSet normal = new SimpleAttributeSet();
StyleConstants.setFontFamily(normal, "Serif");
StyleConstants.setFontSize(normal, 72);
StyleConstants.setForeground(normal, Color.blue);
doc.insertString(doc.getLength(), "Test", normal);
jtp.setSelectionStart(doc.getLength());
jtp.insertIcon(UIManager.getIcon("OptionPane.warningIcon"));
jtp.setSelectionStart(doc.getLength());
jtp.insertComponent(new JLabel("Label"));
jtp.setSelectionStart(doc.getLength());
ElementIterator iterator = new ElementIterator(doc);
Element element;
while ((element = iterator.next()) != null) {
System.out.println(element);
AttributeSet as = element.getAttributes();
if (as.containsAttribute(ELEM, ICON)) {
System.out.println(StyleConstants.getIcon(as).getClass());
}
if (as.containsAttribute(ELEM, COMP)) {
System.out.println(StyleConstants.getComponent(as).getClass());
}
}
f.add(jtp);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Problem
I am using a `JTextPane` to display characters and symbols, where the latter are represented by custom painted `JComponents`. For example, the text pane might show something like this: The text pane is user editable and it is allowed for the user to add more symbols via a button at any position and as a replacement for selected text. I do this via the `JTextPane.insertComponent()` method. At some point in the application I need to know what is currently being displayed in the text pane, and by that I mean not only the entered text, but also the exact components contained within. I went through extensive troubles with `Positions` and `DocumentListeners` to manage the content of my text pane, but I kept causing more problems than I was solving. That is why I finally decided, that my troubles are probably due to a design fault on my part, so I decided to see, if I can't get to my components through the text pane. Searching through the documentation and the source code of `AbstractDocument` and other related classes, I found the interface `javax.swing.text.Element`. I then let my application output ``` for(int i = 0; i < textPane.getDocument().getLength(); i++) { System.out.println(((StyledDocument) textPane.getDocument()).getCharacterElement(i)); } ``` which gave me: LeafElement(content) 0,4 LeafElement(content) 0,4 LeafElement(content) 0,4 LeafElement(content) 0,4 LeafElement(component) 4,5 LeafElement(content) 5,9 LeafElement(content) 5,9 LeafElement(content) 5,9 LeafElement(content) 5,9 LeafElement(component) 9,10 Seeing that the `LeafElements` that I got do seem to have some kind of information about what is displayed at which position in the `Document`, I figured that it must be possible to get the actual content at that position. After searching for another half hour how to get the content each of the elements represent, I gave up and decided to post my question here, hoping that some of you might know how to accomplish this!? I have seen this question where someone tries to access the components through `textPane.getComponents()`, which returns an array of components with the exact number of components actually contained in the `JTextPane`, but they are all of the type `javax.swing.text.ComponentView$Invalidator`, which is obviously of no use to me. Maybe I just don't see how to properly continue from here, because a cast to the original type of my symbol doesn't work. tl;dr How do I get a `JComponent`, which is inside the text of a `JTextPane`, and its position from the text pane?