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?
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);
}
}
The original component is the first (and only) child of the javax.swing.text.ComponentView$Invalidator
as you can see from ComponentView.
You can get list of the invalidators and use their children to acccess inserted components.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With