4

I have a question on how I could easily compute the preferred size/height of a JTextPane given a fixed width. What I have done so far is put my JTextPane in a JScrollPane and whenever I update the text, I update the size of the JScrollPane. Now, this works well (although I find my code a bit twisted, but it works) but when I add a new line, my code that updates the height of the scrollpane needs to be called twice: once immediately and a second time with invokeLater. I am looking for a way to avoid the invokeLater(). Anything will do, including dispatching events on the components, overriding Swing methods, etc...

Here is the snipet that illustrates all this:

import java.awt.AWTEvent;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Toolkit;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class Test {

    private static MyEventQueue queue;

    private static final class MyEventQueue extends EventQueue {

        public boolean log = false;

        @Override
        protected void dispatchEvent(AWTEvent event) {
            if (log) {
                System.err.println(event.getClass().getName() + " " + event.getSource());
            }
            super.dispatchEvent(event);
        }
    }

    public static class WrapApp extends JFrame {
        JTextPane edit = new JTextPane() {
            @Override
            public boolean getScrollableTracksViewportWidth() {
                return true;
            }

        };
        private JScrollPane comp;

        protected void updateVPSize() {
            updateSize(true);
        }

        protected void updateSize(boolean repeat) {
            edit.setSize(150, 1000);
            Dimension size = edit.getPreferredScrollableViewportSize();
            System.err.println("Before " + size);
            size.width = 150;
            comp.setSize(size);
            if (repeat) {
                queue.log = true;
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        queue.log = false;
                        updateSize(false);
                    }
                });
            }
        }

        public WrapApp() {
            super("Forced wrap/no wrap example");
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            getContentPane().setLayout(null);
            edit.getDocument().addDocumentListener(new DocumentListener() {

                @Override
                public void removeUpdate(DocumentEvent e) {
                    updateVPSize();
                }

                @Override
                public void insertUpdate(DocumentEvent e) {
                    updateVPSize();
                }

                @Override
                public void changedUpdate(DocumentEvent e) {
                    updateVPSize();
                }
            });
            comp = new JScrollPane(edit) {
                @Override
                public void setSize(int width, int height) {
                    super.setSize(width, height);
                };
            };
            comp.setBorder(null);
            comp.setLocation(0, 0);
            comp.setViewportBorder(null);
            comp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
            edit.setText("Some long text that needs to be wrapped on several lines.\n\nBut this is not the end of it, it can go on and on and on and on...Some long text that needs to be wrapped on several lines.\n\nBut this is not the end of it, it can go on and on and on and on...");
            getContentPane().add(comp);
            setSize(300, 700);
            setLocationRelativeTo(null);
            updateVPSize();
        }
    }

    public static void main(String[] args) {
        Toolkit.getDefaultToolkit().getSystemEventQueue().push(queue = new MyEventQueue());
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                WrapApp m = new WrapApp();
                m.setVisible(true);

            }
        });
    }

}

Don't really mind all the stuffs left over in there, I tried a bunch of stuffs

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • please why do you want to set for JScrollPane, is there important reason, btw +1 for interesting question, sorry but I can't idea if is about LayoutManager or about contents of JTextComponent – mKorbel Mar 26 '12 at 13:07
  • No, if I can do it without the scrollpane, it is fine for me. But I need the JTextPane to wrap its content and so far this is one ugly but working solution I found. – Guillaume Polet Mar 26 '12 at 13:14

1 Answers1

4

Try this http://java-sl.com/tip_text_height_measuring.html

StanislavL
  • 56,971
  • 9
  • 68
  • 98
  • Yes I already saw that link, but unfortunately (and I have absolutely no idea why) this works with `JEditorPane` but not with `JTextPane` – Guillaume Polet Mar 26 '12 at 13:15
  • Do you set the same kit? Don't you set vertical size? What you mean work with JEditorPane and not with JTextPane? What's effects? – StanislavL Mar 26 '12 at 13:20
  • Well, sorry, my comment was in part incorrect. It also works with JTextPane, it is just that with JEditorPane, both width and height are correct while with JTextPane, only height is correct. If you look at my code, you will see that it is actually inspired by the idea shown in your link. But what I would like is to get the correct height after editing the JTextPane. Somehow, I have to do this is 2 passes and I would like it to be only a one-pass mechanism – Guillaume Polet Mar 26 '12 at 13:42
  • FYI, this doesn't work with a text/rtf JEditorPane, only text/plain. – Sam Barnum May 18 '12 at 14:42