001 package util.swing;
002
003 import util.swing.InputFilter;
004 import java.awt.event.*;
005 import javax.swing.JTextField;
006 import javax.swing.text.*;
007
008
009 /**
010 * An input field that lets you easily set up input filters.
011 *
012 * <p>This class derives from JTextField and adds support for the {@link InputFilter}
013 * interface. This way you can validate input much more easily.</p>
014 *
015 * <p>The filters can react when the input content is changed and when the user exits
016 * the control. So, e.g. you can check while editing that it is an integer and when
017 * leaving check for correct input bounds. Doing so already when typing is quite
018 * problematic since not all number ranges can then be covered. </p>
019 *
020 * <p><b>Example</b> for using JFilterInput: This will allow only binary numbers for
021 * input.
022 * <br/><br/>
023 * <tt> JFilterInput jfiBinary = new JFilterInput(new InputFilter() {<br/>
024 * public boolean allowEditing(String value) {<br/>
025 * return !value.matches(".*[^01]+.*");<br/>
026 * }<br/>
027 * public String validExit(String value) {<br/>
028 * return value;<br/> }<br/>
029 * });</tt></p>
030 *
031 * @author Thomas Ryssel
032 * @version 3.2
033 * @since 3.2 2006-03-18
034 */
035 public class JFilterInput extends JTextField {
036
037 /**
038 * ID for serialization.
039 */
040 private static final long serialVersionUID = -6448548193991908200L;
041
042 /**
043 * The filter used for this input. If set to <code>null</code>, no filter
044 * will be applied.
045 */
046 private InputFilter m_ifFilter = null;
047
048 /**
049 * Internal flag indicating that changes will be allowed without any
050 * conditions.
051 */
052 private boolean m_bForceAllow = false;
053
054 /**
055 * Create a JFilterInput without a filter.
056 *
057 * <p>This will then behave like a normal JTextField.
058 * </p>
059 */
060 public JFilterInput() {
061 this("", JTextField.LEFT, null);
062 }
063
064 /**
065 * Create a JFilterInput with the given filter.
066 *
067 * @param filter The filter to apply on this.
068 */
069 public JFilterInput(InputFilter filter) {
070 this("", JTextField.LEFT, filter);
071 }
072
073 /**
074 * Create a JFilterInput with the given parameters.
075 *
076 * @param text The default text.
077 * @param type The input type (text alignment).
078 * @param filter The filter to apply on this.
079 */
080 public JFilterInput(String text, int type, InputFilter filter) {
081 super(text, type);
082 m_ifFilter = filter;
083 addFocusListener(new FocusListener() {
084 public void focusGained(FocusEvent e) {
085 // does nothing at the current implementation.
086 }
087
088 public void focusLost(FocusEvent e) {
089 if (m_ifFilter != null) {
090 try {
091 boolean oldForceAllow = m_bForceAllow;
092 m_bForceAllow = true;
093 setText(m_ifFilter.validExit(getText(0, getDocument().getLength())));
094 m_bForceAllow = oldForceAllow;
095 } catch (BadLocationException ble) { }
096 }
097
098 }
099 });
100 }
101
102 /**
103 * Set the InputFilter that should be used from now on.
104 *
105 * <p><b>Note:</b> This will simply set the new filter reference. The input
106 * already given will be in no way processed or validated.</p>
107 *
108 * @param filter
109 */
110 public void setFilter(InputFilter filter) {
111 m_ifFilter = filter;
112 }
113
114 /**
115 * Create and return the input fields document.
116 */
117 protected Document createDefaultModel() {
118 return new PlainDocument() {
119 private static final long serialVersionUID = 3257289110602724153L;
120 public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
121 String sText = getText(0, getLength());
122 if (sText == null) {
123 sText = "";
124 offs = 0;
125 }
126 sText = new StringBuffer(sText).insert(offs, str).toString();
127
128 if (m_bForceAllow || m_ifFilter == null || m_ifFilter.allowEditing(sText)) {
129 super.insertString(offs, str, a);
130 }
131 }
132
133 public void remove(int offs, int len) throws BadLocationException {
134 String sText = getText(0, getLength());
135
136 if (sText == null) {
137 return;
138 }
139
140 sText = new StringBuffer(sText).delete(offs, offs + len).toString();
141 if (m_bForceAllow || m_ifFilter == null || m_ifFilter.allowEditing(sText)) {
142 super.remove(offs, len);
143 }
144 }
145 };
146 }
147
148 /**
149 * Enable or disable the filter. If the filter is disabled, every kind of input
150 * will be accepted. This is mainly for internal use in cases it's useful.
151 *
152 * @param en Whether (<code>true</code>) or not (<code>false</code>) to enable
153 * the filter.
154 */
155 protected void setFilterEnabled(boolean en) {
156 m_bForceAllow = !en;
157 }
158 }