IAmYourFaja
IAmYourFaja

Reputation: 56944

Enforce max characters on Swing JTextArea with a few curve balls

I'm trying to add functionality to a Swing JLabel and JTextArea such that:

I have 90% of this functionality working, but have a few bugs, and have no clue as to how to implement the last item above (bulk commands on highlighted text). Here's what I have:

boolean ignoreInput = false;
int charMax = 500;
JLabel charCntLabel = getLabel();
JTextArea myTextArea = getTextArea();

myTextArea.addKeyListener(new KeyListener() {
    @Override
    public void keyTyped(KeyEvent e) {
        return;
    }

    @Override
    public void keyReleased(KeyEvent e) {
        // If we should be ignoring input then set make sure we
        // enforce max character count and remove the newly typed key.
        if(ignoreInput)
            myTextArea.setText(myTextArea.getText().substring(0,
                myTextArea.getText().length()));
    }

    @Override
    public void keyPressed(KeyEvent e) {
        String charsRemaining = " characters remaining";
        int newLen = 0;

        // The key has just been pressed so Swing hasn't updated
        // the text area with the new KeyEvent.
        int currLen = myTextArea.getText().length();

        // Adjust newLen depending on whether the user just pressed
        // the backspace key or not.
        if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
            newLen = currLen - 1;
            ignoreInput = false;
        }
        else
            newLen = currLen + 1;

        if(newLen < 0)
            newLen = 0;

        if(newLen == 0)
            charCntLabel.setText(charMax + " characters maximum!");
        else if(newLen >= 0 && newLen < charMax)
            charCntLabel.setText((charMax - newLen) + charsRemaining);
        else if(newLen >= charMax) {
            ignoreInput = true;
            charCntLabel.setText("0 " + charsRemaining);
        }
    }
});

The above code works pretty well, but has a few bugs:

The questions

  1. Is there a simpler way to implement this desired functionality (and for a Swing JTextArea)? I feel like I'm reinventing the wheel here and that there might be a "cleaner" way of enforcing character maximums and updating their respective labels.
  2. If not, can anybody spot my > 500 char bug? I've been looking at it all morning and am pulling my hair out.
  3. Most importantly, how do I implement my requirement to handle bulk commands to highlighted text? How do I hand text selections inside the textarea, listen for changes to the highlighted text (e.g., deleting multiple highlighted characters with the backspace button, etc.), and correctly calculate the new value for chars remaining?

Thanks in advance.

Upvotes: 1

Views: 21369

Answers (3)

Forseth11
Forseth11

Reputation: 1438

To add on to what Ray S. Kan said:

There is an easier way to limit the characters for a JTextArea without having to use a DocumentFilter as shown by Ray S. Kan, but the issue with his answer is that it does not prevent someone from pasting in a long text. The following will prevent a user from pasting in stuff to bypass the limit:

@Override
public void keyTyped(KeyEvent e) {
    int max = 25;
    if(text.getText().length() > max+1) {
        e.consume();
        String shortened = text.getText().substring(0, max);
        text.setText(shortened);
    }else if(text.getText().length() > max) {
        e.consume();
    }
}

This will stop a key from being pressed if the length is not pass max, but if it passes max, it will simply replace the string in the text area with a shorter string. The text variable is the JTextArea swing object.

Upvotes: 1

Ray S. Kan
Ray S. Kan

Reputation: 119

evt.consume(); will help alot in this case..you dont need to use DocumentFilter. here is a much easier way of limiting user at certain length

private void jTextArea1KeyTyped(java.awt.event.KeyEvent evt) {                                    
    String s=jTextArea1.getText();
   int l=s.length();
   jTextField1.setText(String.valueOf(l));
   int i=10-l;
   jTextField2.setText(String.valueOf(i));
   try{
   if(l>=10){evt.consume();
   }
   }
   catch(Exception w){}

} 

Upvotes: 2

Polyana Fontes
Polyana Fontes

Reputation: 3212

You can limit the max size by using a DocumentFilter, check this documentation section, it has a working example of what you need.

Take this as an example, I used the component from the example file above:

import java.awt.BorderLayout;

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

import components.DocumentSizeFilter;

public class Test {

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

    private static class TestFrame extends JFrame{
        private JTextField textField;
        private DefaultStyledDocument doc;
        private JLabel remaningLabel = new JLabel();

        public TestFrame() {
            setLayout(new BorderLayout());

            textField = new JTextField();
            doc = new DefaultStyledDocument();
            doc.setDocumentFilter(new DocumentSizeFilter(500));
            doc.addDocumentListener(new DocumentListener(){
                @Override
                public void changedUpdate(DocumentEvent e) { updateCount();}
                @Override
                public void insertUpdate(DocumentEvent e) { updateCount();}
                @Override
                public void removeUpdate(DocumentEvent e) { updateCount();}
            });
            textField.setDocument(doc);

            updateCount();

            add(textField, BorderLayout.CENTER);
            add(remaningLabel, BorderLayout.SOUTH);

            setLocationRelativeTo(null);
            pack();
        }

        private void updateCount()
        {
            remaningLabel.setText((500 -doc.getLength()) + " characters remaining");
        }
    }
}

Upvotes: 10

Related Questions