Question

In a Jtextpane(with in a Jscrollpane) whenever we enter text it will be printed from top to bottom,

is it possible to keep the scroll bar at the bottom and move old text upwards(enabling scrollbar) whenever a text is appended?

e.g : if user is at line 1 and types "xyz" and press ENTER . Scrollbar should be enabled and xyz should be moved upwards with caret at second line.So if user scrolls up he will be able to see "xyz".

The problem is similar to my previous questions. I am trying to make it clear to get an answer.

Thanks.

EDIT

public class TextPane extends JFrame implements ActionListener, KeyListener{
public static TextPane instance;
private static JTextPane terminal = new JTextPane();
private static JScrollPane scroll = new JScrollPane();

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            TextPane.getInstance().init();
        }
    });
}
private static TextPane getInstance() {
    if(null == instance){
        instance =  new TextPane();
    }
    return instance;
}
private EnterAction enterAction;

private void init() {
    terminal.setFont(new Font("Courier new", Font.PLAIN, 12));
    terminal.setLayout(new BorderLayout());
    terminal.setBackground(Color.black);
    terminal.setForeground(Color.white);
    terminal.addKeyListener(this);
    terminal.setCaretColor(Color.green);
    terminal.setDragEnabled(false);

    enterAction = new EnterAction();
    terminal.getInputMap().put( KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "enter");
    terminal.getActionMap().put("enter", enterAction);

    DefaultCaret caret = (DefaultCaret) terminal.getCaret();
    caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
    scroll.setViewportView(terminal);
    add(scroll);     
    setTitle("Dummy");
    setSize(500 , 500);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setResizable(true);
    setVisible(true);
}

class EnterAction extends AbstractAction {
   //I typed a single line (in first line) of text and pressed enter.
    private static final long serialVersionUID = 1L;  
    public void actionPerformed(ActionEvent ae ) {
        try {
            Document doc = terminal.getDocument();
            int car = terminal.getCaretPosition();

            //Currently i tried adding newlines to get scroll bar in order to hide previously displayed contents.
            for(int i=0 ; i< 35 ; i++){
               doc.insertString(doc.getLength(), "\n", null);
            }               
            terminal.setCaretPosition(car + 1); // To set the caret at second line.
            JScrollBar sb = scroll.getVerticalScrollBar();
            sb.setValue( sb.getMaximum());//to keep the scroll bar at bottom.(Any how above setCaretPositon() makes it not work).
        } catch (Exception ex) {
            ex.printStackTrace();

        }
    }
}
@Override
public void actionPerformed(ActionEvent e) {}

@Override
public void keyTyped(KeyEvent e) {}

@Override
public void keyPressed(KeyEvent e) {}

@Override
public void keyReleased(KeyEvent e) {}

}

Was it helpful?

Solution

I think this is what you want. Some notes:

  1. Like your example the code uses blank lines to fill up the document. Although in this example the lines are only ever added once when the text component is created.
  2. The invokeLater is used in the EnterAction to add the code to the end of the Event Dispatch Thread. This makes sure that all updates to the Document have been completed and therefore the maximum value of the scrollbar will be correct.
  3. I used a JTextArea because it is easy to control the size of the text area based on the number of blank lines you need to initialize the text area. If you use a JTextPane then you will need to do extra calculations to determine the actual size of the text pane so that the scrollbars work correctly.
  4. As a bonus I created a simple NavigationFilter which will prevent the user from moving the caret to any of the blank lines at the end of the Document. This feature will also prevent text selection for any of those blank lines using the mouse.
  5. It is still possible to delete the blank lines. So you may want to create a KeyBinding that does nothing when the "Delete" key is pressed.
  6. The code to override the width of the frame is just to prevent the horizontal scrollbar from appearing the first time you enter any data in the text pane. You may want to consider to always display the scrollbar, since it will continually be visible after you add the first line of data anyway.

The Code:

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

public class NavigationFilterSuffix extends NavigationFilter
{
    private int suffixLength;
    private Document doc;

    public NavigationFilterSuffix(int suffixLength, Document doc)
    {
        this.suffixLength = suffixLength;
        System.out.println(suffixLength);
        this.doc = doc;
    }

    public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
    {
        int endOfDocument = doc.getLength() - suffixLength + 1;
        fb.setDot(Math.min(dot, endOfDocument), bias);
    }

    public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
    {
        int endOfDocument = doc.getLength() - suffixLength + 1;
        fb.moveDot(Math.min(dot, endOfDocument), bias);
    }

    public static void main(String args[]) throws Exception
    {
        final JTextArea terminal = new JTextArea(15, 30);
        // add 14 new lines (one less than rows specified above
        terminal.setText("\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
        terminal.setFont(new Font("Courier new", Font.PLAIN, 12));
        terminal.setBackground(Color.black);
        terminal.setForeground(Color.white);
        terminal.setCaretColor(Color.green);
        terminal.setDragEnabled(false);
        terminal.setCaretPosition(0);

        final JScrollPane scrollPane = new JScrollPane( terminal );

        Action enterAction = new EnterAction();
        terminal.getInputMap().put( KeyStroke.getKeyStroke("ENTER"), "enter");
        terminal.getActionMap().put("enter", enterAction);

        terminal.setNavigationFilter(new NavigationFilterSuffix(terminal.getRows(), terminal.getDocument()));

        JFrame frame = new JFrame("Navigation Filter Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(scrollPane);
        frame.pack();
        Dimension d = frame.getSize();
        d.width += 20;
        frame.setSize(d);
        frame.setLocationRelativeTo( null );
        frame.setVisible(true);
    }


    static class EnterAction extends AbstractAction
    {
        public void actionPerformed(ActionEvent ae )
        {
            try
            {
                final JTextArea terminal = (JTextArea)ae.getSource();
                Document doc = terminal.getDocument();
                int car = terminal.getCaretPosition();
                doc.insertString(car, "\n", null);

                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        JScrollPane scrollPane = (JScrollPane)SwingUtilities.getAncestorOfClass(JScrollPane.class, terminal);
                        JScrollBar sb = scrollPane.getVerticalScrollBar();
                        sb.setValue( sb.getMaximum());
                    }
                });

            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
        }
    }
}

Good luck.

OTHER TIPS

Sample code as per the conversation above. Please check if this solves your purpose.

public class ThingDoer extends JPanel {

    public ThingDoer() {
        JTextArea text = new JTextArea(1, 10);
        text.setLineWrap(true);
        text.setWrapStyleWord(true);

        JScrollPane scrollPane = new JScrollPane(text,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
                );
        add(scrollPane);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.add(new ThingDoer());
        frame.pack();
        frame.setSize(200, 300);
        frame.setVisible(true);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top