1 /*
2 * SSHTools - Java SSH2 API
3 *
4 * Copyright (C) 2002-2003 Lee David Painter and Contributors.
5 *
6 * Contributions made by:
7 *
8 * Brett Smith
9 * Richard Pernavas
10 * Erwin Bolwidt
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 */
26 package com.sshtools.common.ui;
27
28 import java.awt.Color;
29 import java.awt.event.FocusEvent;
30
31 import java.text.DecimalFormat;
32 import java.text.DecimalFormatSymbols;
33 import java.text.NumberFormat;
34 import java.text.ParseException;
35
36 import javax.swing.JTextField;
37 import javax.swing.text.AttributeSet;
38 import javax.swing.text.BadLocationException;
39 import javax.swing.text.PlainDocument;
40
41
42 /**
43 *
44 *
45 * @author $author$
46 * @version $Revision: 1.14 $
47 */
48 public class NumericTextField extends XTextField {
49 private Color positiveBackground;
50 private DecimalFormatSymbols symbols;
51
52 // Private instance variables
53 private NumberFormat numberFormat;
54 private boolean selectAllOnFocusGain;
55 private int wColumnWidth;
56
57 /**
58 * Creates a new NumericTextField object.
59 *
60 * @param min
61 * @param max
62 */
63 public NumericTextField(Number min, Number max) {
64 this(min, max, min);
65 }
66
67 /**
68 * Creates a new NumericTextField object.
69 *
70 * @param min
71 * @param max
72 * @param initial
73 * @param rightJustify
74 */
75 public NumericTextField(Number min, Number max, Number initial,
76 boolean rightJustify) {
77 this(min, max, initial, rightJustify, null);
78 }
79
80 /**
81 * Creates a new NumericTextField object.
82 *
83 * @param min
84 * @param max
85 * @param initial
86 */
87 public NumericTextField(Number min, Number max, Number initial) {
88 this(min, max, initial, true);
89 }
90
91 /**
92 * Creates a new NumericTextField object.
93 *
94 * @param min
95 * @param max
96 * @param initial
97 * @param rightJustify
98 * @param numberFormat
99 *
100 * @throws IllegalArgumentException
101 */
102 public NumericTextField(Number min, Number max, Number initial,
103 boolean rightJustify, NumberFormat numberFormat) {
104 super(Math.max(min.toString().length(), max.toString().length()));
105 setNumberFormat(numberFormat);
106
107 if (min.getClass().equals(max.getClass()) &&
108 max.getClass().equals(initial.getClass())) {
109 setDocument(new ADocument(min, max));
110 setValue(initial);
111 } else {
112 throw new IllegalArgumentException(
113 "All arguments must be of the same class");
114 }
115
116 setRightJustify(rightJustify);
117 }
118
119 /*
120 * Overides <code>JTextFields</codes> calculation of the width of a single
121 * character (M space)
122 *
123 * @return column width based on '9'
124 * public int getColumnWidth() {
125 * if (wColumnWidth==0) {
126 * FontMetrics metrics = getFontMetrics(getFont());
127 * wColumnWidth = metrics.charWidth('W');
128 * }
129 * return wColumnWidth;
130 * }
131 */
132 protected void processFocusEvent(FocusEvent e) {
133 super.processFocusEvent(e);
134
135 if (!e.isTemporary()) {
136 switch (e.getID()) {
137 case FocusEvent.FOCUS_LOST:
138
139 if (getNumberFormat() != null) {
140 String s = getNumberFormat().format(getValue()).toString();
141
142 if (!getText().equals(s)) {
143 setText(s);
144 }
145 }
146
147 break;
148
149 case FocusEvent.FOCUS_GAINED:
150
151 if (isSelectAllOnFocusGain()) {
152 selectAll();
153 }
154
155 break;
156 }
157 }
158 }
159
160 /**
161 *
162 *
163 * @return
164 */
165 public boolean isSelectAllOnFocusGain() {
166 return selectAllOnFocusGain;
167 }
168
169 /**
170 *
171 *
172 * @param selectAllOnFocusGain
173 */
174 public void setSelectAllOnFocusGain(boolean selectAllOnFocusGain) {
175 this.selectAllOnFocusGain = selectAllOnFocusGain;
176 }
177
178 /**
179 *
180 *
181 * @param max
182 */
183 public void setMaximum(Number max) {
184 ((ADocument) getDocument()).setMaximum(max);
185 }
186
187 /**
188 *
189 *
190 * @return
191 */
192 public Number getMaximum() {
193 return ((ADocument) getDocument()).max;
194 }
195
196 /**
197 *
198 *
199 * @param min
200 */
201 public void setMinimum(Number min) {
202 ((ADocument) getDocument()).setMinimum(min);
203 }
204
205 /**
206 *
207 *
208 * @return
209 */
210 public Number getMinimum() {
211 return ((ADocument) getDocument()).min;
212 }
213
214 /**
215 *
216 *
217 * @param numberFormat
218 */
219 public void setNumberFormat(NumberFormat numberFormat) {
220 this.numberFormat = numberFormat;
221
222 if (numberFormat instanceof DecimalFormat) {
223 symbols = ((DecimalFormat) numberFormat).getDecimalFormatSymbols();
224 } else {
225 symbols = new DecimalFormatSymbols();
226 }
227 }
228
229 /**
230 *
231 *
232 * @return
233 */
234 public NumberFormat getNumberFormat() {
235 return numberFormat;
236 }
237
238 /**
239 *
240 *
241 * @param rightJustify
242 */
243 public void setRightJustify(boolean rightJustify) {
244 setHorizontalAlignment(rightJustify ? JTextField.RIGHT : JTextField.LEFT);
245 }
246
247 /**
248 *
249 *
250 * @return
251 */
252 public boolean isRightJustify() {
253 return getHorizontalAlignment() == JTextField.RIGHT;
254 }
255
256 /**
257 *
258 *
259 * @param s
260 */
261 public void setText(String s) {
262 ADocument doc = (ADocument) getDocument();
263 Number oldValue = doc.currentVal;
264
265 try {
266 doc.currentVal = doc.parse(s);
267 } catch (Exception e) {
268 e.printStackTrace();
269
270 return;
271 }
272
273 if (oldValue != doc.currentVal) {
274 doc.checkingEnabled = false;
275 super.setText(s);
276 doc.checkingEnabled = true;
277 }
278 }
279
280 /**
281 *
282 *
283 * @param i
284 */
285 public void setValue(Number i) {
286 setText(i.toString());
287 }
288
289 /**
290 *
291 *
292 * @return
293 */
294 public Number getValue() {
295 return ((ADocument) getDocument()).getValue();
296 }
297
298 // Supporting classes
299 class ADocument extends PlainDocument {
300 Number currentVal;
301 Number max;
302 Number min;
303 boolean checkingEnabled = true;
304 boolean rightJustify = true;
305
306 public ADocument(Number min, Number max) {
307 this.min = min;
308 this.max = max;
309
310 if (min.getClass().equals(Byte.class)) {
311 currentVal = new Byte((byte) 0);
312 } else {
313 if (min.getClass().equals(Short.class)) {
314 currentVal = new Short((short) 0);
315 } else {
316 if (min.getClass().equals(Integer.class)) {
317 currentVal = new Integer(0);
318 } else {
319 if (min.getClass().equals(Long.class)) {
320 currentVal = new Long(0L);
321 } else {
322 if (min.getClass().equals(Float.class)) {
323 currentVal = new Float(0f);
324 } else {
325 currentVal = new Double(0d);
326 }
327 }
328 }
329 }
330 }
331 }
332
333 public void setMaximum(Number max) {
334 this.max = max;
335 }
336
337 public void setMinimum(Number min) {
338 this.min = min;
339 }
340
341 public void setRightJustify(boolean rightJustify) {
342 this.rightJustify = rightJustify;
343 }
344
345 public boolean isRightJustify() {
346 return rightJustify;
347 }
348
349 public Number getValue() {
350 return currentVal;
351 }
352
353 public void insertString(int offs, String str, AttributeSet a)
354 throws BadLocationException {
355 if (str == null) {
356 return;
357 }
358
359 if (!checkingEnabled) {
360 super.insertString(offs, str, a);
361
362 return;
363 }
364
365 String proposedResult = null;
366
367 if (getLength() == 0) {
368 proposedResult = str;
369 } else {
370 StringBuffer currentBuffer = new StringBuffer(getText(0,
371 getLength()));
372 currentBuffer.insert(offs, str);
373 proposedResult = currentBuffer.toString();
374 }
375
376 try {
377 currentVal = parse(proposedResult);
378 super.insertString(offs, str, a);
379 } catch (Exception e) {
380 }
381 }
382
383 public Number parse(String proposedResult) throws NumberFormatException {
384 Double d = new Double(0d);
385
386 // See if the proposed result matches the number format (if any)
387 if (!proposedResult.equals(String.valueOf(symbols.getMinusSign())) &&
388 (proposedResult.length() != 0)) {
389 if (getNumberFormat() != null) {
390 // Strip out everything from the proposed result other than the
391 // numbers and and decimal separators
392 StringBuffer sB = new StringBuffer();
393
394 for (int i = 0; i < proposedResult.length(); i++) {
395 char ch = proposedResult.charAt(i);
396
397 if ((ch == symbols.getDecimalSeparator()) ||
398 ((ch >= '0') && (ch <= '9'))) {
399 sB.append(ch);
400 }
401 }
402
403 String s = sB.toString();
404
405 // Find out how many digits there are before the decimal place
406 int i = 0;
407
408 for (;
409 (i < s.length()) &&
410 (s.charAt(i) != symbols.getDecimalSeparator());
411 i++) {
412 ;
413 }
414
415 int before = i;
416 int after = 0;
417
418 if (before < s.length()) {
419 after = s.length() - i - 1;
420 }
421
422 if (before > getNumberFormat().getMaximumIntegerDigits()) {
423 throw new NumberFormatException(
424 "More digits BEFORE the decimal separator than allowed:" +
425 proposedResult);
426 }
427
428 if (after > getNumberFormat().getMaximumFractionDigits()) {
429 throw new NumberFormatException(
430 "More digits AFTER the decimal separator than allowed:" +
431 proposedResult);
432 }
433
434 // Now try to parse the field against the number format
435 try {
436 d = new Double(getNumberFormat().parse(proposedResult)
437 .doubleValue());
438 } catch (ParseException pE) {
439 throw new NumberFormatException("Failed to parse. " +
440 proposedResult + pE.getMessage());
441 }
442 }
443 // Just use the default parse
444 else {
445 d = new Double(proposedResult);
446 }
447 }
448
449 // Now determine if the number if within range
450 if ((d.doubleValue() >= min.doubleValue()) &&
451 (d.doubleValue() <= max.doubleValue())) {
452 // Now create the real type
453 if (min.getClass().equals(Byte.class)) {
454 return new Byte(d.byteValue());
455 } else {
456 if (min.getClass().equals(Short.class)) {
457 return new Short(d.shortValue());
458 } else {
459 if (min.getClass().equals(Integer.class)) {
460 return new Integer(d.intValue());
461 } else {
462 if (min.getClass().equals(Long.class)) {
463 return new Long(d.longValue());
464 } else {
465 if (min.getClass().equals(Float.class)) {
466 return new Float(d.floatValue());
467 } else {
468 return d;
469 }
470 }
471 }
472 }
473 }
474 } else {
475 throw new NumberFormatException(d +
476 " Is out of range. Minimum is " + min.doubleValue() +
477 ", Maximum is " + max.doubleValue());
478 }
479 }
480
481 public void remove(int offs, int len) throws BadLocationException {
482 if (!checkingEnabled) {
483 super.remove(offs, len);
484
485 return;
486 }
487
488 String currentText = getText(0, getLength());
489 String beforeOffset = currentText.substring(0, offs);
490 String afterOffset = currentText.substring(len + offs,
491 currentText.length());
492 String proposedResult = beforeOffset + afterOffset;
493
494 try {
495 currentVal = parse(proposedResult);
496 super.remove(offs, len);
497 } catch (Exception e) {
498 }
499 }
500 }
501 }