Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

JTextArea: define scrolling behaviour after setText/append globally?

83 views
Skip to first unread message

Kleopatra

unread,
Nov 28, 2001, 9:04:22 AM11/28/01
to

The default behaviour after setting/appending Text to a JTextArea that's
in a JScrollpane seems to be to scroll to the end of the text. I know
that I can reset to top of document with setCaretPosition(0).

But how to make that the default? (I would rather not subclass
JTextXX.setText/append and call that method after calling super). Where
exactly is the default scrolling defined?

Thanks in advance
Jeanette

Christian Kaufhold

unread,
Nov 28, 2001, 4:41:15 PM11/28/01
to
Hello!

Kleopatra <fast...@addcom.de> wrote:


It's in DefaultCaret. There are actually two different things to consider:

- DefaultCaret itself "often" (I suppose at least on model changes and on
caret moves) makes "itself" (i.e. the location where it wants to paint
its image) visible (by scrollRectToVisible(), see DefaultCaret.adjust-
Visibility). This needs to be done during typing and typically also make
sense for programmatically inserted texts, if only because the caret
(or selection) typically is the only indication of whether the text
component has focus. Also, if the caret were not visible, typing any
key will possibly cause weird scrolling effects.

- How does the caret move on model changes? By default (and it would need
quite some efforts to do this differently), the caret (i.e. DefaultCaret)
is supposed to implicitly move on model changes, and not changed expli-
citly on user insertions. OTOH, DefaultCaret does not move at all if the
DocumentEvent does not arrive in the event-dispatch thread (why?).

This policy essentially determines how the caret has to move with
document changes to have the standard behaviour for typing in text.
The one ambiguous situation for a "cursor" (i.e. something that is bet-
ween two indices) is when insertion happens at the place where the cursor
is (see also http://www.chka.de/swing/cursors.html):

Let | be the caret/cursor:

A B | C D E

If now text is inserted at C (index 2), should the caret move with it:

A B N E W | C D E (I)

or not:

A B | N E W C D E (II)

?

This corresponds to:

| public void insertUpdate(DocumentEvent e) {
| if (async || SwingUtilities.isEventDispatchThread()) {
| int adjust = 0;
| int offset = e.getOffset();
| int length = e.getLength();
| if (dot >= offset) { (*)
| adjust = length;
| }
| if (mark >= offset) { (*)
| mark += length;
| }

having ">=" or ">" in the (*)-marked places in DefaultCaret.

To have the normal behaviour when typing text, (I) is performed.
Otherwise each method that inserted text would also have to move the
Caret explicitly by the number of inserted characters.


Now for the problem of filling a document/text component just by appen-
ding.

- If you don't fill from the event-dispatch thread (which is not really
thread-safe, despite the claims in the documentation), the caret will
remain at the beginning, thus the text-component will never scroll to
the bottom automatically. This can be done explicitly, but if you
just use scrollRectToVisible(), the caret will scroll back to top at
"the next time it thinks it has to".
- If you fill from the event-dispatch thread:
* If you start with an empty document, the caret will be at the end,
and, preferring to stay after newly-inserted text, will always
remain there, so the bottom of the text component will always be
made visible.
If it should remain at the top, one can (for example) use a Docu-
mentListener that sets the caret back to index 0 whenever the
document was filled and was empty before.
* If you start with a non-empty document, it depends on whether the
caret is at the end (and it is by default.) whether it the bottom
will be made visible or the top will remain (i.e. caret remains
where it is).

Because index 0 is ambiguous when the document is empty (does it mean
"more" at the end or "more" at the beginning?), empty document are
special in this case.
Because caret cannot distinguish between programmatical and user-induced
text insertions, one of these needs explicit calls to setCaretPosition
(or similar). It is probably easier with programmatical insertions

for example (untested).

public void append(String s)
{
Document d = getDocument();

int position = getCaretPosition();
boolean atEnd = position == d.getLength();

try
{
d.insertString(d.getLength(), s, null);
}
catch (BadLocationException argh) { }

if (atEnd)
setCaretPosition(position);
}

to avoid the caret moving with the insertion.


Christian
--
NP: R.E.M. - So Fast, So Numb
NR: James Morrow - Die eingeborene Tochter

Kleopatra

unread,
Nov 29, 2001, 3:36:34 AM11/29/01
to

Christian,

thanks for the very thorough and complete explanation, especially for
pointing out why I really don't want to fiddle with the default
behaviour. Reviewing my problem I found that actually I wanted the caret
to stay at position 0 after setText only so I ended up with subclassing
and calling setCaretPosition(0) after super's setText.

> citly on user insertions. OTOH, DefaultCaret does not move at all if the
> DocumentEvent does not arrive in the event-dispatch thread (why?).

Sounds strange. Maybe the documentListener in DefaultCaret is not
thread-safe and we see here a side-effect? Just a wild guess.

>
> - If you don't fill from the event-dispatch thread (which is not really
> thread-safe, despite the claims in the documentation), the caret will

Why isn't it thread-safe? I thought the write lock guarantees it?

Thanks again
Jeanette

Christian Kaufhold

unread,
Nov 29, 2001, 8:24:51 AM11/29/01
to
Hello!

Kleopatra <fast...@addcom.de> wrote:

>> - If you don't fill from the event-dispatch thread (which is not really
>> thread-safe, despite the claims in the documentation), the caret will

> Why isn't it thread-safe? I thought the write lock guarantees it?


There are several ways in which it is not thread-safe (See some thoughts
at http://www.chka.de/swing/text/threads.html for more information).

1. The Document need not support multithreading at all, i.e. it assumes
all of its methods are only called from the event-dispatch thread.
This is explicitly allowed by the Document documentation, but no
indication of that fact is provided with those text methods that
are claimed to be "thread-safe". At the minimum level, the documen-
tation should read:

"This method is thread-safe if the underlying Document is."

But weirdnesses of the Document locking mechanism make it impossible
that these methods are thread-safe at all;

2. JTextArea.append calls getLength() without a (read) lock. It is un-
specified whether any Document read methods work without a read lock.
Even if it obtained a read lock, it would have to give it up in order
to call insertString() (read locks are not upgradable), and then the
length may have changed again.

3. Even if getLength() doesn't crash, there is no guarantee that the
length of the Document remains the same until insertString() actually
acquires the write lock. *Except for insertString(0, ...) and
remove(0, 0) (which is pointless) there are no Document modification
methods that can be called in a thread-safe way at all.* You may end
up at invalid indices or at a totally different place in the document
then you intented.

No-one would consider (vector being a Vector, so its individual
methods are thread-safe, which is even more than can be said about
Document.getLength())

vector.add(vector.size(), "whatever");

thread-safe without a synchronized (vector) { } around it, but the
interface Document doesn't allow the write-lock equivalent of it.

document.writeLock();

try
{
document.insertString(document.getLength(), s, null);
}
finally
{
document.writeUnlock();
}

would be thread-safe (if the document were required to support
these methods). The fact that insertString() and remove() are
thread-safe is pointless if they can almost never be called in a
thread-safe way.

[Insert rant about BadLocationException here]

If a) you use thread-safe Documents (that means subclasses of Abstract-
Document, Document.render() is not supported, instead AbstractDocument.
read(Un-)Lock is used) and b) only change the Document from one thread,
you won't see any problems. That excludes the case of an editable text
component that is although written to by a background thread.


Christian
--
NP: k's Choice - My Head

Kleopatra

unread,
Nov 30, 2001, 4:04:43 AM11/30/01
to

Christian Kaufhold schrieb:


>
> There are several ways in which it is not thread-safe (See some thoughts
> at http://www.chka.de/swing/text/threads.html for more information).

[...]

Thanks, Christian, for that very good summary.

Jeanette


0 new messages