1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
|
/*
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.util;
import java.awt.Component;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
/**
* This class will actively check the inputs of 2 numbers in seperate text
* components. The number in the text components represent an upper and lower
* bound to some range. This class checks to make sure the user inputs values in
* the lower bound text field that are less than the value of the upper bound
* and vice versa for the upper bound text field. This class will also check to
* make sure the bounds fall within a given range if specified.
*
* The checks are automatically performed when the focus is lost on either
* component. If the inputs are correct then no event occurs. If the inputs are
* not correct, then a dialog message is displayed stating the reason why the
* bounds are invalid, and the original correct value is restored into the text
* components.
*
* @author rglista
* @author $Author: cgruber $
* @version $Revision: 904 $
*/
public class TextInputRangeChecker implements FocusListener {
protected static final int NONE = 0;
protected static final int LOWER = 1;
protected static final int UPPER = 2;
private JTextComponent lowerComponent;
private JTextComponent upperComponent;
private double maxRange;
private double lowerNumber;
private double upperNumber;
private Collection focusListeners;
private String invalidLowerMessage;
private String invalidUpperMessage;
private String invalidEitherMessage;
private String invalidRangeMessage;
/**
* Constructor with some of the settable parameters. No range checking is used.
*
* @param aLowerTextComponent A text component for the lower bound.
* @param anUpperTextComponent A text component for the upper bound.
*/
public TextInputRangeChecker(JTextComponent aLowerTextComponent, JTextComponent anUpperTextComponent) {
this(aLowerTextComponent, anUpperTextComponent, null, null, 0.0);
}
/**
* Constructor with some of the settable parameters. No range checking is used.
*
* @param aLowerTextComponent A text component for the lower bound.
* @param anUpperTextComponent A text component for the upper bound.
* @param lowerTextName The name of the lower bound, eg - start year.
* @param upperTextName The name of the upper bound, eg - end year. is
* used.
*/
public TextInputRangeChecker(JTextComponent aLowerTextComponent, JTextComponent anUpperTextComponent,
String lowerTextName, String upperTextName) {
this(aLowerTextComponent, anUpperTextComponent, lowerTextName, upperTextName, 0.0);
}
/**
* Constructor with some of the settable parameters.
*
* @param aLowerTextComponent A text component for the lower bound.
* @param anUpperTextComponent A text component for the upper bound.
* @param aMaxRange The range the bounds muist fall between, if 0
* then no range is used.
*/
public TextInputRangeChecker(JTextComponent aLowerTextComponent, JTextComponent anUpperTextComponent,
double aMaxRange) {
this(aLowerTextComponent, anUpperTextComponent, null, null, aMaxRange);
}
/**
* Constructor with all the settable parameters.
*
* @param aLowerTextComponent A text component for the lower bound.
* @param anUpperTextComponent A text component for the upper bound.
* @param lowerTextName The name of the lower bound, eg - start year.
* @param upperTextName The name of the upper bound, eg - end year.
* @param aMaxRange The range the bounds muist fall between, if 0
* then no range is used.
*/
public TextInputRangeChecker(JTextComponent aLowerTextComponent, JTextComponent anUpperTextComponent,
String lowerTextName, String upperTextName, double aMaxRange) {
lowerComponent = aLowerTextComponent;
upperComponent = anUpperTextComponent;
maxRange = aMaxRange;
focusListeners = new ArrayList(1); // For most cases, there will be only 1 listener.
lowerComponent.addFocusListener(this);
upperComponent.addFocusListener(this);
lowerNumber = getNumber(lowerComponent);
upperNumber = getNumber(upperComponent);
if ((lowerTextName != null) && (upperTextName != null)) {
invalidLowerMessage = "The " + lowerTextName + " must be less than or equal to the " + upperTextName + ".";
invalidUpperMessage = "The " + upperTextName + " must be greater than or equal to the " + lowerTextName
+ ".";
invalidEitherMessage = "The " + lowerTextName + " and/or the " + upperTextName + " are not correct.";
invalidRangeMessage = "The maximum range for the " + lowerTextName + " and " + upperTextName + " is "
+ maxRange + ".";
} else {
invalidLowerMessage = "The lower bound must be less than or equal to the upper bound.";
invalidUpperMessage = "The upper bound must be greater than or equal to the lower bound.";
invalidEitherMessage = "The upper and/or lower bounds are not correct.";
invalidRangeMessage = "The maximum range is " + maxRange + ".";
}
}
/**
* Allows the caller to perform the validation of the bounds programatically.
* The lower bound is compared to the upper bound and range checking is
* performed. If the lower bound is greater than the upper bound, or the range
* between the bounds is greater than the max range, then validation fails.
*
* @return TRUE is validation is successfull, FALSE if it fails.
*/
public boolean performCheck() {
return validate(null);
}
/**
* Adds the listener to the lists of focus listener maintened by this object.
* When one of the 2 text components receives a focus event, this object will
* fire that focus event to any of its listeners. This is useful when the
* calling object wants to be notified of the components focus events, but wants
* to ensure that the validation has occured first. <br>
* <br>
* NOTE: The focus is only fired if the validation was successful. This might
* have to be changed.
*
* @param aListener A Focus Listener to receive Focus Events.
*/
public void addFocusListener(FocusListener aListener) {
focusListeners.add(aListener);
}
/**
* Returns the last valid value of the lower bound. If this is called while the
* user is updating the text component but before the focus is lost, the value
* returned will be the original value before the user started updating the
* bound.
*
* @return The last valid value of the lower bound.
*/
public double getLastValidatedLowerNumber() {
return lowerNumber;
}
/**
* Returns the last valid value of the upper bound. If this is called while the
* user is updating the text component but before the focus is lost, the value
* returned will be the original value before the user started updating the
* bound.
*
* @return The last valid value of the upper bound.
*/
public double getLastValidatedUpperNumber() {
return upperNumber;
}
/**
* Method used to be notified when one of the text components has gained its
* focus.
*/
public void focusGained(FocusEvent e) {
lowerNumber = getNumber(lowerComponent);
upperNumber = getNumber(upperComponent);
}
/**
* Method used to be notified when one of the text components has lost its
* focus. Automatic validation occurs here.
*/
public void focusLost(FocusEvent e) {
if (e.isTemporary()) {
return;
}
if (validate(e.getSource())) {
fireFocusEvent(e);
}
}
/**
* Fires a focus lost event if the validation was successfull.
*/
protected void fireFocusEvent(FocusEvent e) {
for (Iterator it = focusListeners.iterator(); it.hasNext();) {
((FocusListener) it.next()).focusLost(e);
}
}
/**
* Validates the bounds inputed by the user.
*
* @param aComponent The component to use to display a dialog window, if
* neccessray. If null, then the parent window of the text
* componets will be used.
* @return TRUE if validation was successful, FALSE otherwise.
*/
protected boolean validate(Object aComponent) {
int componentUsed = NONE;
if (aComponent == lowerComponent) {
componentUsed = LOWER;
} else if (aComponent == upperComponent) {
componentUsed = UPPER;
}
double lower = getNumber(lowerComponent);
double upper = getNumber(upperComponent);
if (lower > upper) {
if (componentUsed == LOWER) {
lowerComponent.setText(Double.toString(lowerNumber));
displayMessage(invalidLowerMessage, lowerComponent);
} else if (componentUsed == UPPER) {
upperComponent.setText(Double.toString(upperNumber));
displayMessage(invalidUpperMessage, upperComponent);
} else {
upperComponent.setText(Double.toString(upperNumber));
lowerComponent.setText(Double.toString(lowerNumber));
displayMessage(invalidEitherMessage, lowerComponent.getTopLevelAncestor());
}
return false;
}
if (maxRange != 0.0) {
if ((upper - lower) > maxRange) {
if (componentUsed == LOWER) {
lowerComponent.setText(Double.toString(lowerNumber));
displayMessage(invalidRangeMessage, lowerComponent);
} else if (componentUsed == UPPER) {
upperComponent.setText(Double.toString(upperNumber));
displayMessage(invalidRangeMessage, upperComponent);
} else {
upperComponent.setText(Double.toString(upperNumber));
lowerComponent.setText(Double.toString(lowerNumber));
displayMessage(invalidRangeMessage, lowerComponent.getTopLevelAncestor());
}
return false;
}
}
lowerNumber = lower;
upperNumber = upper;
return true;
}
/**
* Creates a JOptionPane to display the reason why the bounds failed validation.
*/
protected void displayMessage(final String message, final Component parent) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(parent, message, "Data Entry Error", JOptionPane.ERROR_MESSAGE);
}
});
}
/**
* Gets the number represented in the text component. If the text does not
* represent a number, then zero is returned.
*/
protected double getNumber(JTextComponent aComponent) {
try {
return Double.valueOf(aComponent.getText()).doubleValue();
//1.2 return Double.parseDouble( aComponent.getText() );
} catch (NumberFormatException e) {
System.out.println(
"[GUI] TextInputRangeChecker.getNumber(): The text is NOT a number: " + aComponent.getText());
return 0.0;
}
}
}
/*
* $Log$ Revision 1.2 2006/02/18 23:19:05 cgruber Update imports and maven
* dependencies.
*
* Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in
* eclipse-friendly maven-enabled packages.
*
* Revision 1.2 2003/08/06 23:07:53 chochos general code cleanup (mostly,
* removing unused imports)
*
* Revision 1.1.1.1 2000/12/21 15:51:49 mpowers Contributing wotonomy.
*
* Revision 1.2 2000/12/20 16:25:46 michael Added log to all files.
*
*
*/
|