001/* ---------------------------------------------------------------------------- 002 The Kiwi Toolkit - A Java Class Library 003 Copyright (C) 1998-2004 Mark A. Lindner 004 005 This library is free software; you can redistribute it and/or 006 modify it under the terms of the GNU General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This library is distributed in the hope that it will be useful, 011 but WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 General Public License for more details. 014 015 You should have received a copy of the GNU General Public License 016 along with this library; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 018 02111-1307, USA. 019 020 The author may be contacted at: mark_a_lindner@yahoo.com 021 ---------------------------------------------------------------------------- 022 $Log: DataField.java,v $ 023 Revision 1.10 2004/05/12 19:13:26 markl 024 comment block updates 025 026 Revision 1.9 2004/03/22 07:01:01 markl 027 javadoc corrections. 028 029 Revision 1.8 2003/01/19 09:50:53 markl 030 Javadoc & comment header updates. 031 032 Revision 1.7 2001/03/12 09:27:53 markl 033 Source code and Javadoc cleanup. 034 035 Revision 1.6 1999/10/05 03:10:48 markl 036 Documentation correction. 037 038 Revision 1.5 1999/10/04 06:22:55 markl 039 Fixed bugs, implemented checkInput() and made class concrete. 040 041 Revision 1.4 1999/07/29 06:45:19 markl 042 Changes to support length constraints. 043 044 Revision 1.3 1999/07/19 09:46:24 markl 045 More validation fixes. 046 047 Revision 1.2 1999/07/19 03:59:11 markl 048 Fixed deadlock problem. 049 050 Revision 1.1 1999/07/09 04:37:54 markl 051 Initial revision 052 ---------------------------------------------------------------------------- 053*/ 054 055package kiwi.ui; 056 057import java.awt.*; 058import java.awt.event.*; 059import javax.swing.*; 060import javax.swing.event.*; 061import javax.swing.text.*; 062 063import kiwi.event.*; 064import kiwi.ui.model.*; 065import kiwi.text.*; 066 067/** A class that implements basic functionality for a text field 068 * that places constraints on its input. 069 * <code>DataField</code> validates its input when it loses or gains focus, 070 * generates an action event, or when messaged with the 071 * <code>validateInput()</code> method. The no-op method 072 * <code>checkInput()</code> must be overridden by subclassers to perform 073 * the actual data validation. 074 * <p> 075 * Invalid input is flagged by repainting the contents of the field in red. If 076 * a key is typed into a field so highlighted, the text reverts back to black 077 * (non-flagged). Validation is not performed whenever the contents of the 078 * field change, as the necessary parsing is an expensive operation. 079 * 080 * @author Mark Lindner 081 */ 082 083public class DataField extends JTextField implements FormatConstants 084 { 085 private ChangeSupport csupport; 086 private _DocumentListener documentListener = null; 087 private boolean inputRequired = false; 088 /** A state flag for representing validation state. */ 089 protected boolean invalid = false; 090 private boolean adjusting = false; 091 092 /** Construct a new <code>DataField</code>. 093 */ 094 095 public DataField() 096 { 097 super(); 098 _init(); 099 } 100 101 /** Construct a new <code>DataField</code> with the specified width. 102 */ 103 104 public DataField(int width) 105 { 106 super(width); 107 _init(); 108 } 109 110 /* initialization */ 111 112 private void _init() 113 { 114 documentListener = new _DocumentListener(); 115 csupport = new ChangeSupport(this); 116 KDocument doc = new KDocument(); 117 setDocument(doc); 118 doc.addDocumentListener(documentListener); 119 120 addKeyListener(new KeyAdapter() 121 { 122 public void keyTyped(KeyEvent evt) 123 { 124 // we don't want to validate after each keypress; too 125 // expensive 126 127 if(invalid) 128 { 129 invalid = false; 130 paintInvalid(invalid); 131 } 132 } 133 }); 134 135 addFocusListener(new FocusAdapter() 136 { 137 public void focusLost(FocusEvent evt) 138 { 139 validateInput(); 140 } 141 142 public void focusGained(FocusEvent evt) 143 { 144 validateInput(); 145 } 146 }); 147 148 addActionListener(new ActionListener() 149 { 150 public void actionPerformed(ActionEvent evt) 151 { 152 validateInput(); 153 } 154 }); 155 } 156 157 /** Set the document model for this text field. 158 * 159 * @param doc The new document model. 160 */ 161 162 public void setDocument(Document doc) 163 { 164 super.setDocument(doc); 165 166 Document oldDoc = getDocument(); 167 168 if((documentListener != null) && (oldDoc != null)) 169 { 170 oldDoc.removeDocumentListener(documentListener); 171 doc.addDocumentListener(documentListener); 172 } 173 } 174 175 /** Add a <code>ChangeListener</code> to this component's list of listeners. 176 * <code>ChangeEvent</code>s are fired when this text field's document model 177 * changes. 178 * 179 * @param listener The listener to add. 180 */ 181 182 public void addChangeListener(ChangeListener listener) 183 { 184 csupport.addChangeListener(listener); 185 } 186 187 /** Remove a <code>ChangeListener</code> from this component's list 188 * of listeners. 189 * 190 * @param listener The listener to remove. 191 */ 192 193 public void removeChangeListener(ChangeListener listener) 194 { 195 csupport.removeChangeListener(listener); 196 } 197 198 /* document listener */ 199 200 private class _DocumentListener implements DocumentListener 201 { 202 public void changedUpdate(DocumentEvent evt) 203 { 204 _fireChange(); 205 } 206 207 public void insertUpdate(DocumentEvent evt) 208 { 209 _fireChange(); 210 } 211 212 public void removeUpdate(DocumentEvent evt) 213 { 214 _fireChange(); 215 } 216 } 217 218 /* Delay-fire a change event, but only if the current DocumentEvent is not 219 * the result of a call to setText(). 220 */ 221 222 private void _fireChange() 223 { 224 if(adjusting) 225 return; 226 227 SwingUtilities.invokeLater(new Runnable() 228 { 229 public void run() 230 { 231 csupport.fireChangeEvent(); 232 } 233 }); 234 } 235 236 /** Set the text to be displayed by this field. A <code>ChangeEvent</code> 237 * will <i>not</i> be fired when the data in the field is modified via this 238 * call. 239 * 240 * @param text The text to set. 241 */ 242 243 public final synchronized void setText(String text) 244 { 245 adjusting = true; 246 super.setText(text); 247 adjusting = false; 248 } 249 250 /** Paint the necessary decorations for the field to denote invalid (or 251 * valid) input. The default implementation sets the text color to red 252 * if the input is invalid and black otherwise. This method may be 253 * overridden by subclassers who wish to customize the method of visual 254 * feedback. 255 * 256 * @param invalid A flag specifying whether the input in the field is 257 * currently valid or invalid. 258 */ 259 260 protected void paintInvalid(boolean invalid) 261 { 262 setForeground(invalid ? Color.red : Color.black); 263 } 264 265 /** Set the editable state of this field. 266 * 267 * @param flag A flag specifying whether this field should be editable. 268 * Non-editable fields are made transparent. 269 */ 270 271 public void setEditable(boolean flag) 272 { 273 super.setEditable(flag); 274 setOpaque(flag); 275 } 276 277 /** Specify whether an input is required in this field. If no input is 278 * required, the <code>validateInput()</code> method will return 279 * <code>true</code> if the field is left empty; otherwise it will return 280 * <code>false</code>. 281 * 282 * @param flag The flag. 283 * @see #validateInput 284 * @see #isInputRequired 285 */ 286 287 public void setInputRequired(boolean flag) 288 { 289 inputRequired = flag; 290 } 291 292 /** Determine if input is required in this field. 293 * 294 * @return <code>true</code> if input is required in this field, and 295 * <code>false</code> 296 * otherwise. 297 */ 298 299 public boolean isInputRequired() 300 { 301 return(inputRequired); 302 } 303 304 /** Validate the input in this field. 305 * 306 * @return <code>true</code> if the field contains valid input or if the 307 * field contains no input and input is not required, and <code>false</code> 308 * otherwise. 309 */ 310 311 public final boolean validateInput() 312 { 313 String s = getText().trim(); 314 315 if(s.length() == 0) 316 return(!isInputRequired()); 317 318 return(checkInput()); 319 } 320 321 /** Determine if the given input is valid for this field. The default 322 * implementation returns <code>true</code>. 323 * 324 * @return <code>true</code> if the input is valid, and <code>false</code> 325 * otherwise. 326 */ 327 328 protected boolean checkInput() 329 { 330 return(true); 331 } 332 333 /** Set the maximum number of characters that may be entered into this field. 334 * This method will have no effect if the document has been changed from 335 * a <code>KDocument</code> via a call to <code>setDocument()</code>. 336 * 337 * @param length The new maximum length, or <code>KDocument.NO_LIMIT</code> 338 * for unlimited length. 339 * @see kiwi.ui.model.KDocument 340 */ 341 342 public void setMaximumLength(int length) 343 { 344 Document d = getDocument(); 345 if(d instanceof KDocument) 346 ((KDocument)d).setMaximumLength(length); 347 } 348 349 /** Get the maxmium number of characters that may be entered into this field. 350 * 351 * @return The maximum length, or <code>KDocument.NO_LIMIT</code> if there 352 * is no limit. 353 */ 354 355 public int getMaximumLength() 356 { 357 Document d = getDocument(); 358 if(d instanceof KDocument) 359 return(((KDocument)d).getMaximumLength()); 360 else 361 return(KDocument.NO_LIMIT); 362 } 363 364 } 365 366/* end of source file */