package com.vitco.layout.content.colorchooser.components;
import com.vitco.layout.content.colorchooser.basic.Settings;
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.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Text-field that only allows numbers and has an easy way to retrieve the current one
* also allows for notification listen (and change "onChange")
*/
public class NumberBox extends JTextField {
// setter that does not notify the listeners
public void setValueWithoutRefresh(int value) {
int croppedValue = cropValue(value);
if (getValue() != croppedValue) {
blockNotify = true;
super.setText(String.valueOf(croppedValue));
blockNotify = false;
}
}
// the current string used
private String currentString = "";
// holds the listeners
private final ArrayList<TextChangeListener> listener = new ArrayList<TextChangeListener>();
// add a listener
public final void addTextChangeListener(TextChangeListener tcl) {
listener.add(tcl);
}
// notify listeners
private boolean blockNotify = false;
private void notifyListeners() {
if (!blockNotify) {
for (TextChangeListener tcl : listener) {
tcl.onChange();
}
}
}
// filter to allow only numbers in textarea and notify on change
// also remembers the current string
private class AxisJTextFilter extends DocumentFilter {
@Override
public void insertString(DocumentFilter.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);
currentString = sb.toString();
notifyListeners();
}
@Override
public void replace(DocumentFilter.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);
currentString = sb.toString();
notifyListeners();
}
@Override
public void remove(DocumentFilter.FilterBypass fb, int offset, int length) throws BadLocationException
{
super.remove(fb, offset, length);
currentString = fb.getDocument().getText(0, fb.getDocument().getLength());
notifyListeners();
}
private final Pattern pattern = Pattern.compile("\\d{0," + (String.valueOf(MAX).length() + 1) + "}?");
public boolean invalidContent(String text)
{
Matcher matcher = pattern.matcher(text);
boolean isMatch = matcher.matches();
return !text.equals("") && !isMatch;
}
}
// link to this field (for nested reference)
private final NumberBox thisField = this;
// conversion helper
private int cropValue(int value) {
return Math.min(MAX,Math.max(MIN,value));
}
// get value (range MIN-MAX)
public final int getValue() {
int result = MIN;
try {
result = cropValue(Integer.valueOf(currentString));
} catch (NumberFormatException ignored) {}
return result;
}
private final int MIN;
private final int MAX;
// constructor
public NumberBox(int min, int max, int current) {
super(String.valueOf(current), String.valueOf(max).length());
MIN = min;
MAX = max;
currentString = String.valueOf(current);
setForeground(Settings.TEXTAREA_TEXT_COLOR);
setOpaque(true);
setBackground(Settings.TEXTAREA_BG_COLOR);
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Settings.TEXTAREA_BORDER_COLOR),
BorderFactory.createEmptyBorder(0, 3, 0, 3)
));
((AbstractDocument)this.getDocument()).setDocumentFilter(new AxisJTextFilter());
// handle highlight on focus
this.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
setForeground(Settings.TEXTAREA_TEXT_COLOR_FOCUS);
setBackground(Settings.TEXTAREA_BG_COLOR_FOCUS);
}
@Override
public void focusLost(FocusEvent e) {
thisField.setText(String.valueOf(getValue()));
setForeground(Settings.TEXTAREA_TEXT_COLOR);
setBackground(Settings.TEXTAREA_BG_COLOR);
}
});
// handle update on return (only visual)
this.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10) {
thisField.setText(String.valueOf(getValue()));
// remove focus from this component
thisField.transferFocusBackward();
}
}
});
}
}