Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4201999
Votes 151
Synopsis JTextArea's don't automatically scroll when appending() to them.
Category java:classes_swing
Reported Against 1.2 , 1.3 , 1.2.2 , 1.4.1 , 1.2fcs , kestrel-rc1 , merlin-beta
Release Fixed 1.5(tiger)
State 10-Fix Delivered, bug
Priority: 4-Low
Related Bugs 4302560 , 4424708 , 4476714 , 4834399
Submit Date 11-JAN-1999
Description




I have tried several ways to get the JTextArea to scroll when 
appending text to it from somewhere OUTSIDE of an event handler,
but none seem to work.

This piece of code in an applet works correctly:


JTextArea ta = new JTextArea();
JScrollPane sp = new JScrollPane(ta);

public void init() 
{
  getContentPane().add(sp, BorderLayout.CENTER);
}

public void ActionPerformed(ActionEvent e)
{
  ta.append("Test");
}


If I move the ta.append() line outside of the ActionPerformed()
box, however, when the entire JTextArea fills up, it won't
continue scrolling down like I want it to (and like the old
TextAreas do).  The code that doesn't work is as follows (and
this is just a test, mind you):

JTextArea ta = new JTextArea();
JScrollPane sp = new JScrollPane(ta);

public void init() 
{
  getContentPane().add(sp, BorderLayout.CENTER);

// try { Thread.sleep(1000); } catch (InterruptedException e) {}

  ta.append("Test");
}
(Review ID: 52291)
======================================================================




bash-2.02$ //e/jdk1.3/bin/java -version
java version "1.3.0rc1"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0rc1-T)
Java HotSpot(TM) Client VM (build 1.3.0rc1-S, mixed mode)

When you append() a line to a JTextPane in a JScrollPane, the way of scrolling
depends on how you append it:
a) in a Thread - no scrolling to the new text
b) in a Listener - scrolling to the new text (desirable)
This is inconsistent and IMHO confusing to the user, who doesn't care about
threads/events and just wonders "why is this scrolling differently from that?"

Code to demonstrate:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class TestScrolls extends JFrame implements Runnable{

    JScrollPane scroll1;
    JScrollPane scroll2;
    JTextArea text1;
    JTextArea text2;

    public TestScrolls(){
	setSize(300, 300);
	getContentPane().setLayout(new GridLayout(2, 1));
	text1 = new JTextArea();
	text1.setEditable(false);
	text2 = new JTextArea();
	text2.setEditable(false);
	scroll1 = new JScrollPane(text1);
	scroll2 = new JScrollPane(text2);
	getContentPane().add(scroll1);
	getContentPane().add(scroll2);
	MM mm = new MM();
	this.addMouseMotionListener(mm);
	text1.addMouseMotionListener(mm);
	text2.addMouseMotionListener(mm);
	new Thread(this).start();
    }

    public void run(){
	while(true){
	    try{
		Thread.sleep(10);
		text1.append("an xxxxx  10 milliseconds have passed!\n");
	    }
	    catch(Throwable t){
	    }
	}
    }

    class MM extends MouseMotionAdapter{
	public void mouseMoved(MouseEvent e){
	    text2.append("mouse moved!\n");
	}
    }

    public static void main(String[] args){
	new TestScrolls().setVisible(true);
    }
}
(Review ID: 102906)
======================================================================
Work Around
Appending text in the event dispatching thread does scrolling.

Runnable doAppend = 
	    new Runnable() {
		public void run() {
		    text1.append("another 1 second has passed!\n");
		}
	    };

SwingUtilities.invokeLater(doAppend);
 xxxxx@xxxxx  2001-11-13
Evaluation
The submitter says that TextAreas work correctly, but JTextAreas do not, 
so I am reassigning this to Swing.  
 xxxxx@xxxxx  1999-01-11
--------
In fact this is a bug not a rfe.
Appending text in the event dispatching thread scrolls to the appended text appending text not in the event dispatching thread does not.
--- TestScrolls.java --
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;


public class TestScrolls extends JFrame implements Runnable{

    JScrollPane scroll1;
    JScrollPane scroll2;
    JTextArea text1;
    JTextArea text2;

    public TestScrolls(){
	setSize(300, 300);
	getContentPane().setLayout(new GridLayout(2, 1));
	text1 = new JTextArea();
	text1.setEditable(false);
	text2 = new JTextArea();
	text2.setEditable(false);
	scroll1 = new JScrollPane(text1);
	scroll2 = new JScrollPane(text2);
	getContentPane().add(scroll1);
	getContentPane().add(scroll2);
	MM mm = new MM();
	this.addMouseMotionListener(mm);
	text1.addMouseMotionListener(mm);
	text2.addMouseMotionListener(mm);
	new Thread(this).start();
    }

    public void run(){
	Runnable doAppend = 
	    new Runnable() {
		public void run() {
		    text1.append("another 1 second has passed!\n");
		}
	    };
	while(true){
	    try{
		Thread.sleep(1000);
		SwingUtilities.invokeLater(doAppend);
                text2.append("another 1 second has passed!\n");
	    }
	    catch(Throwable t){
	    }
	}
    }

    class MM extends MouseMotionAdapter{
	public void mouseMoved(MouseEvent e){
	    text2.append("mouse moved!\n");
	}
    }

    public static void main(String[] args){
	new TestScrolls().setVisible(true);
    }
}

---

Converting this rfe to a bug.


 xxxxx@xxxxx  2001-11-13




    The default behavior should remain the same because
some applications use the fact that caret doesn't update
its position when the document mutations are performed
not on the Event Dispatching Thread.
    The way out is to add 'updatePolicy' property to
the DefaultCaret which will control the caret updates
in response to the document updates. This property
allows to disable and enable caret updates independently
of the thread where document mutations are performed.


======================================================================
Comments
  
  Include a link with my name & email   

Submitted On 04-FEB-1999
amodla
Here is a work-around :
  /** scroll text up to see latest text appended */
  public void scrollUp()
    {
      JScrollBar vbar = scrollArea.getVerticalScrollBar();
      vbar.setValue(vbar.getMaximum());
    }
	public void addText(String text) {
		this.textArea.append(text);
		// delay needed for append thread to finish before setting the scroll bar
		// work around for Java bug #4201999
		try {Thread.sleep(100); } catch (InterruptedException e) {}
		scrollUp();
	}
There may be a way to determine when append finishes,
but I am not aware of how to do this yet.


Submitted On 22-APR-1999
daveriesz
For a workaround, try calling invalidate() for the scroll pane after you append
the text.


Submitted On 30-APR-1999
jpeng3
You need to set your caret position to the current
appending text location (see setCaretPosition).  
That should solve your problem.


Submitted On 29-DEC-1999
markuskhouri
here is another work around, which i found at
http://forum.java.sun.com/forum?13@214.7fZeaQZajHl^0@.ee86fac/0
TextArea ta = new TextArea(20,20);
ta.append("Some text!");
ta.setCaretPosition(ta.getText().length());


Submitted On 29-DEC-1999
markuskhouri
Even when I use the invokeLater(), it still does not scroll properly...
final String temp = newMessage;
Runnable updater = new Runnable(){
		public void run() {append(temp);}
		};
SwingUtilities.invokeLater(updater);


Submitted On 20-FEB-2001
traiton
i've met the same bug and i worked around it by creating an 
AdjustmentListener and added to the virtical ScrollBar, it 
should also work with the horizontal one.
here is the code for the work aroud:

this.getVerticalScrollBar().addAdjustmentListener(new 
ScrollCorrector());

  /**
   * corrects a strange behaviour with JScrollPane when 
lines are appended from another thread.
   * moves the vertical slider of a JScrollPane to the last 
position.
   */
  final class ScrollCorrector implements AdjustmentListener
  {
    private int lastMax=0;
    private boolean atLastPos=true;

    public void adjustmentValueChanged(AdjustmentEvent ae)
    {
    	JScrollBar vScroll=(JScrollBar)ae.getAdjustable();
			BoundedRangeModel 
brm=vScroll.getModel();
 	    int maxValue=brm.getMaximum();

      if(vScroll.getValueIsAdjusting())
      {
        int curValue=brm.getValue();
        int extent=brm.getExtent();
        int diff=maxValue-curValue-extent;
        if(diff==0)
        	atLastPos=true;
        else
        	atLastPos=false;
      }
      else
      {
        //	maxValue>lastMax means that a new line has 
been appended.
        if(maxValue>lastMax && atLastPos)
        {
  	  		brm.setValue(maxValue);
        	lastMax=maxValue;
        }

      }
    }
  }//	end of ScrollCorrector

please tell me if you have any enhancements.


Submitted On 24-APR-2001
munsey
In contrast to one of these comments, when I put the append 
inside of a Runnable and then invokeLater(), the auto 
scrolling does occur. I created the following, which did 
the job for me:
   private void appendText(String text_in)
   {
      final String text = text_in;
      Runnable append = new Runnable()
         {
            public void run()
            {
               text_.append(text);
            }
         };
      SwingUtilities.invokeLater(append);
   }


Submitted On 08-MAY-2001
MiguelM
In java.awt.TextArea, I wanted to turn this off! If it gets added to Swing, please make it property that I 
can disable. And leave it disabled by default.


Submitted On 31-MAY-2001
mdb47
I found this work around to work well.  I extended 
JTextArea and overridded the append method to include the 
line, "setCaretPosition(getText().length()-1);".  The minus 
one is necessary to prevent an empty row appearing at the 
bottom (i.e. the caret is past the last filled line).  I 
typically do this by creating an anonymous class: 

JTextArea jta = new JTextArea() {
    public void append(String str) {
        super.append(str);
        setCaretPosition(getText().length()-1);
    }
};


Submitted On 28-JUN-2001
bestsss
Absolutely, I don't even though this is a bug...


Submitted On 05-AUG-2001
HBlanchard
This effect shows up because the programmer is violating 
the following rule:
--------------------
Rule: Once a Swing component has been realized, all code 
that might affect or depend on the state of that component 
should be executed in the event-dispatching thread. 
--------------------
In other words use the invokeLater().


Submitted On 10-AUG-2001
dnoyeB
For JtextPanes, if the caretposition is at the end of the 
pane before the append, it will autoscroll, and then 
automatically redupate the caretposition so that you dont 
have to keep updating it.  maybe this is for Jtextareas as 
well?


Submitted On 05-DEC-2001
chuanhaochiu
This is NOT a bug.  Why did so many people vote on it?
I don't think it's one of those methods the JDK docs 
promise to work correctly when called from outside of the 
event dispatch thread.
So you just have to call it from the event dispatch thread 
for it to work, as you have to with the vast majority of 
Swing methods.

I for one do NOT want the auto-scrolling, from inside the 
event dispatch thread or not.
In fact, bug #4227520 was summitted against this "feature", 
but unfortunately closed without a good reason.


Submitted On 05-DEC-2001
chuanhaochiu
Sorry, that should've been: "with nary a good reason."


Submitted On 03-JAN-2002
enicholas
what is this bug still doing open?  You're modifying a
component from outside of the event thread.  Stop
doing that.


Submitted On 16-JAN-2002
rolandsaikali
I got this problem and I resolved it with the 
setCaretPosition() method... It works fine...


Submitted On 27-JAN-2002
selinadu
selinadu@yahoo.com.sg


Submitted On 03-MAR-2002
bernie01
After such easy and reliable workarounds are known it
appears that energy e.g. votes would be well spent on bug
4486185.
It appears it is not even possible to reliably show
multi-line text using this JTextarea component as
TableCellRenderer in a JTable.


Submitted On 29-MAR-2002
jdoe3000
Which morons are still voting for this?  This is NOT a bug!!!


Submitted On 18-APR-2002
bestsss
close that bug most of swing doesn't work well called
outside even dispatcher thread.
setCaretPosition is so easy.


Submitted On 30-APR-2002
bikegeek
This should be optional behaviour.

It should be possible to require that the JTextArea stays 
scrolled to the end IF the end is being viewed, but stay 
scrolled back if other parts of the text are being viewed.

This is a bit more than just doing a setCaretPosition().

You have to check whether the last line is visible before 
you append, set a flag, append the text, and only then (if 
the flag was set) position the caret at the end again.

It definitely needs adding.


Submitted On 06-JUN-2002
PaulFranz
I agree I don't think this is a bug. This actually could
cause problems if somebody is using a JTextArea for logging
then this could easily become annoying if it keeps
automatically scrolling to the end. All you need to do is
use the setCaretPosition and it will scroll.


Submitted On 10-JUN-2002
dnoyeB
I should add that I disagree with closing this bug unless 
this behavior gets well explained and documented in the doc 
of JTextArea.


Submitted On 10-JUN-2002
dnoyeB
I dont understand.  It though all Swing object manipulation 
MUST be done from the event disbatch thread, and that is 
what SwingUtilities.invokeLater() is for?  Did I miss 
somthing?

Also I have experienced this and I dont use swingUtilities 
as I should since I have only 1 thread writing to it 
anyway. and the swing thread can NOT write to it.

My solution too was to make sure the cursor was at the end 
and all works well.  To turn off scrolling simply move the 
cursor away from the end.
[code]
if(JCBAutoScroll.isSelected())
  jTextPane1.setCaretPosition(JStyDoc1.getLength
());      //this will cause autoscrolling
else if(jTextPane1.getCaretPosition() == JStyDoc1.getLength
())
  jTextPane1.setCaretPosition(JStyDoc1.getLength() -
1 );  //this will prevent autoscrolling

JStyDoc1.insertString(JStyDoc1.getLength(), message , 
mas_current);
[/code]


Submitted On 13-JUN-2002
dieselmom
Actually I agree with bikegeek.  It would be better if 
there were some way to control the automatic scrolling of 
the JScrollPane.  I have the opposite problem of the 
original defect.  My JTextArea is only a tiny component of 
the entire JScrollPane, yet it still scrolls the entire 
JScrollPane down to the JTextArea. If I use awt.TextArea it 
does not scroll, but it's ugly.  If I use JTextArea which 
due to designer constraints gets placed inside a panel with 
other components, inside another panel, and then inside a 
JScrollPane, the JTextArea is causing the viewport to 
scrolldown to my tiny little text area, although I really 
want the other components at the top of the JScrollPane to 
be visible first.  I cannot do this outside the thread 
either.


Submitted On 16-JUL-2002
kmranganathan
The work-around of having a adjustment listener for the 
VerticalScrollbar of the JScrollPane is not fully ok.It does take 
the scrollpane to the end but try to move the scrollbar back 
and trty to see the older contents.Since a adjustmentlistener 
is added and you cannot see the older part.setCaretPosition 
works properly.


Submitted On 23-JUL-2002
davidkheld
I've also met this problem and used the setCaretPosition() 
workaround.  Nowadays I use invokeLater().  I still don't 
understand why the behaviour is different from the event 
thread vs. any other thread.  

However, for everyone who made comments about how using 
append() from outside the event thread is invalid, check out 
the JavaDoc:

    /**
     * Appends the given text to the end of the document.  
Does nothing if
     * the model is null or the string is null or empty.
     * <p>
     * This method is thread safe, although most Swing 
methods
     * are not. Please see 
     * <A HREF="http://java.sun.com/products/jfc/swingdoc-
archive/threads.html">Threads
     * and Swing</A> for more information.     
     *
     * @param str the text to insert
     * @see #insert
     */
    public void append(String str) {


Several other JTextArea/JTextComponent methods - like 
setText() - are documented as being thread safe.  If they 
really are, then they ought to behave the same way 
regardless of the invoking thread.


Submitted On 19-FEB-2003
davin0
I am not sure if this is the same issue or not, but I found that 
calling JTextArea.append(text) can cause the program to 
freeze event though the docs say it is thread-safe.  This 
occurs in 1.4.1 but not in 1.3.1.

Switching to calling it in invokeLater solved the scrolling 
problem and resolved the deadlock.
 


Submitted On 17-JUL-2003
Alan_Peery1
I agree strongly with bikegeek's interpretation of how this
should be handled.


Submitted On 29-NOV-2003
pmuurray@bigpond.com
In any case, I don't see that appending to the text (a text
model thing) should affect the user display. What if you
have two panes backed by the same text model - positioned at
diferent "bookmarks"? You don't want both of them scrolling
to the end!

To do this sort of thing: manipulating the scroll position
in a particular window - you use setCaretPosition)(. 


Submitted On 29-NOV-2003
pmuurray@bigpond.com
"In fact this is a bug not a rfe. Appending text in the
event dispatching thread scrolls to the appended text
appending text not in the event dispatching thread does not."

This is not a bug. The Swing docs stipulate that
maniputation of swing objects must be done only on the swing
thread, with the exception of calling repaint() and
invalidate().



PLEASE NOTE: JDK6 is formerly known as Project Mustang