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.JTextField;
import javax.swing.text.AbstractDocument; import javax.swing.event.DocumentEvent;
import javax.swing.text.AttributeSet; import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException; import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter; import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;
import com.samskivert.Log; import com.samskivert.Log;
import com.samskivert.swing.util.SwingUtil; import com.samskivert.swing.util.SwingUtil;
@@ -56,9 +58,9 @@ public class IntField extends JTextField
*/ */
public IntField (int initial, int minValue, int maxValue) public IntField (int initial, int minValue, int maxValue)
{ {
if (minValue < 0) { super(new IntDocument(), format(initial), 5);
throw new IllegalArgumentException("minValue must be at least 0");
} validateMinMax(minValue, maxValue);
if (initial > maxValue || initial < minValue) { if (initial > maxValue || initial < minValue) {
throw new IllegalArgumentException("initial value not between " + throw new IllegalArgumentException("initial value not between " +
"min and max"); "min and max");
@@ -67,13 +69,12 @@ public class IntField extends JTextField
_maxValue = maxValue; _maxValue = maxValue;
setHorizontalAlignment(JTextField.RIGHT); setHorizontalAlignment(JTextField.RIGHT);
setColumns(5);
// create a document filter which enforces the restrictions on // create a document filter which enforces the restrictions on
// what text may be entered. This is similar to the filter // what text may be entered. This is similar to the filter
// that is configured in SwingUtil.setDocumentHelpers, only the // that is configured in SwingUtil.setDocumentHelpers, only the
// remove operation is modified to do the sneaky highlighting we do. // 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() { doc.setDocumentFilter(new DocumentFilter() {
public void remove (FilterBypass fb, int offset, int length) public void remove (FilterBypass fb, int offset, int length)
throws BadLocationException throws BadLocationException
@@ -83,8 +84,7 @@ public class IntField extends JTextField
current.substring(offset + length); current.substring(offset + length);
// if the bit to be removed is still a valid value, go ahead // if the bit to be removed is still a valid value, go ahead
if (unformatSafe(potential) >= _minValue) { if (unformatSafe(potential) >= _minValue) {
fb.remove(offset, length); transform(fb, current, potential);
transform(fb);
return; return;
} }
@@ -121,27 +121,35 @@ public class IntField extends JTextField
String s, AttributeSet attr) String s, AttributeSet attr)
throws BadLocationException throws BadLocationException
{ {
fb.insertString(offset, s, attr); String current = doc.getText(0, doc.getLength());
transform(fb); String potential = current.substring(0, offset) + s +
current.substring(offset);
transform(fb, current, potential);
} }
public void replace (FilterBypass fb, int offset, int length, public void replace (FilterBypass fb, int offset, int length,
String text, AttributeSet attrs) String text, AttributeSet attrs)
throws BadLocationException throws BadLocationException
{ {
fb.replace(offset, length, text, attrs); String current = doc.getText(0, doc.getLength());
transform(fb); 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 { boolean wouldaBeenEqual = current.equals(potential);
String text = doc.getText(0, doc.getLength()); potential = transform(potential);
String xform = transform(text); // we only change it if it needs changing
if (!text.equals(xform)) { if (!current.equals(potential) ||
fb.replace(0, text.length(), xform, null); // or if it would have been the same pre-transforming
} // and there is a selection (IE undo the selection)
} catch (BadLocationException ble) { (wouldaBeenEqual &&
(getSelectionEnd() != getSelectionStart()))) {
fb.replace(0, doc.getLength(), potential, null);
} }
} }
@@ -205,9 +213,20 @@ public class IntField extends JTextField
return newText; 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) public void setMinValue (int minValue)
{ {
if (minValue < 0) { validateMinMax(minValue, _maxValue);
throw new IllegalArgumentException("minValue must be at least 0");
}
_minValue = minValue; _minValue = minValue;
validateText(); validateText();
} }
@@ -243,6 +260,7 @@ public class IntField extends JTextField
*/ */
public void setMaxValue (int maxValue) public void setMaxValue (int maxValue)
{ {
validateMinMax(_minValue, maxValue);
_maxValue = maxValue; _maxValue = maxValue;
validateText(); 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 */ /** min/max */
protected int _minValue, _maxValue; protected int _minValue, _maxValue;