/* * NumericTextField.java - A TextField that accepts only numeric values * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2008 Matthieu Casanova * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.gui; import javax.swing.*; import java.awt.Color; import java.awt.Component; import java.awt.Font; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.DocumentFilter; import javax.swing.text.DocumentFilter.FilterBypass; import org.gjt.sp.jedit.jEdit; import org.gjt.sp.jedit.syntax.SyntaxStyle; import org.gjt.sp.util.SyntaxUtilities; /** A TextField that accepts only numeric values. The numeric values may be * either integer or float values. * @author Matthieu Casanova * @version $Id: KeyEventWorkaround.java 12889 2008-06-23 20:14:00Z kpouer $ * @since jEdit 4.3pre15 */ public class NumericTextField extends JTextField implements ComboBoxEditor { private final boolean positiveOnly; private final boolean integerOnly; private Number minValue; private Number maxValue; private SyntaxStyle invalidStyle; private Color defaultBackground; private Color defaultForeground; public NumericTextField(String text) { this(text, false); } public NumericTextField(String text, boolean positiveOnly) { super(text); this.positiveOnly = positiveOnly; integerOnly = true; minValue = positiveOnly ? new Integer(0) : Integer.MIN_VALUE; maxValue = Integer.MAX_VALUE; addFilter(); loadInvalidStyle(); } public NumericTextField(String text, boolean positiveOnly, boolean integerOnly) { super(text); this.positiveOnly = positiveOnly; this.integerOnly = integerOnly; if (integerOnly) { minValue = positiveOnly ? new Integer(0) : Integer.MIN_VALUE; maxValue = Integer.MAX_VALUE; } else { minValue = positiveOnly ? new Float(0.0) : Float.MIN_VALUE; maxValue = Float.MAX_VALUE; } addFilter(); loadInvalidStyle(); } public NumericTextField(String text, int columns, boolean positiveOnly) { super(text, columns); this.positiveOnly = positiveOnly; integerOnly = true; minValue = positiveOnly ? new Integer(0) : Integer.MIN_VALUE; maxValue = Integer.MAX_VALUE; addFilter(); loadInvalidStyle(); } public NumericTextField(String text, int columns, boolean positiveOnly, boolean integerOnly) { super(text, columns); this.positiveOnly = positiveOnly; this.integerOnly = integerOnly; if (integerOnly) { minValue = positiveOnly ? new Integer(0) : Integer.MIN_VALUE; maxValue = Integer.MAX_VALUE; } else { minValue = positiveOnly ? new Float(0.0) : Float.MIN_VALUE; maxValue = Float.MAX_VALUE; } addFilter(); loadInvalidStyle(); } private void loadInvalidStyle() { Font font = getFont(); String family = font.getFamily(); int size = font.getSize(); invalidStyle = SyntaxUtilities.parseStyle(jEdit.getProperty("view.style.invalid"), family, size, true); defaultForeground = getForeground(); defaultBackground = getBackground(); } // set the minimum allowed value for this text field. If this NumericTextField // was constructed with positive only, then values less than zero are ignored. public void setMinValue(Number n) { if (positiveOnly) { float f = n.floatValue(); if (f < 0.0) return; } if (integerOnly) { int i = n.intValue(); int max = maxValue.intValue(); if (i > max) return; } else { float f = n.floatValue(); float max = maxValue.floatValue(); if (f > max) return; } minValue = n; } // set the maximum allowed value for this text field. If this NumericTextField // was constructed with positive only, then values less than zero are ignored. public void setMaxValue(Number n) { if (positiveOnly) { float f = n.floatValue(); if (f < 0) return; } if (integerOnly) { int i = n.intValue(); int min = minValue.intValue(); if (i < min) return; } else { float f = n.floatValue(); float min = minValue.floatValue(); if (f < min) return; } maxValue = n; } /** * @return The value of the text field as either an Integer or a Float, * depending on whether this text field allows Integers or Floats. */ public Number getValue() { if (integerOnly) return Integer.valueOf(getText()); else return Float.valueOf(getText()); } // add an Integer or Float document filter as appropriate private void addFilter() { if (integerOnly) ( ( AbstractDocument ) this.getDocument() ).setDocumentFilter( new IntegerDocumentFilter() ); else ( ( AbstractDocument ) this.getDocument() ).setDocumentFilter( new FloatDocumentFilter() ); // apply the filter to the current value try { String text = getText(); ((AbstractDocument)getDocument()).getDocumentFilter().replace(null, 0, text.length(), text, null); } catch(Exception e) {} // NOPMD } class IntegerDocumentFilter extends DocumentFilter { public void insertString( FilterBypass fb, int offset, String string, AttributeSet attr ) throws BadLocationException { if ( string == null || string.length() == 0 ) { return ; } String newString = new StringBuilder(getText()).insert(offset, string).toString(); if (!isInteger( newString )) { return; } setBackground(inRange( newString ) ? defaultBackground : invalidStyle.getBackgroundColor()); setForeground(inRange( newString ) ? defaultForeground : invalidStyle.getForegroundColor()); super.insertString( fb, offset, string, attr ); } public void remove( FilterBypass fb, int offset, int length ) throws BadLocationException { String newString = new StringBuilder(getText()).delete(offset, offset + length).toString(); setBackground(inRange( newString ) ? defaultBackground : invalidStyle.getBackgroundColor()); setForeground(inRange( newString ) ? defaultForeground : invalidStyle.getForegroundColor()); super.remove( fb, offset, length ); } public void replace( FilterBypass fb, int offset, int length, String text, AttributeSet attrs ) throws BadLocationException { if ( text == null || text.length() == 0 ) { return ; } String newString = new StringBuilder(getText()).replace(offset, offset + length, text).toString(); if (!isInteger( newString )) { return; } setBackground(inRange( newString ) ? defaultBackground : invalidStyle.getBackgroundColor()); setForeground(inRange( newString ) ? defaultForeground : invalidStyle.getForegroundColor()); super.replace( fb, offset, length, text, attrs ); } private boolean isInteger( String string ) { if (string == null || string.isEmpty()) { return false; } try { if (!positiveOnly && "-".equals(string)) { return true; } Integer.parseInt(string); return true; } catch(Exception e) { return false; } } private boolean inRange( String string ) { if (string == null || string.isEmpty()) { return false; } int value = Integer.parseInt( string ); return value <= maxValue.intValue() && value >= minValue.intValue(); } } class FloatDocumentFilter extends DocumentFilter { public void insertString( FilterBypass fb, int offset, String string, AttributeSet attr ) throws BadLocationException { if ( string == null || string.length() == 0 ) { return ; } String newString = new StringBuilder(getText()).insert(offset, string).toString(); if (!isFloat( newString )) { return; } setBackground(inRange( newString ) ? defaultBackground : invalidStyle.getBackgroundColor()); setForeground(inRange( newString ) ? defaultForeground : invalidStyle.getForegroundColor()); super.insertString( fb, offset, string, attr ); } public void remove( FilterBypass fb, int offset, int length ) throws BadLocationException { String newString = new StringBuilder(getText()).delete(offset, offset + length).toString(); setBackground(inRange( newString ) ? defaultBackground : invalidStyle.getBackgroundColor()); setForeground(inRange( newString ) ? defaultForeground : invalidStyle.getForegroundColor()); super.remove( fb, offset, length ); } public void replace( FilterBypass fb, int offset, int length, String text, AttributeSet attrs ) throws BadLocationException { if ( text == null || text.length() == 0 ) { return ; } String newString = new StringBuilder(getText()).replace(offset, offset + length, text).toString(); if (!isFloat( newString )) { return; } setBackground(inRange( newString ) ? defaultBackground : invalidStyle.getBackgroundColor()); setForeground(inRange( newString ) ? defaultForeground : invalidStyle.getForegroundColor()); super.replace( fb, offset, length, text, attrs ); } private boolean isFloat( String string ) { if (string == null || string.isEmpty()) { return false; } try { if (".".equals(string)) { return true; } if (!positiveOnly && "-".equals(string)) { return true; } Float.parseFloat(string); return true; } catch(Exception e) { return false; } } private boolean inRange( String string ) { if (string == null || string.isEmpty()) { return false; } if (".".equals(string)) { return true; } float value = Float.parseFloat( string ); boolean toReturn = value <= maxValue.floatValue() && value >= minValue.floatValue(); return toReturn; } } //{{{ ComboBoxEditor methods public Component getEditorComponent() { return this; } public Object getItem() { return getText(); } public void setItem(Object item) { if (item == null) setText(""); else setText(item.toString()); } //}}} }