tags:

views:

1538

answers:

4

What is the general approach with Java swing to update a textarea with lines of text (say from a Thread) and then have the text caret flow to the bottom of the textarea as text is being added. Also update the scrollbar so that it is at the bottom.

I was thinking that I would have a stringbuffer and append text to that and then set the string in the textarea and position the scrollbar at the bottom.

+7  A: 

The append() method doesn't do what you want?

And although you didn't ask: when you're generating something in a background thread, be sure to use SwingUtilities.invokeLater() to update your components.

kdgregory
+6  A: 

Use append() to add the text, then setCaretPosition() to make sure you scroll with it.

myTextPane.append(textFromSomewhere);
myTextPane.setCaretPosition(myTextPane.getDocument().length());
Michael Myers
+1 for remembering to move the cursor
kdgregory
Copying the entire text area to a String just to get the length? This is not going to be thread-safe.
Tom Hawtin - tackline
Yeah, standard Swing threading procedures apply. (I didn't realize that getText() involved copying things; I'll change that to getDocument().)
Michael Myers
@Tom - as long as you do it on the event thread, should be no problem (and although append() is thread-safe, I'd still run on EDT as good practice). Not necessarily the most efficient, going to the document is better in that regard. I'm surprised that there's not a setting somewhere for "cursor follows appended text".
kdgregory
I found this helpful, though I needed .getLength()
Brian
+1  A: 

If you are updating from a Thread, dont forget to use SwingWorker or some other AWT Thread-safe approach.

akf
+2  A: 

From another thread, you should use java.awt.EventQueue.invokeLater to get on the EDT and then everything works.

So:

java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
    Document doc = text.getDocument();
    int origLen = doc.getLength()
    try {
        doc.insertString(origLen, msg, null);
    } catch (BadLocationException exc) {
        // Odd APIs forces us to deal with this nonsense.
        IndexOutOfBoundsException wrapExc = new IndexOutOfBoundsException();
        wrapExc.initCause(exc);
        throw wrapExc;
    }
    // IIRC, Position is a bit odd and 
    if (origLen == 0) {
        text.setCaretPosition(doc.getLength());
    }
}});

Should anyone read the API docs for JTextArea.append it claims to be thread-safe. JDK7 removes that unlikely claim (reminder: threading is hard). As a rule, in Swing I tend to always go straight for the model/Document.

I believe if the caret is at the end it should get moved on after an append. The only exception is if there is no text, because of the strange API. If it has been moved, then we probably don't want to update it after the append.

Note: If multiple threads are doing this, you don't necessarily know which will get there first.

Tom Hawtin - tackline