001: /*
002: * SSHTools - Java SSH2 API
003: *
004: * Copyright (C) 2002-2003 Lee David Painter and Contributors.
005: *
006: * Contributions made by:
007: *
008: * Brett Smith
009: * Richard Pernavas
010: * Erwin Bolwidt
011: *
012: * This program is free software; you can redistribute it and/or
013: * modify it under the terms of the GNU General Public License
014: * as published by the Free Software Foundation; either version 2
015: * of the License, or (at your option) any later version.
016: *
017: * This program is distributed in the hope that it will be useful,
018: * but WITHOUT ANY WARRANTY; without even the implied warranty of
019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
020: * GNU General Public License for more details.
021: *
022: * You should have received a copy of the GNU General Public License
023: * along with this program; if not, write to the Free Software
024: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
025: */
026: package com.sshtools.common.ui;
027:
028: import java.awt.Color;
029: import java.awt.event.FocusEvent;
030:
031: import java.text.DecimalFormat;
032: import java.text.DecimalFormatSymbols;
033: import java.text.NumberFormat;
034: import java.text.ParseException;
035:
036: import javax.swing.JTextField;
037: import javax.swing.text.AttributeSet;
038: import javax.swing.text.BadLocationException;
039: import javax.swing.text.PlainDocument;
040:
041: /**
042: *
043: *
044: * @author $author$
045: * @version $Revision: 1.14 $
046: */
047: public class NumericTextField extends XTextField {
048: private Color positiveBackground;
049: private DecimalFormatSymbols symbols;
050:
051: // Private instance variables
052: private NumberFormat numberFormat;
053: private boolean selectAllOnFocusGain;
054: private int wColumnWidth;
055:
056: /**
057: * Creates a new NumericTextField object.
058: *
059: * @param min
060: * @param max
061: */
062: public NumericTextField(Number min, Number max) {
063: this (min, max, min);
064: }
065:
066: /**
067: * Creates a new NumericTextField object.
068: *
069: * @param min
070: * @param max
071: * @param initial
072: * @param rightJustify
073: */
074: public NumericTextField(Number min, Number max, Number initial,
075: boolean rightJustify) {
076: this (min, max, initial, rightJustify, null);
077: }
078:
079: /**
080: * Creates a new NumericTextField object.
081: *
082: * @param min
083: * @param max
084: * @param initial
085: */
086: public NumericTextField(Number min, Number max, Number initial) {
087: this (min, max, initial, true);
088: }
089:
090: /**
091: * Creates a new NumericTextField object.
092: *
093: * @param min
094: * @param max
095: * @param initial
096: * @param rightJustify
097: * @param numberFormat
098: *
099: * @throws IllegalArgumentException
100: */
101: public NumericTextField(Number min, Number max, Number initial,
102: boolean rightJustify, NumberFormat numberFormat) {
103: super (Math
104: .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())
141: .toString();
142:
143: if (!getText().equals(s)) {
144: setText(s);
145: }
146: }
147:
148: break;
149:
150: case FocusEvent.FOCUS_GAINED:
151:
152: if (isSelectAllOnFocusGain()) {
153: selectAll();
154: }
155:
156: break;
157: }
158: }
159: }
160:
161: /**
162: *
163: *
164: * @return
165: */
166: public boolean isSelectAllOnFocusGain() {
167: return selectAllOnFocusGain;
168: }
169:
170: /**
171: *
172: *
173: * @param selectAllOnFocusGain
174: */
175: public void setSelectAllOnFocusGain(boolean selectAllOnFocusGain) {
176: this .selectAllOnFocusGain = selectAllOnFocusGain;
177: }
178:
179: /**
180: *
181: *
182: * @param max
183: */
184: public void setMaximum(Number max) {
185: ((ADocument) getDocument()).setMaximum(max);
186: }
187:
188: /**
189: *
190: *
191: * @return
192: */
193: public Number getMaximum() {
194: return ((ADocument) getDocument()).max;
195: }
196:
197: /**
198: *
199: *
200: * @param min
201: */
202: public void setMinimum(Number min) {
203: ((ADocument) getDocument()).setMinimum(min);
204: }
205:
206: /**
207: *
208: *
209: * @return
210: */
211: public Number getMinimum() {
212: return ((ADocument) getDocument()).min;
213: }
214:
215: /**
216: *
217: *
218: * @param numberFormat
219: */
220: public void setNumberFormat(NumberFormat numberFormat) {
221: this .numberFormat = numberFormat;
222:
223: if (numberFormat instanceof DecimalFormat) {
224: symbols = ((DecimalFormat) numberFormat)
225: .getDecimalFormatSymbols();
226: } else {
227: symbols = new DecimalFormatSymbols();
228: }
229: }
230:
231: /**
232: *
233: *
234: * @return
235: */
236: public NumberFormat getNumberFormat() {
237: return numberFormat;
238: }
239:
240: /**
241: *
242: *
243: * @param rightJustify
244: */
245: public void setRightJustify(boolean rightJustify) {
246: setHorizontalAlignment(rightJustify ? JTextField.RIGHT
247: : JTextField.LEFT);
248: }
249:
250: /**
251: *
252: *
253: * @return
254: */
255: public boolean isRightJustify() {
256: return getHorizontalAlignment() == JTextField.RIGHT;
257: }
258:
259: /**
260: *
261: *
262: * @param s
263: */
264: public void setText(String s) {
265: ADocument doc = (ADocument) getDocument();
266: Number oldValue = doc.currentVal;
267:
268: try {
269: doc.currentVal = doc.parse(s);
270: } catch (Exception e) {
271: e.printStackTrace();
272:
273: return;
274: }
275:
276: if (oldValue != doc.currentVal) {
277: doc.checkingEnabled = false;
278: super .setText(s);
279: doc.checkingEnabled = true;
280: }
281: }
282:
283: /**
284: *
285: *
286: * @param i
287: */
288: public void setValue(Number i) {
289: setText(i.toString());
290: }
291:
292: /**
293: *
294: *
295: * @return
296: */
297: public Number getValue() {
298: return ((ADocument) getDocument()).getValue();
299: }
300:
301: // Supporting classes
302: class ADocument extends PlainDocument {
303: Number currentVal;
304: Number max;
305: Number min;
306: boolean checkingEnabled = true;
307: boolean rightJustify = true;
308:
309: public ADocument(Number min, Number max) {
310: this .min = min;
311: this .max = max;
312:
313: if (min.getClass().equals(Byte.class)) {
314: currentVal = new Byte((byte) 0);
315: } else {
316: if (min.getClass().equals(Short.class)) {
317: currentVal = new Short((short) 0);
318: } else {
319: if (min.getClass().equals(Integer.class)) {
320: currentVal = new Integer(0);
321: } else {
322: if (min.getClass().equals(Long.class)) {
323: currentVal = new Long(0L);
324: } else {
325: if (min.getClass().equals(Float.class)) {
326: currentVal = new Float(0f);
327: } else {
328: currentVal = new Double(0d);
329: }
330: }
331: }
332: }
333: }
334: }
335:
336: public void setMaximum(Number max) {
337: this .max = max;
338: }
339:
340: public void setMinimum(Number min) {
341: this .min = min;
342: }
343:
344: public void setRightJustify(boolean rightJustify) {
345: this .rightJustify = rightJustify;
346: }
347:
348: public boolean isRightJustify() {
349: return rightJustify;
350: }
351:
352: public Number getValue() {
353: return currentVal;
354: }
355:
356: public void insertString(int offs, String str, AttributeSet a)
357: throws BadLocationException {
358: if (str == null) {
359: return;
360: }
361:
362: if (!checkingEnabled) {
363: super .insertString(offs, str, a);
364:
365: return;
366: }
367:
368: String proposedResult = null;
369:
370: if (getLength() == 0) {
371: proposedResult = str;
372: } else {
373: StringBuffer currentBuffer = new StringBuffer(getText(
374: 0, getLength()));
375: currentBuffer.insert(offs, str);
376: proposedResult = currentBuffer.toString();
377: }
378:
379: try {
380: currentVal = parse(proposedResult);
381: super .insertString(offs, str, a);
382: } catch (Exception e) {
383: }
384: }
385:
386: public Number parse(String proposedResult)
387: throws NumberFormatException {
388: Double d = new Double(0d);
389:
390: // See if the proposed result matches the number format (if any)
391: if (!proposedResult.equals(String.valueOf(symbols
392: .getMinusSign()))
393: && (proposedResult.length() != 0)) {
394: if (getNumberFormat() != null) {
395: // Strip out everything from the proposed result other than the
396: // numbers and and decimal separators
397: StringBuffer sB = new StringBuffer();
398:
399: for (int i = 0; i < proposedResult.length(); i++) {
400: char ch = proposedResult.charAt(i);
401:
402: if ((ch == symbols.getDecimalSeparator())
403: || ((ch >= '0') && (ch <= '9'))) {
404: sB.append(ch);
405: }
406: }
407:
408: String s = sB.toString();
409:
410: // Find out how many digits there are before the decimal place
411: int i = 0;
412:
413: for (; (i < s.length())
414: && (s.charAt(i) != symbols
415: .getDecimalSeparator()); i++) {
416: ;
417: }
418:
419: int before = i;
420: int after = 0;
421:
422: if (before < s.length()) {
423: after = s.length() - i - 1;
424: }
425:
426: if (before > getNumberFormat()
427: .getMaximumIntegerDigits()) {
428: throw new NumberFormatException(
429: "More digits BEFORE the decimal separator than allowed:"
430: + proposedResult);
431: }
432:
433: if (after > getNumberFormat()
434: .getMaximumFractionDigits()) {
435: throw new NumberFormatException(
436: "More digits AFTER the decimal separator than allowed:"
437: + proposedResult);
438: }
439:
440: // Now try to parse the field against the number format
441: try {
442: d = new Double(getNumberFormat().parse(
443: proposedResult).doubleValue());
444: } catch (ParseException pE) {
445: throw new NumberFormatException(
446: "Failed to parse. " + proposedResult
447: + pE.getMessage());
448: }
449: }
450: // Just use the default parse
451: else {
452: d = new Double(proposedResult);
453: }
454: }
455:
456: // Now determine if the number if within range
457: if ((d.doubleValue() >= min.doubleValue())
458: && (d.doubleValue() <= max.doubleValue())) {
459: // Now create the real type
460: if (min.getClass().equals(Byte.class)) {
461: return new Byte(d.byteValue());
462: } else {
463: if (min.getClass().equals(Short.class)) {
464: return new Short(d.shortValue());
465: } else {
466: if (min.getClass().equals(Integer.class)) {
467: return new Integer(d.intValue());
468: } else {
469: if (min.getClass().equals(Long.class)) {
470: return new Long(d.longValue());
471: } else {
472: if (min.getClass().equals(Float.class)) {
473: return new Float(d.floatValue());
474: } else {
475: return d;
476: }
477: }
478: }
479: }
480: }
481: } else {
482: throw new NumberFormatException(d
483: + " Is out of range. Minimum is "
484: + min.doubleValue() + ", Maximum is "
485: + max.doubleValue());
486: }
487: }
488:
489: public void remove(int offs, int len)
490: throws BadLocationException {
491: if (!checkingEnabled) {
492: super .remove(offs, len);
493:
494: return;
495: }
496:
497: String currentText = getText(0, getLength());
498: String beforeOffset = currentText.substring(0, offs);
499: String afterOffset = currentText.substring(len + offs,
500: currentText.length());
501: String proposedResult = beforeOffset + afterOffset;
502:
503: try {
504: currentVal = parse(proposedResult);
505: super .remove(offs, len);
506: } catch (Exception e) {
507: }
508: }
509: }
510: }
|