Altered so that document events are never fired while the document contains

an invalid value. This was made extra hairy by the fact that the
FilterBypass installed on a Document implements replace() by simply
calling remove() and then insert(). That's fine and dandy and saves lots of
code, I'm sure, but the each of them fires an event, resulting in two events
when a replace() happens.

I Created a special Document that suppresses 'remove' events from firing.
Not a problem, because I've also structured the filter such that removes()
end up being implemented as a replace() (due to the filtering and
processing that we do to ensure that valid, properly formatted integers
are always displayed) so the 'insert' event will take care of all our needs.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@1585 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
ray
2005-02-03 00:54:57 +00:00
parent 3efec4861b
commit 7f5640db80
@@ -10,10 +10,12 @@ import java.text.ParseException;
import javax.swing.JTextField;
import javax.swing.text.AbstractDocument;
import javax.swing.event.DocumentEvent;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;
import com.samskivert.Log;
import com.samskivert.swing.util.SwingUtil;
@@ -56,9 +58,9 @@ public class IntField extends JTextField
*/
public IntField (int initial, int minValue, int maxValue)
{
if (minValue < 0) {
throw new IllegalArgumentException("minValue must be at least 0");
}
super(new IntDocument(), format(initial), 5);
validateMinMax(minValue, maxValue);
if (initial > maxValue || initial < minValue) {
throw new IllegalArgumentException("initial value not between " +
"min and max");
@@ -67,13 +69,12 @@ public class IntField extends JTextField
_maxValue = maxValue;
setHorizontalAlignment(JTextField.RIGHT);
setColumns(5);
// create a document filter which enforces the restrictions on
// what text may be entered. This is similar to the filter
// that is configured in SwingUtil.setDocumentHelpers, only the
// remove operation is modified to do the sneaky highlighting we do.
final AbstractDocument doc = (AbstractDocument) getDocument();
final IntDocument doc = (IntDocument) getDocument();
doc.setDocumentFilter(new DocumentFilter() {
public void remove (FilterBypass fb, int offset, int length)
throws BadLocationException
@@ -83,8 +84,7 @@ public class IntField extends JTextField
current.substring(offset + length);
// if the bit to be removed is still a valid value, go ahead
if (unformatSafe(potential) >= _minValue) {
fb.remove(offset, length);
transform(fb);
transform(fb, current, potential);
return;
}
@@ -121,27 +121,35 @@ public class IntField extends JTextField
String s, AttributeSet attr)
throws BadLocationException
{
fb.insertString(offset, s, attr);
transform(fb);
String current = doc.getText(0, doc.getLength());
String potential = current.substring(0, offset) + s +
current.substring(offset);
transform(fb, current, potential);
}
public void replace (FilterBypass fb, int offset, int length,
String text, AttributeSet attrs)
throws BadLocationException
{
fb.replace(offset, length, text, attrs);
transform(fb);
String current = doc.getText(0, doc.getLength());
String potential = current.substring(0, offset) + text +
current.substring(offset + length);
transform(fb, current, potential);
}
protected void transform (FilterBypass fb)
protected void transform (FilterBypass fb,
String current, String potential)
throws BadLocationException
{
try {
String text = doc.getText(0, doc.getLength());
String xform = transform(text);
if (!text.equals(xform)) {
fb.replace(0, text.length(), xform, null);
}
} catch (BadLocationException ble) {
boolean wouldaBeenEqual = current.equals(potential);
potential = transform(potential);
// we only change it if it needs changing
if (!current.equals(potential) ||
// or if it would have been the same pre-transforming
// and there is a selection (IE undo the selection)
(wouldaBeenEqual &&
(getSelectionEnd() != getSelectionStart()))) {
fb.replace(0, doc.getLength(), potential, null);
}
}
@@ -205,9 +213,20 @@ public class IntField extends JTextField
return newText;
}
});
}
// and set the initial value
setValue(initial);
/**
* Validate min/max.
*/
protected void validateMinMax (int minValue, int maxValue)
{
if (minValue < 0) {
throw new IllegalArgumentException("minValue must be at least 0");
}
if (maxValue < minValue) {
throw new IllegalArgumentException(
"maxValue must be greater than minValue");
}
}
/**
@@ -231,9 +250,7 @@ public class IntField extends JTextField
*/
public void setMinValue (int minValue)
{
if (minValue < 0) {
throw new IllegalArgumentException("minValue must be at least 0");
}
validateMinMax(minValue, _maxValue);
_minValue = minValue;
validateText();
}
@@ -243,6 +260,7 @@ public class IntField extends JTextField
*/
public void setMaxValue (int maxValue)
{
validateMinMax(_minValue, maxValue);
_maxValue = maxValue;
validateText();
}
@@ -286,6 +304,23 @@ public class IntField extends JTextField
}
}
/**
* Our own special Document class.
*/
protected static class IntDocument extends PlainDocument
{
// documentation inherited
protected void fireRemoveUpdate (DocumentEvent e)
{
// suppress: the DocumentFilter implements replace by doing
// a remove and then an insert. We do not want listeners to ever
// be notified when the document contains invalid text.
// (Gosh, it'd be nice if we didn't have to hack this, but
// the standard implementation has a bunch of methods with
// package-protected access. Thanks Sun!)
}
}
/** min/max */
protected int _minValue, _maxValue;