Search code examples
javaswingjtextpane

How to redraw JTextPane when one line is changed to a larger font


I have a JTextPane whose model is a DefaultStyledDocument. I'm noticing that if the text is displayed, and I then use setCharacterAttributes to change every character on a line to a much larger font, the font of the characters on that line is changed in the display as I expect, but the lines below it don't move, so that the lines overlap in the display.

Is there a way to force the JTextPane to recompute the text locations and redisplay itself (or a portion of itself)? I tried setting up a DocumentListener with changedUpdate, and changedUpdate is indeed called, but I can't find a way to make it redraw the JTextPane. revalidate() didn't work.

EDIT: The lines do shift by themselves with a smaller test case, so apparently something else I'm doing in the program is interfering, but I haven't figured out what. Anyway, repaint() without revalidate() works if I can't determine what feature is causing a problem and how to get around it.

EDIT 2: The problem occurs when the JTextPane is added to a JPanel and the JPanel is set up with BoxLayout and BoxLayout.X_AXIS. A sample:

public class Demo extends JFrame {
    JPanel panel;
    JTextPane textPane;
    DefaultStyledDocument doc;

    SimpleAttributeSet smallText, bigText;

    public Demo() {
        super("Demo");
        doc = new DefaultStyledDocument ();
        textPane = new JTextPane(doc);
        panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
            // problem goes away if above line is removed
        panel.add(textPane);
        panel.setPreferredSize(new Dimension(1000, 500));
        textPane.setCaretPosition(0);
        textPane.setMargin(new Insets(5,5,5,5));
        getContentPane().add(panel, BorderLayout.CENTER);

        smallText = new SimpleAttributeSet();
        StyleConstants.setFontFamily(smallText, "SansSerif");
        StyleConstants.setFontSize(smallText, 16);

        bigText = new SimpleAttributeSet();
        StyleConstants.setFontFamily(bigText, "Times New Roman");
        StyleConstants.setFontSize(bigText, 32);

        initDocument();
        textPane.setCaretPosition(0);
    }

    protected void initDocument() {
        String initString[] =
                { "This is the first line.",
                  "This is the second line.",
                  "This is the third line." };

        for (int i = 0; i < initString.length; i ++) {
            try {
                doc.insertString(doc.getLength(), initString[i] + "\n",
                        smallText);
            } catch (BadLocationException e) {
            }
        }
    }

    private void createAndShowGUI() throws InterruptedException {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setVisible(true);
    }

    public static void main(String[] args) throws Exception {
        new Demo().runMain();
    }

    private void runMain() throws Exception {
        SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                UIManager.put("swing.boldMetal", Boolean.FALSE);
                try {
                    createAndShowGUI();
                } catch (InterruptedException e) {
                }
            }
        });
        Thread.sleep(2000);
        doc.setCharacterAttributes(24, 24, bigText, false);
    }
}

Solution

  • Call repaint() it will redraw the container. DocumentListener reflects changes to a text document, so in your case is inappropriate. You can use DefaultStyledDocument.getStyle().addChangeListener() to handle changes of the attributes.