001 package org.LiveGraph.gui; 002 003 import java.awt.Color; 004 import java.awt.Point; 005 import java.awt.event.FocusEvent; 006 import java.awt.event.FocusListener; 007 import java.awt.event.KeyEvent; 008 import java.awt.event.KeyListener; 009 import java.util.HashMap; 010 011 import javax.swing.JLabel; 012 import javax.swing.JTextField; 013 import javax.swing.Popup; 014 import javax.swing.PopupFactory; 015 016 import com.softnetConsult.utils.string.StringTools; 017 import com.softnetConsult.utils.sys.SystemTools; 018 019 020 /** 021 * This validating adaptor listens to {@code focusLost}-messages of a 022 * {@code JTextField}. If at that moment the field contains a valid 023 * {@code double} value (as a string), the {@link #valueChanged(JTextField, double)}-method is 024 * called. That method must be overridden by subclasses to take some appropriate 025 * action. If, however, the field does not contain a valid {@code double} value, 026 * the {@link #valueChanged(JTextField, double)}-method is not called and a tooltip with an error 027 * message is displayed near the text fiels. The last known "good" value is then 028 * restored. 029 * 030 * <p style="font-size:smaller;">This product includes software developed by the 031 * <strong>LiveGraph</strong> project and its contributors.<br /> 032 * (<a href="http://www.live-graph.org" target="_blank">http://www.live-graph.org</a>)<br /> 033 * Copyright (c) 2007-2008 G. Paperin.<br /> 034 * All rights reserved. 035 * </p> 036 * <p style="font-size:smaller;">File: RealNumFieldValueChangeAdaptor.java</p> 037 * <p style="font-size:smaller;">Redistribution and use in source and binary forms, with or 038 * without modification, are permitted provided that the following terms and conditions are met: 039 * </p> 040 * <p style="font-size:smaller;">1. Redistributions of source code must retain the above 041 * acknowledgement of the LiveGraph project and its web-site, the above copyright notice, 042 * this list of conditions and the following disclaimer.<br /> 043 * 2. Redistributions in binary form must reproduce the above acknowledgement of the 044 * LiveGraph project and its web-site, the above copyright notice, this list of conditions 045 * and the following disclaimer in the documentation and/or other materials provided with 046 * the distribution.<br /> 047 * 3. All advertising materials mentioning features or use of this software or any derived 048 * software must display the following acknowledgement:<br /> 049 * <em>This product includes software developed by the LiveGraph project and its 050 * contributors.<br />(http://www.live-graph.org)</em><br /> 051 * 4. All advertising materials distributed in form of HTML pages or any other technology 052 * permitting active hyper-links that mention features or use of this software or any 053 * derived software must display the acknowledgment specified in condition 3 of this 054 * agreement, and in addition, include a visible and working hyper-link to the LiveGraph 055 * homepage (http://www.live-graph.org). 056 * </p> 057 * <p style="font-size:smaller;">THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 058 * OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 059 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 060 * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 061 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 062 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 063 * </p> 064 * 065 * @author Greg Paperin (<a href="http://www.paperin.org" target="_blank">http://www.paperin.org</a>) 066 * @version {@value org.LiveGraph.LiveGraph#version} 067 */ 068 public abstract class RealNumFieldValueChangeAdaptor implements FocusListener, KeyListener { 069 070 /** 071 * Display length for the error messgae tooltip in milliseconds. 072 */ 073 public static final long TOOLTIP_DISPLAY_LEN = 2000; 074 075 private double defaultValue = Double.NaN; 076 private HashMap<JTextField, Double> lastLegalvaluesCache = null; 077 078 /** 079 * Constructor. 080 * @param defaultValue Default "good" value. 081 */ 082 public RealNumFieldValueChangeAdaptor(double defaultValue) { 083 this.defaultValue = defaultValue; 084 this.lastLegalvaluesCache = new HashMap<JTextField, Double>(); 085 } 086 087 /** 088 * Does nothing. 089 */ 090 public void focusGained(FocusEvent e) { 091 ; 092 } 093 094 /** 095 * Catches the focus lost event and performs field validation. 096 */ 097 public void focusLost(FocusEvent e) { 098 Object source = e.getSource(); 099 if (source instanceof JTextField) 100 handleEvent((JTextField) source); 101 } 102 103 /** 104 * Catches the enter pressed event and performs field validation. 105 */ 106 public void keyPressed(KeyEvent e) { 107 Object source = e.getSource(); 108 if (! (source instanceof JTextField)) 109 return; 110 if (KeyEvent.VK_ENTER != e.getKeyCode()) 111 return; 112 handleEvent((JTextField) source); 113 } 114 115 /** 116 * Does nothing. 117 */ 118 public void keyReleased(KeyEvent e) { 119 ; 120 } 121 122 /** 123 * Does nothing. 124 */ 125 public void keyTyped(KeyEvent e) { 126 ; 127 } 128 129 /** 130 * Performs the validation and handles appropriately. 131 * 132 * @param field The text field that generated the event. 133 */ 134 public void handleEvent(final JTextField field) { 135 try { 136 double val = StringTools.parseDouble(field.getText()); 137 lastLegalvaluesCache.put(field, val); 138 double newVal = valueChanged(field, val); 139 if (newVal != val) { 140 field.setText(StringTools.toString(newVal)); 141 lastLegalvaluesCache.put(field, newVal); 142 } 143 } catch (NumberFormatException ex) { 144 Point sc = field.getLocationOnScreen(); 145 sc.x += field.getWidth() / 2; 146 sc.y += field.getHeight() / 2; 147 JLabel info = new JLabel("You need a valid real value here. \"" + field.getText() + "\" is not a real number."); 148 info.setOpaque(true); 149 info.setBackground(Color.ORANGE); 150 info.setForeground(Color.DARK_GRAY); 151 final Popup popup = PopupFactory.getSharedInstance().getPopup(field, info, sc.x, sc.y); 152 popup.show(); 153 Thread offSwitch = new Thread(new Runnable() { 154 public void run() { 155 try { 156 SystemTools.sleep(TOOLTIP_DISPLAY_LEN); 157 } finally { 158 popup.hide(); 159 if (lastLegalvaluesCache.containsKey(field)) 160 field.setText(lastLegalvaluesCache.get(field).toString()); 161 else 162 field.setText(StringTools.toString(defaultValue)); 163 } 164 }}, "RealNumFieldValueChangeAdaptor.OffSwitch"); 165 offSwitch.start(); 166 } 167 } 168 169 /** 170 * Subclasses must override that in order to take the appropriate action when the field 171 * contains a valid {@code double} value. 172 * 173 * @param field The text field containing the value. 174 * @param newValue The {@code double} value in the field. 175 * @return A string representing the value returned by this method will be used instead 176 * of the current field content. 177 */ 178 abstract public double valueChanged(JTextField field, double newValue); 179 180 }