package com.vitco.util.components.textfield;
import javax.swing.*;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import java.util.ArrayList;
/**
* A text field that allows for attaching a listener.
*
* By default it is restricted to the characters a-z, A-Z, 0-9, "-", "_", and "."
*/
public class JCustomTextField extends JTextField {
// list of valid characters
private final int[] validChars;
// constructor with default valid chars
public JCustomTextField() {
this(new int[] {
// a-z
97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122,
// A-Z
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
// 0-9
48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
// "-", "_", "."
45, 95, 46
});
}
// constructor (image and the action)
public JCustomTextField(final int[] validChars) {
super();
this.validChars = validChars.clone();
// set the filter for this textarea
((AbstractDocument)this.getDocument()).setDocumentFilter(new JCustomTextField.TextFilter());
}
// ==================
// listener events
// ==================
// the current string used
private String currentString = "";
// retrieve the current string
// Note: This needs to be overwritten to avoid modification exception
@Override
public final String getText() {
return currentString;
}
// --------------
// holds the listeners
private final ArrayList<TextChangeListener> listener = new ArrayList<TextChangeListener>();
// add a listener
public final void addTextChangeListener(TextChangeListener tcl) {
listener.add(tcl);
}
// remove a listener
public final void removeTextChangeListener(TextChangeListener tcl) {
listener.remove(tcl);
}
// notify listeners
private void notifyListeners(String newCurrentString) {
if (!currentString.equals(newCurrentString)) {
currentString = newCurrentString;
for (TextChangeListener tcl : listener) {
tcl.onChange();
}
}
}
// -----------
// filter to allow only hex values in textarea and notify on change
// also remembers the current string
private class TextFilter extends DocumentFilter {
@Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
StringBuilder sb = new StringBuilder();
sb.append(fb.getDocument().getText(0, fb.getDocument().getLength()));
sb.insert(offset, text);
if(invalidContent(sb.toString())) return;
fb.insertString(offset, text, attr);
notifyListeners(sb.toString());
}
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attr) throws BadLocationException {
StringBuilder sb = new StringBuilder();
sb.append(fb.getDocument().getText(0, fb.getDocument().getLength()));
sb.replace(offset, offset + length, text);
if(invalidContent(sb.toString())) return;
fb.replace(offset, length, text, attr);
notifyListeners(sb.toString());
}
@Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
super.remove(fb, offset, length);
notifyListeners(fb.getDocument().getText(0, fb.getDocument().getLength()));
}
private boolean invalidContent(String text) {
for (int i = 0, len = text.length(); i < len; i++) {
char aChar = text.charAt(i);
int intVal = (int)aChar;
boolean contained = false;
for (int v : validChars) {
if (v == intVal) {
contained = true;
break;
}
}
if (!contained) {
return true;
}
}
// all characters are contained
return false;
}
}
}