javaswingjtextfielddocumentfilter

Pasting/Replacing a character using mouse selection in Java textfield (with specified character limit)


I have extended DocumentFilter class to limit the number of characters to be entered to the specified number in text field. Here's my SSCE:

Main class:

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.net.*;
import java.io.*;
import java.util.Date;
import java.util.StringTokenizer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.text.PlainDocument;

public class Sandbox implements Runnable {
    private JFrame frame;
    private JTextField inputField;
    private JButton searchButton; 
    private int MAX_CHAR_LIMIT = 1;

    public Sandbox() {
        inputField = new JTextField(); 
        inputField.setColumns(10);
        inputField.setFont(new Font(null, Font.BOLD, 20));
    }

    @Override
    public void run() {
        frame = new JFrame("SSCE");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(500, 300));

        PlainDocument doc = (PlainDocument) inputField.getDocument();
        doc.setDocumentFilter(new DocumentCharLimitFilter(MAX_CHAR_LIMIT));
        frame.getContentPane().setLayout(new FlowLayout());
        frame.getContentPane().add(inputField);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Sandbox());
    }

}

DocumentCharLimitFilter class

import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;

public class DocumentCharLimitFilter extends DocumentFilter {

    private int MAX_CHAR_LIMIT;

    public DocumentCharLimitFilter(int maxChars) {
        this.MAX_CHAR_LIMIT = maxChars;
    }

    // You don't need insertString() when entering text in JTextField. 
    @Override
public void replace(FilterBypass fb, int offset, int length, String newText, AttributeSet aSet) throws BadLocationException {
    int oldTextLength = fb.getDocument().getLength();
    String oldText = fb.getDocument().getText(0, oldTextLength);
    String combinedText = oldText + newText;        

    if (combinedText.length() <= MAX_CHAR_LIMIT) {
        super.replace(fb, offset, length, newText, aSet);

        // paste characters upto maximum allowed limit 
    } else if (oldTextLength < MAX_CHAR_LIMIT) {
        int cutPosition = MAX_CHAR_LIMIT - oldTextLength;
        String cutToFit = newText.substring(0, cutPosition);
        super.replace(fb, offset, length, cutToFit, aSet);

    } else {
        System.out.println("Character limit of " + MAX_CHAR_LIMIT + " exceeded.");
    }
}
}

enter image description here

When I fire up the above program and try to paste some new character on top of existing character using Ctrl+V, it takes me to else loop of replace() method of DocumentCharLimitFilter and I get:

Character limit of 1 exceeded.

From what I understand through all this, the new character that I am pasting using mouse and Ctrl+V is taken as additional character on top of existing character and I get the above message. When I use keyboard to input characters, it works just fine. If I increase the MAX_CHAR_LIMIT to 2, I can paste in the text but now when I give input from keyboard, I am able to input two characters which I don't want.

How do I make the above code such that it works like normal pasting on top of existing text and replacing it, using Ctr + V and still limiting the keyboard characters to the specified limit? I am a beginner to Java. If I need to provide any information, I would be glad to do so. Thank you.

Update: @camickr, @VGR thank you so much! I didn't pay attention to the length argument in replace() method. This is what I did in DocumentCharLimitFilter class:

@Override
public void replace(FilterBypass fb, int offset, int length, String newText, AttributeSet aSet) throws BadLocationException {
    System.out.println(fb.getClass());
    int oldTextLength = fb.getDocument().getLength();
    String oldText = fb.getDocument().getText(0, oldTextLength);
    String combinedText = oldText + newText;

    if (combinedText.length() <= MAX_CHAR_LIMIT) {
        super.replace(fb, offset, length, newText, aSet);

        // paste characters upto maximum allowed limit 
    } else if (oldTextLength < MAX_CHAR_LIMIT) {
        int cutPosition = MAX_CHAR_LIMIT - oldTextLength;
        String cutToFit = newText.substring(0, cutPosition);
        super.replace(fb, offset, length, cutToFit, aSet);

        // NEW CODE 
        // http://stackoverflow.com/questions/42512743/pasting-replacing-a-character-using-mouse-selection-in-java-textfield-with-spec
        // length indicates number of characters highlighted using mouse or keyboard. This will work only when 
        // the entire text is highlighted (if entire text is not highlighted, it would get complicated)
        // and when it is highlighted, I make sure that the new text to be pasted is within limits of the MAX_CHAR_LIMIT
    } else if (length == MAX_CHAR_LIMIT) {
        String correctedString = newText;
        if (newText.length() > MAX_CHAR_LIMIT) {
            correctedString = newText.substring(0, MAX_CHAR_LIMIT);
        }
        super.replace(fb, offset, length, correctedString, aSet);

    } else {
        System.out.println("Entered characters exceed specified limit of " + MAX_CHAR_LIMIT + "exceeded.");
    }
}

Solution

  • You are ignoring the length argument passed to the replace method, which contains the number of existing characters being replaced.

    If you highlight one or more characters in the JTextField, then type or paste some new text, length will contain a positive value. The new length of the JTextField’s content will be oldTextLength - length + newText.length() (unless you change it).