3

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);
    }
}
Roman C
  • 49,761
  • 33
  • 66
  • 176
ajb
  • 31,309
  • 3
  • 58
  • 84
  • no idea without an SSCCE – mKorbel Nov 01 '13 at 18:09
  • i could not reproduce your problem at all – Sage Nov 01 '13 at 18:20
  • Now that I'm trying to create a small example, I can't reproduce the problem either. My larger example uses a subclass of `DefaultStyledDocument` and various other stuff; I'm not sure which of them is causing the problem. It will take some research to figure this out. – ajb Nov 01 '13 at 19:37
  • I got it to work correctly by placing the text pane into a scroll pane first, which would suggest that's in an issue with `BoxLayout`... – MadProgrammer Nov 01 '13 at 21:34
  • @MadProgrammer FYI, I've found another workaround... see answer below. Thought you'd be interested. – ajb Nov 04 '13 at 02:46
  • @ajb You might like to take a look at [this](http://stackoverflow.com/questions/7229226/should-i-avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swi) – MadProgrammer Nov 04 '13 at 03:09
  • @MadProgrammer Thanks for the tip--I'll look into it. Don't know how well it applies, since I'm using `setMinimumSize` (on the `JTextPane`) as a hack to work around an apparent bug, rather than to specify a size, but maybe the `setPreferredSize` on the outer `JPanel` is causing a problem. – ajb Nov 04 '13 at 05:47

2 Answers2

0

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.

  • OK, thanks. Interestingly, after I tried `revalidate()`, I added `repaint()` so that it called `revalidate` followed by `repaint`, but that didn't work. But calling `repaint` without `revalidate` does work. I'm not using `Style`'s so your other suggestion doesn't help, and in any case for my purposes I want the ability to change the font of whatever characters I want to change, not simply for one particular `Style`. – ajb Nov 01 '13 at 19:24
  • If you are not using a style then what changes you made affect only the container and the only way is to get it redraw itself rather than particular component. –  Nov 01 '13 at 19:48
  • I don't think that's true. As I said in my question, the font changes **are** reflected in the view; the issue is whether the lines below it are shifted. I've found out more information and will edit the question. It has nothing to do with `Style`. – ajb Nov 01 '13 at 20:20
  • 2
    `DocumentListener reflects changes to a text document, so in your case is inappropriate.` - changing attributes should be considered a change. I believe that is what the `changedUpdate()` event of the DocumentListener is for. – camickr Nov 01 '13 at 20:59
  • The `Document` being the model and the `JTextPane` being the view, the model will trigger events that the view can respond to. The fact that it works if you use a layout manager other then `BoxLayout` or even add the `JTextPane` to a `JScrollPane` first suggests that it's an issue with the layout manager not the MVC of the document and editor... – MadProgrammer Nov 01 '13 at 21:33
0

I've just found that it also works to add a setMinimumSize call to the JTextPane:

        .......
        panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
            // problem goes away if above line is removed
        panel.add(textPane);
        textPane.setMinimumSize(new Dimension(1000, 500));  // NEW
        // These also work:
        // textPane.setMinimumSize(new Dimension(1, 1));   or
        // textPane.setMinimumSize(new Dimension(0, 0));
        panel.setPreferredSize(new Dimension(1000, 500));
        textPane.setCaretPosition(0);
        .......

This is slightly preferable to the solution of wrapping the JTextPane inside a JScrollPane, because the latter displays some extra lines near the border even when no scrollbars are displayed.

ajb
  • 31,309
  • 3
  • 58
  • 84