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