7

I am trying to make a GUI for a service, which have a JTextArea to view messages in, each message is written on a single line, and wordwrapped if needed.

The messages arrive via a socket, so it is merely an .append(message) that i am using to update the JTextArea, i need to limit these lines to 50 or 100 and i have no need to limit character count on each line.

If there is a method to limit the number lines in the JTextArea or if there is an alternative method of doing it?

I could really use the assistance in this matter.

Edit

The problem is that each client can send infinite lines, all these lines have to be readable, so this is not a simple check of the number of lines in the JTextArea. I need to remove older lines in order to view newer lines.

Ralph Andreasen
  • 103
  • 1
  • 1
  • 10
  • if you know how many characters are allowed in a line of the jtextarea just divide that by the number of total characters in the jtextarea – Noah Passalacqua Aug 28 '12 at 13:17
  • Related: http://stackoverflow.com/questions/479182/how-to-limit-jtextarea-max-rows-and-coloums – CloudyMarble Aug 28 '12 at 13:24
  • it is just the lines or rows that i need to limit in this case, so the number of characters is kind of irrelevant. – Ralph Andreasen Aug 28 '12 at 13:24
  • Just count the lines you append and stop appending after 50 lines. – Steve Kuo Aug 28 '12 at 13:37
  • It is my first question, so i am still learning how to ask them in a correct manner. But i need the most recent lines to be shown and after the program reaches 50 lines, then i have to remove from the beginning, so it will always have 50 lines, that is after it reaches 50 lines. I hope that this enlightens my problem a bit more. – Ralph Andreasen Aug 28 '12 at 13:49

4 Answers4

7

Use this to get row and column

Add a DocumentFilter which checks amount of rows (pass the doc.getLength() offset) and prevent adding more text.

Or you can create a dummy invisible JTextArea and add all the text there. Then measure last allowed line and cut the text.

StanislavL
  • 56,971
  • 9
  • 68
  • 98
  • curious: why use the utility method instead of textArea.getLine/Start/OfOffset ? – kleopatra Aug 28 '12 at 14:12
  • hach ... think I found it: the lines handled in that methods are the lines-between-cr, not the actual lines as they appear in the wrapped view? If that's true ... what-a-crap ... – kleopatra Aug 28 '12 at 15:13
7

Below is a crude DocumentFilter which appears to work. Its basic approach is to let the insert/append happen, query the number of lines after the fact, if more the the max, remove lines from the start as appropriate.

Beware: the lines counted with the textArea methods are (most probably, waiting for confirmation from @Stani) lines-between-cr, not the actual lines as layouted. Depending on your exact requirement, they may or may not suite you (if not, use the Stan's utility methods)

I was surprised and not entirely sure if it's safe

  • surprised: the insert method isn't called, needed to implement the replace method instead (in production ready code probably both)
  • not sure if the textArea is guaranteed to return up-to-date values in the filter methods (probably not, then the length check can be wrapped in an invokeLater)

Some code:

public class MyDocumentFilter extends DocumentFilter {

    private JTextArea area;
    private int max;

    public MyDocumentFilter(JTextArea area, int max) {
        this.area = area;
        this.max = max;
    }

    @Override
    public void replace(FilterBypass fb, int offset, int length,
            String text, AttributeSet attrs) throws BadLocationException {
        super.replace(fb, offset, length, text, attrs);
        int lines = area.getLineCount();
        if (lines > max) {
            int linesToRemove = lines - max -1;
            int lengthToRemove = area.getLineStartOffset(linesToRemove);
            remove(fb, 0, lengthToRemove);
        }
    }
}

// usage
JTextArea area = new JTextArea(10, 10);
((AbstractDocument) area.getDocument()).setDocumentFilter(new MyDocumentFilter(area, 3));
kleopatra
  • 51,061
  • 28
  • 99
  • 211
7

Would this be more efficient?

final int SCROLL_BUFFER_SIZE = 100;
public void trunkTextArea(JTextArea txtWin)
{
    int numLinesToTrunk = txtWin.getLineCount() - SCROLL_BUFFER_SIZE;
    if(numLinesToTrunk > 0)
    {
        try
        {
            int posOfLastLineToTrunk = txtWin.getLineEndOffset(numLinesToTrunk - 1);
            txtWin.replaceRange("",0,posOfLastLineToTrunk);
        }
        catch (BadLocationException ex) {
            ex.printStackTrace();
        }
    }
}
yan bellavance
  • 4,710
  • 20
  • 62
  • 93
  • Please note that whenever you hit the limit, it will truncate on every line insertion which is inefficient. It would be better to truncate to e.g. half of the limit once you hit the limit so you minimize the truncations. – BullyWiiPlaza Jun 05 '19 at 08:04
1

My approach works with the content of the JTextPane as a document. It assumes that your program has been appending text to the end of the document over and over, so that when you call doc.getCharacterElement(1), you will get basically a leaf element that represents the earliest line of code. This is what we want to delete, so then we get the start and end offsets of that element, and call the remove method to cut the earliest text.

This could be selectively performed by checking whether the number of characters total is greater than a million (as I have done below), or based on the number of lines you have already added.

If you want to based it on the number of lines already added, you will need a field that gets incremented each time you print to the JTextPane, so that this code runs once every time you print a new line of text and the count exceeds your maximum value.

        JTextPane pane = ...
        StyledDocument doc = pane.getStyledDocument();
        int size = doc.getLength();
        if (size > 1000000) {
            out.println("Size: " + size);
            out.println(":" + 1);
            int i = 0;
            Element e = doc.getCharacterElement(i + 1);
            int start = e.getStartOffset();
            int end = e.getEndOffset();

            try {
                doc.remove(start, end);
            } catch (BadLocationException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }

Cheers!

Thomas Adkins
  • 412
  • 3
  • 6