I'm attempting to create a dynamic JTextField. This JTextField appends a wildcard ('*') when its max characters haven't been filled. I'm wanting to remove this wildcard when it does have its max characters, but if the user backspaces, I want the wildcard to be appended again. The wildcard should also be "permanent" in the sense that it can't be deleted or backspaced and the caret position should begin before the wildcard.
TLDR; A dynamic wildcard appended in a JTextField
What I Have So Far (4/17/2017)
Program initalizes
JTextField with max characters
This JTextField is alphanumeric and its max characters is 10. I want it so that when it hits 10, the wildcard is deleted. And if I backspace, it should be appended again.
The problem is that the wildcard is permanently there. Whenever I type anything AND if it it's max characters.
AlphaNumericTextField.java
public class AlphaNumericTextField extends DocumentFilter{
int maxDigits;
public AlphaNumericTextField(int maxDigits) {
this.maxDigits = maxDigits;
}
public void AlphaNumericTextField(){
this.maxDigits = Integer.MAX_VALUE;
}
@Override
public void insertString(FilterBypass fb, int off, String str, AttributeSet attr) throws BadLocationException {
if(str == null)
return;
if ((fb.getDocument().getLength() + str.length()-1) <= maxDigits) {
fb.insertString(off, str.replaceAll("[^\\w*]", ""), attr);
}
}
@Override
public void replace(FilterBypass fb, int off, int len, String str, AttributeSet attr) throws BadLocationException {
if(str == null)
return;
if ((fb.getDocument().getLength() + str.length()-1 - len) <= maxDigits) {
fb.replace(off, len, str.replaceAll("[^\\w*]", ""), attr);
}
}
}
WildCard.java (NavigationFilter)
The NavigationFilter is my incredibly makeshift attempt to making the wildcard "permanent" or "uneditable".
public class WildCard extends NavigationFilter
{
private Action deletePrevious;
private Action deleteNext;
public JTextComponent component;
public WildCard(JTextComponent component)
{
this.component = component;
deletePrevious = component.getActionMap().get("delete-previous");
deleteNext = component.getActionMap().get("delete-next");
component.getActionMap().put("delete-previous", new BackspaceAction());
component.getActionMap().put("delete-next", new DeleteAction());
component.setCaretPosition(0);
}
public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
{
if(dot < component.getText().length()) {
fb.setDot(Math.min(dot, component.getText().length()), bias);
}
}
public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
{
if(dot < component.getText().length())
fb.moveDot(Math.min(dot, component.getText().length()), bias);
}
class BackspaceAction extends AbstractAction
{
@Override
public void actionPerformed(ActionEvent e)
{
JTextComponent component = (JTextComponent)e.getSource();
if (component.getCaretPosition() > 0)
deletePrevious.actionPerformed( null );
}
}
class DeleteAction extends AbstractAction
{
@Override
public void actionPerformed(ActionEvent e)
{
JTextComponent component = (JTextComponent)e.getSource();
if (component.getCaretPosition() < component.getText().length()-1)
deleteNext.actionPerformed( null );
}
}
}
Resources and Where I've Looked:
Limited selection in a JTextField/JTextComponent?
How to implement in Java ( JTextField class ) to allow entering only digits?
remove last character from jtextfield
I saw a lot of places discouraging keyListeners due to copy and pasted information. I also saw a solution dealing with a propertyChangeListener but I couldn't get that to work.
I also attempted both:
setText(getText().substring(0, getText().length() - 1))
setText(getText.replaceAll("*", ""))
However, there's this weird issue with the caret restarting all the way to index 0 and then I begin typing backwards into my JTextField. Another issue was due to my NavigationFilter. Once I deleted the wildcard, I wasn't able to edit the last digit.
Current Environment
- NetBeans IDE 8.2 (for the GUI generator)
- Java 1.8.0_111
Additional Notes
A crude example of what I'm hoping to implement...
The '|' symbol is the caret symbol and max characters in this example is 6:
a|*
ab|*
abc|*
abc1|*
abc12|*
abc123| (NO WILDCARD)
abc12|* (Backspaced, so there is a wildcard)
OR
ab|123* (Deleted some character so the length is less than 6)
Thank you so much!
EDIT Here is the solution. Thank you so much!
public class AlphaNumericTextField extends DocumentFilter{
int maxChar;
JTextComponent field;
public AlphaNumericTextField(int maxChar, JTextComponent field) {
this.maxChar = maxChar;
this.field = field;
}
public void AlphaNumericTextField(){
this.maxChar = Integer.MAX_VALUE;
}
@Override
public void insertString(FilterBypass fb, int off, String str, AttributeSet attr)
throws BadLocationException {
if(str == null)
return;
fb.insertString(off, str.replaceAll("[^\\w*]", ""), attr);
}
@Override
public void replace(FilterBypass fb, int off, int len, String str, AttributeSet attr)
throws BadLocationException {
if(str == null)
return;
if ((fb.getDocument().getLength() + str.length()-1 - len) < maxChar) {
if(fb.getDocument().getText(0, fb.getDocument().getLength()).indexOf("*") == -1) {
fb.replace(off, len, str.replaceAll("[^\\w*]", "") + "*", attr);
field.setCaretPosition(off+str.length());
}
else {
fb.replace(off, len, str.replaceAll("[^\\w*]", ""), attr);
}
}
else if ((fb.getDocument().getLength() + str.length()-1 - len) == maxChar) {
fb.replace(off, len+1, str, attr);
}
}
@Override
public void remove(DocumentFilter.FilterBypass fb, int off, int len) throws BadLocationException{
fb.remove(off, len);
if (off == maxChar - 1) {
// Not really sure why this works... Adds 2 asterisks if field.getText() + "*"
field.setText(field.getText());
field.setCaretPosition(off);
}
}
}