/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2000 Blacksmith, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org */ package net.wotonomy.ui.swing.components; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.KeyEvent; import javax.swing.JTextField; /** * SmartTextField is an abstract class for that allows the text field to * intelligently analyze the user's input in real-time. As the user enters * keystrokes, the generated string is analyzed to determine if the new string * is valid based on the criteria of the concrete classes that extend this * class. An invalid keystroke is rejected and not displayed in the text field. * This class can be extended to to create smart text fields that only accept * integers or floating points number or alphabetic strings of maximum length. * These are several examples. * * @author rob@straylight.princeton.com * @author $Author: cgruber $ * @version $Revision: 904 $ */ public abstract class SmartTextField extends JTextField { /******************************* * CONSTANTS *******************************/ private static final int BACKSPACE = 8; private static final int DELETE = 127; private static final int SPACE = 32; private static final int DASH = 45; private static final int UNDERSCORE = 95; private static final int PERIOD = 46; private static final int PASTE = 22; // Ctl-V /******************************* * PUBLIC METHODS *******************************/ /** * This method processes a key event. This event is generated by input from the * keyboard when this text field has the focus. This method is called for every * key that is pressed and released on the keyboard. This includes modifier keys * like the shift and alt keys. This class looks at the key and determines if * the key is valid input given the restrictions of the concrete sub-classes. *
*
* Example - A smart text field only allows alphabetic characters. If the key * pressed is a "2" then this method will determine that the key is invalid and * "consume" the key event.
*
* Note - Every printable character has a "TYPED" key event. Currentlt under * Java 1.2.1 this does not happen. Bug 4186905 relating this bug has been fixed * and is awaiting release. * * @param e A key event generated by a keyboard action. */ public void processKeyEvent(KeyEvent e) { String currentText = ""; String testString = ""; char newChar = e.getKeyChar(); int currentLength = 0; int selectionStart = 0; int selectionEnd = 0; int endOfHead = 0; int startOfTail = 0; boolean backspace = false; boolean delete = false; boolean paste = false; boolean insertionPoint = false; boolean selectionAtStart = false; boolean selectionAtEnd = false; backspace = (newChar == BACKSPACE); delete = (newChar == DELETE); paste = (newChar == PASTE); if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste))) // A "key-typed" event { if (isValidCharacter(newChar)) { if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste)) { // Analyze the current contents of the field currentText = getText(); currentLength = currentText.length(); selectionStart = getSelectionStart(); selectionEnd = getSelectionEnd(); insertionPoint = (selectionStart == selectionEnd); selectionAtStart = (selectionStart == 0); selectionAtEnd = (selectionEnd >= currentLength); if (selectionEnd > currentLength) { setSelectionEnd(currentLength); } // Generate new string if (selectionStart > 0) // Create head of test string { endOfHead = selectionStart; if (insertionPoint && backspace) { endOfHead -= 1; } testString += currentText.substring(0, endOfHead); } if (!(backspace || delete || paste)) // Add the new character { testString += newChar; } if (paste) // Add the string from the clipboard { Transferable data = getToolkit().getSystemClipboard().getContents(this); if (data != null) { try { String clipString = (String) data.getTransferData(DataFlavor.stringFlavor); testString += clipString; } catch (java.io.IOException ioe) { // Do nothing } catch (UnsupportedFlavorException ufe) { // Do nothing } } } if (selectionEnd < currentLength) // Add the tail of the string { startOfTail = selectionEnd; if (insertionPoint && delete) { startOfTail += 1; } testString += currentText.substring(startOfTail); } } if (testString.compareTo("") != 0) // Null string is OK { if (!(isValidString(testString))) { e.consume(); } } } else { e.consume(); } } super.processKeyEvent(e); postProcessing(); } /******************************* * PROTECTED METHODS *******************************/ /** * Default constructor for this class. The initial text of the smart text field * can be specified as well as the size (in characters) of the text field. * * @param text The initial string that is displayed in the text field. * @param columns THe width of the text field in characters. */ protected SmartTextField(String text, int columns) { super(text, columns); } /** * Returns whether a character is valid for this text field. As the user types * from the keyboard, this method is called to determine if the character is a * valid character based in the restrictions of the subclass. * * @param aChar A character to perform the validity test with. * @return True if the character is valid for this subclassed text field.
* False is the character is not valid. */ abstract protected boolean isValidCharacter(char aChar); /** * Returns whether a string is valid for this text field. As the user types from * the keyboard, this method is called to determine if the new string in the * text field is valid based upon the restriction of the subclass. This is done * after the character has been determined to be valid since there can be * restrictions placed on the text string as a whole, such a maximum length or * date format. * * @param aString The string to perform the validity check with. * @return True if the string is valid for this subclassed text field.
* False if the character is not valud. */ abstract protected boolean isValidString(String aString); /** * This method is used by the any subclass that need to complete any processing * of the text string in the text field after all the requirement checks have * been performed. */ abstract protected void postProcessing(); /******************************* * PRIVATE METHODS *******************************/ private boolean isPrintableCharacter(char inputChar) { if ((inputChar >= ' ') && (inputChar <= '~')) { return true; } return false; } }