Get a component from a JTextPane through javax.swing.text.Element?

点点圈 提交于 2020-01-19 16:22:50

问题


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?


回答1:


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);
    }
}



回答2:


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.



来源:https://stackoverflow.com/questions/15661508/get-a-component-from-a-jtextpane-through-javax-swing-text-element

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!