Java / Swing: JTextArea en un JScrollPane, ¿cómo evitar el desplazamiento automático?

Aquí hay un código ejecutable que muestra cuál es mi "problema".

tengo unJTextArea envuelto en unJScrollPane. Cuando cambio el texto de laJTextArea, elJScrollPane se desplaza automáticamente hasta el final del texto y no quiero eso.

Aquí están mis requisitos:

la aplicaciónno debería desplazarse verticalmente automáticamente pero ...el usuariodebería ser capaz de desplazarse verticalmenteel usuariono debería ser capaz de desplazarse horizontalmentela aplicaciónnunca debería desplazarse horizontalmenteJTextArea no debe ser editable

(por lo tanto, incluso si hay más texto del que puede caber horizontalmente, ni la aplicación ni el usuario deberían poder desplazarse horizontalmente. Mientras que verticalmente, solo el usuario debería poder desplazarse).

No sé cómo "arreglar" esto: si esto se soluciona usandoJTextArea oJScrollPane métodos?

Tenga en cuenta que AFAICT esto esno un duplicado en absoluto de:JTextPane evita el desplazamiento en el JScrollPane padre

Aquí está el ejemplo un poco divertido, cada 200 ms pone texto nuevo en elJTextArea y puedes ver elJScrollPane siempre desplazándose automáticamente al final del texto.

import javax.swing.*;
import java.awt.*;
import java.util.Random;

public final class TextInScrollPane extends JFrame {

    private static final Random r = new Random( 42 );

    public static void main( final String[] args ) {
        final JFrame f = new JFrame();
        f.setDefaultCloseOperation( EXIT_ON_CLOSE );
        f.setLayout(new BorderLayout());
        final JTextArea jta = new JTextArea( "Some text", 30, 30 );
        jta.setEditable( false );   // This must not be editable
        final JScrollPane jsp = new JScrollPane( jta );
        jsp.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
        jsp.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS );
        f.add( jsp, BorderLayout.CENTER );
        f.pack();
        f.setLocationRelativeTo( null );
        f.setVisible(true);

        final Thread t = new Thread( new Runnable() {
            public void run() {
                while ( true ) { 
                    try {Thread.sleep( 200 );} catch ( InterruptedException e ) {}
                    final StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < 50 + r.nextInt( 75 ); i++) {
                        for (int j = 0; j < r.nextInt(120); j++) {
                            sb.append( (char) 'a' + r.nextInt(26) );
                        }
                        sb.append( '\n' );
                    }
                    SwingUtilities.invokeLater( new Runnable() {
                        public void run() {
                            jta.setText( sb.toString() );
                        }
                    } );
                }
            }
        });
        t.start();
    }

}

Respuestas a la pregunta(3)

Su respuesta a la pregunta