//package org.shodor.gui11; import java.awt.*; import java.awt.event.*; /** NumInput -- Note: NumInput is not fully Java 1.1 compatible. It still uses handleEvent() to filter out undesirable input because I couldn't figure out how to do what I wanted with the 1.1 event-handling methods. This class implements a uniform way to retrieve floating point numbers or integers from a TextField, and to allow tab and enter keys to move between textboxes in a predictable way, even in browsers which don't support that behavior by default.

To take advantage of the tab/enter feature, you must give each implementation of NumInput a pointer to the next NumInput to jump to, either in the constructor or by calling setNext().

It is possible to set a NumInput to expect only input of a certain type by specifying the type of numbers to expect in the constructor. The number types are:
INTEGER Positive or negative integers (no decimal places)
POSINTEGER Positive integers only
NEGINTEGER Negative integers only
DOUBLE Positive or negative floating point numbers
POSDOUBLE Positive floating point numbers only
NEGDOUBLE Negative floating point numbers only

If no type is specified, DOUBLE is assumed. NumInput will filter out all characters that don't fit belong in properly formatted numbers of the given type. Alphanumeric characters are always ignored. Multiple decimal points, negative signs, and positive signs are disallowed. For the integer types, decimal points are disallowed. For the positive types negative signs are disallowed and vice versa for the negative types.

For the integer types NumInput also tries to prevent strings from getting too long. It will not let the text grow longer than the string "+" + Integer.MAX_VALUE.

NumInput implements two methods for getting numbers: int getInt() and double getDouble(). If the string in the NumInput is not a correctly formatted int or double, these methods throw a "NumberFormatException," which should be handled by the calling code. As a side effect, getInt() and getDouble() copy the displayed text back to the NumInput by calling setText(getText()). This causes any unwanted hidden characters (such as the phantom question marks on the mac) to show up so that the user can deal with them.

When tab or enter is pressed in a NumInput, NumInput posts an event with id = INPUT_EVENT. Applets can handle this event to find out when data in a NumInput has been updated. @author Alton Patrick @version August 2001 */ public class NumInput extends TextField{ private static int DELKEY = 127; private static int BSKEY = 8; public static int INTEGER = 1; public static int POSINTEGER = 2; public static int NEGINTEGER = 3; public static int DOUBLE = 4; public static int POSDOUBLE = 5; public static int NEGDOUBLE = 6; public static int INPUT_EVENT = -100; NumInput next, prev; private int type; /** Constructor that allows setting a type. @param text initial text to display @param columns width of text field @param t determines what type of data is handled. Can be NumInput.DOUBLE, NumInput.POSDOUBLE, NumInput.NEGDOUBLE, NumInput.INTEGER, NumInput.POSINTEGER, or NumInput.NEGINTEGER. @param NumInput to move to when tab or enter is pressed */ public NumInput(String text, int columns, int t, NumInput n){ super(text, columns); type = t; next = n; } /** Constructor that allows setting a type. @param text initial text to display @param columns width of text field @param t determines what type of data is handled. Can be NumInput.DOUBLE, NumInput.POSDOUBLE, NumInput.NEGDOUBLE, NumInput.INTEGER, NumInput.POSINTEGER, or NumInput.NEGINTEGER. */ public NumInput(String text, int columns, int t){ this(text, columns, t, null); } /** Constructor that allows setting a type. @param columns width of text field @param t determines what type of data is handled. Can be NumInput.DOUBLE, NumInput.POSDOUBLE, NumInput.NEGDOUBLE, NumInput.INTEGER, NumInput.POSINTEGER, or NumInput.NEGINTEGER. @param NumInput to move to when tab or enter is pressed */ public NumInput(int columns, int t, NumInput n){ this("", columns, t, n); } /** Constructor that allows setting a type. @param columns width of text field @param t determines what type of data is handled. Can be NumInput.DOUBLE, NumInput.POSDOUBLE, NumInput.NEGDOUBLE, NumInput.INTEGER, NumInput.POSINTEGER, or NumInput.NEGINTEGER. */ public NumInput(int columns, int t){ this("", columns, t, null); } /** Constructor. @param text initial text to display in the TextField @param columns width of the TextField, in characters @param n NumInput to move to when tab or enter is pressed */ public NumInput(String text, int columns, NumInput n){ this(text, columns, DOUBLE, n); } /** Constructor. @param text initial text to display in the TextField @param columns width of the TextField, in characters */ public NumInput(String text, int columns){ this(text, columns, DOUBLE, null); } /** Constructor. @param text initial text to display in the TextField @param n NumInput to move to when tab or enter is pressed */ public NumInput(String text, NumInput n){ this(text, text.length(), DOUBLE, n); } /** Constructor. @param columns width of the TextField, in characters @param n NumInput to move to when tab or enter is pressed */ public NumInput(int columns, NumInput n){ this("", columns, DOUBLE, n); } /** Constructor. @param text initial text to display in the TextField */ public NumInput(String text){ this(text, text.length(), DOUBLE, null); } /** Constructor. @param columns width of the TextField, in characters */ public NumInput(int columns){ this("", columns, DOUBLE, null); } /** Sets the next NumInput. @param n NumInput to move to when tab or enter is pressed */ public void setNext(NumInput n){ next = n; } /** Sets the previous NumInput. @param p NumInput to move to when shift + tab or enter is pressed */ public void setPrev(NumInput p){ prev = p; } /** Attempts to get a double from the TextField. @return the number in the TextField */ public double getDouble() throws NumberFormatException{ setText(getText()); // Netscape 4.x will not throw a NumberFormatException for an empty string. if(getText().length() == 0) throw(new NumberFormatException()); return((new Double(getText())).doubleValue()); } /** Attempts to get an integer from the TextField. @return the number in the TextField */ public int getInt() throws NumberFormatException{ setText(getText()); String s = getText(); s.trim(); // For some reason Java doesn't like '+' at the front of an integer, // so I'll get rid of it. if(s.length() > 0) if(s.charAt(0) == '+') s = s.substring(1); setText(s); return((new Integer(getText())).intValue()); } /** Event handler. */ public boolean handleEvent(Event evt){ if(evt.id == Event.KEY_PRESS){ // Handle tab or enter if(evt.key == '\t' || evt.key == '\n'){ processActionEvent(new ActionEvent(this, INPUT_EVENT, "")); if(evt.shiftDown()){ if(prev != null){ prev.requestFocus(); } } else{ if(next != null){ next.requestFocus(); } } return(false); } // Handle other keystrokes boolean keyOK = false; // Digits, backspace, and delete are always ok if((evt.key >= '0' && evt.key <= '9') || evt.key == DELKEY || evt.key == BSKEY) keyOK = true; // Decimal points else if(evt.key == '.'){ if(type == DOUBLE || type == POSDOUBLE || type == NEGDOUBLE){ if(getText().indexOf('.') == -1) keyOK = true; else if(getSelectedText().indexOf('.') != -1) keyOK = true; } } // Positive signs else if(evt.key == '+'){ String s = getText(); if(type == INTEGER || type == POSINTEGER || type == DOUBLE || type == POSDOUBLE){ if(s.indexOf('+') == -1 && s.indexOf('-') == -1) keyOK = true; else if(getSelectedText().indexOf('+') != -1 || getSelectedText().indexOf('-') != -1) keyOK = true; } } // Negative signs else if(evt.key == '-'){ String s = getText(); if(type == INTEGER || type == NEGINTEGER || type == DOUBLE || type == NEGDOUBLE){ if(s.indexOf('+') == -1 && s.indexOf('-') == -1) keyOK = true; else if(getSelectedText().indexOf('+') != -1 || getSelectedText().indexOf('-') != -1) keyOK = true; } } // Try to keep string from getting too long if(type == INTEGER || type == POSINTEGER || type == NEGINTEGER){ String longestInt = new String("+" + Integer.MAX_VALUE); if((getText().length() - getSelectedText().length() >= longestInt.length()) && evt.key != BSKEY && evt.key != DELKEY) keyOK = false; } if(keyOK) return(super.handleEvent(evt)); else return(true); } else if(evt.id == Event.GOT_FOCUS){ selectAll(); return(true); } else if(evt.id == Event.LOST_FOCUS){ select(0,0); return(true); } else return(super.handleEvent(evt)); } }