1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.ui.swing.components;
20
21 import java.awt.datatransfer.DataFlavor;
22 import java.awt.datatransfer.Transferable;
23 import java.awt.datatransfer.UnsupportedFlavorException;
24 import java.awt.event.KeyEvent;
25
26 import javax.swing.JTextField;
27
28 /***
29 * SmartTextField is an abstract class for that allows the text field to
30 * intelligently analyze the user's input in real-time. As the user enters
31 * keystrokes, the generated string is analyzed to determine if the new string
32 * is valid based on the criteria of the concrete classes that extend this
33 * class. An invalid keystroke is rejected and not displayed in the text
34 * field. This class can be extended to to create smart text fields that only
35 * accept integers or floating points number or alphabetic strings of maximum
36 * length. These are several examples.
37 *
38 * @author rob@straylight.princeton.com
39 * @author $Author: cgruber $
40 * @version $Revision: 904 $
41 */
42 public abstract class SmartTextField extends JTextField
43 {
44
45 /********************************
46 * CONSTANTS
47 *******************************/
48 private static final int BACKSPACE = 8;
49 private static final int DELETE = 127;
50 private static final int SPACE = 32;
51 private static final int DASH = 45;
52 private static final int UNDERSCORE = 95;
53 private static final int PERIOD = 46;
54 private static final int PASTE = 22;
55
56
57 /********************************
58 * PUBLIC METHODS
59 *******************************/
60
61 /***
62 * This method processes a key event. This event is generated by input from the
63 * keyboard when this text field has the focus. This method is called for every
64 * key that is pressed and released on the keyboard. This includes modifier
65 * keys like the shift and alt keys. This class looks at the key and determines
66 * if the key is valid input given the restrictions of the concrete sub-classes. <BR> <BR>
67 * Example - A smart text field only allows alphabetic characters. If the key
68 * pressed is a "2" then this method will determine that the key is invalid and
69 * "consume" the key event. <BR> <BR>
70 * Note - Every printable character has a "TYPED" key event. Currentlt under
71 * Java 1.2.1 this does not happen. Bug 4186905 relating this bug has been
72 * fixed and is awaiting release.
73 * @param e A key event generated by a keyboard action.
74 */
75 public void processKeyEvent(KeyEvent e)
76 {
77 String currentText = "";
78 String testString = "";
79 char newChar = e.getKeyChar();
80 int currentLength = 0;
81 int selectionStart = 0;
82 int selectionEnd = 0;
83 int endOfHead = 0;
84 int startOfTail = 0;
85 boolean backspace = false;
86 boolean delete = false;
87 boolean paste = false;
88 boolean insertionPoint = false;
89 boolean selectionAtStart = false;
90 boolean selectionAtEnd = false;
91
92 backspace = (newChar == BACKSPACE);
93 delete = (newChar == DELETE);
94 paste = (newChar == PASTE);
95
96 if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste)))
97 {
98 if (isValidCharacter(newChar))
99 {
100
101 if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste))
102 {
103
104 currentText = getText();
105 currentLength = currentText.length();
106
107 selectionStart = getSelectionStart();
108 selectionEnd = getSelectionEnd();
109
110 insertionPoint = (selectionStart == selectionEnd);
111 selectionAtStart = (selectionStart == 0);
112 selectionAtEnd = (selectionEnd >= currentLength);
113 if (selectionEnd > currentLength)
114 {
115 setSelectionEnd(currentLength);
116 }
117
118
119 if (selectionStart > 0)
120 {
121 endOfHead = selectionStart;
122 if (insertionPoint && backspace)
123 {
124 endOfHead -= 1;
125 }
126 testString += currentText.substring(0, endOfHead);
127 }
128
129 if (!(backspace || delete || paste))
130 {
131 testString += newChar;
132 }
133
134 if (paste)
135 {
136 Transferable data = getToolkit().getSystemClipboard().getContents(this);
137 if (data != null)
138 {
139 try
140 {
141 String clipString = (String)data.getTransferData(DataFlavor.stringFlavor);
142 testString += clipString;
143 }
144 catch (java.io.IOException ioe)
145 {
146
147 }
148 catch (UnsupportedFlavorException ufe)
149 {
150
151 }
152 }
153 }
154
155 if (selectionEnd < currentLength)
156 {
157 startOfTail = selectionEnd;
158 if (insertionPoint && delete)
159 {
160 startOfTail += 1;
161 }
162 testString += currentText.substring(startOfTail);
163 }
164
165 }
166
167 if (testString.compareTo("") != 0)
168 {
169 if (!(isValidString(testString)))
170 {
171 e.consume();
172 }
173 }
174 }
175 else
176 {
177 e.consume();
178 }
179 }
180 super.processKeyEvent(e);
181
182 postProcessing();
183 }
184
185
186 /********************************
187 * PROTECTED METHODS
188 *******************************/
189
190 /***
191 * Default constructor for this class. The initial text of the smart text field
192 * can be specified as well as the size (in characters) of the text field.
193 * @param text The initial string that is displayed in the text field.
194 * @param columns THe width of the text field in characters.
195 */
196 protected SmartTextField(String text, int columns)
197 {
198 super(text, columns);
199 }
200
201 /***
202 * Returns whether a character is valid for this text field. As the user types
203 * from the keyboard, this method is called to determine if the character is a
204 * valid character based in the restrictions of the subclass.
205 * @param aChar A character to perform the validity test with.
206 * @return True if the character is valid for this subclassed text field. <BR>
207 * False is the character is not valid.
208 */
209 abstract protected boolean isValidCharacter(char aChar);
210
211 /***
212 * Returns whether a string is valid for this text field. As the user types from
213 * the keyboard, this method is called to determine if the new string in the text
214 * field is valid based upon the restriction of the subclass. This is done after
215 * the character has been determined to be valid since there can be restrictions
216 * placed on the text string as a whole, such a maximum length or date format.
217 * @param aString The string to perform the validity check with.
218 * @return True if the string is valid for this subclassed text field. <BR>
219 * False if the character is not valud.
220 */
221 abstract protected boolean isValidString(String aString);
222
223 /***
224 * This method is used by the any subclass that need to complete any processing
225 * of the text string in the text field after all the requirement checks have
226 * been performed.
227 */
228 abstract protected void postProcessing();
229
230
231 /********************************
232 * PRIVATE METHODS
233 *******************************/
234
235 private boolean isPrintableCharacter(char inputChar)
236 {
237 if ((inputChar >= ' ') && (inputChar <= '~'))
238 {
239 return true;
240 }
241 return false;
242 }
243
244 }