View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Blacksmith, Inc.
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
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;  // Ctl-V
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)))  // A "key-typed" event
97        {
98           if (isValidCharacter(newChar))
99           {
100 
101             if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste))
102             {
103                // Analyze the current contents of the field
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                // Generate new string
119                if (selectionStart > 0) // Create head of test string
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))  // Add the new character
130                {
131                   testString += newChar;
132                }
133 
134                if (paste)  // Add the string from the clipboard
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                         // Do nothing
147                      }
148                      catch (UnsupportedFlavorException ufe)
149                      {
150                         // Do nothing
151                      }
152                   }
153                }
154 
155                if (selectionEnd < currentLength) // Add the tail of the string
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)  // Null string is OK
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 }