/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ /* * The org.opensourcephysics.media.core package defines the Open Source Physics * media framework for working with video and other media. * * Copyright (c) 2014 Douglas Brown and Wolfgang Christian. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * For additional information and documentation on Open Source Physics, * please see <http://www.opensourcephysics.org/>. */ package org.opensourcephysics.media.core; import java.awt.Color; import java.awt.Toolkit; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.ParseException; import javax.swing.JTextField; import javax.swing.SwingUtilities; /** * A NumberField is a JTextField that formats and displays numbers. * This default implementation displays very small and very large numbers * in scientific notation and intermediate-value numbers in decimal form. * * @author Douglas Brown * @version 1.0 */ public class NumberField extends JTextField { // constants public static final Color DISABLED_COLOR = new Color(120, 120, 120); public static final String INTEGER_PATTERN = "0"; //$NON-NLS-1$ public static final String DECIMAL_1_PATTERN = "0.0"; //$NON-NLS-1$ public static final String DECIMAL_2_PATTERN = "0.00"; //$NON-NLS-1$ public static final String DECIMAL_3_PATTERN = "0.000"; //$NON-NLS-1$ // instance fields protected DecimalFormat format = (DecimalFormat) NumberFormat.getInstance(); protected double prevValue; protected Double maxValue; protected Double minValue; protected int sigfigs; protected boolean fixedPattern = false; protected String[] patterns = new String[5]; protected double[] ranges = {0.1, 10, 100, 1000}; protected String units; protected double conversionFactor = 1.0; protected String userPattern = ""; //$NON-NLS-1$ protected boolean fixedPatternByDefault; /** * Constructs a NumberField with default sigfigs (4) * * @param columns the number of character columns */ public NumberField(int columns) { this(columns, 4); } /** * Constructs a NumberField with specified significant figures. * * @param columns the number of character columns * @param sigfigs the number of significant figures */ public NumberField(int columns, int sigfigs) { super(columns); setBackground(Color.white); setDisabledTextColor(DISABLED_COLOR); setText("0"); //$NON-NLS-1$ addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (!isEditable()) return; if(e.getKeyCode()==KeyEvent.VK_ENTER) { // delay background change so other listeners can look for yellow Runnable runner = new Runnable() { public synchronized void run() { setBackground(Color.white); setValue(getValue()); } }; SwingUtilities.invokeLater(runner); } else { setBackground(Color.yellow); } } }); addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent e) { if (!isEditable()) return; // delay background change so other listeners can look for yellow Runnable runner = new Runnable() { public synchronized void run() { setBackground(Color.white); setValue(getValue()); } }; SwingUtilities.invokeLater(runner); } }); addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (!isEditable()) return; if(e.getClickCount()==2) { selectAll(); } } }); setSigFigs(sigfigs); } /** * Gets the value from the text field. * * @return the value */ public double getValue() { String s = getText().trim(); // strip units, if any if((units!=null)&&!units.equals("")) { //$NON-NLS-1$ int n = s.indexOf(units); while(n>-1) { s = s.substring(0, n); n = s.indexOf(units); } } if(s.equals(format.format(prevValue*conversionFactor))) { return prevValue; } double retValue; try { retValue = format.parse(s).doubleValue()/conversionFactor; if(minValue!=null && retValue<minValue) { setValue(minValue); return minValue; } if(maxValue!=null && retValue>maxValue) { setValue(maxValue); return maxValue; } } catch(ParseException e) { Toolkit.getDefaultToolkit().beep(); setValue(prevValue); return prevValue; } return retValue; } /** * Formats the specified value and enters it in the text field. * * @param value the value to be entered */ public void setValue(double value) { if(!isVisible()) { return; } if(minValue!=null) { value = Math.max(value, minValue.doubleValue()); } if(maxValue!=null) { value = Math.min(value, maxValue.doubleValue()); } prevValue = value; // display value: include factor, format, units value = conversionFactor*value; setFormatFor(value); String s = format.format(value); if(units!=null) { s += units; } if(!s.equals(getText())) { setText(s); } } /** * Sets the expected range of values for this number field. * Note this does not set a firm max or min--only an expectation. * * @param lower the lower end of the range * @param upper the upper end of the range */ public void setExpectedRange(double lower, double upper) { fixedPattern = fixedPatternByDefault = true; double range = Math.max(Math.abs(lower), Math.abs(upper)); // char d = format.getDecimalFormatSymbols().getDecimalSeparator(); char d = '.'; if((range<0.1)||(range>=1000)) { // scientific format String s = ""; //$NON-NLS-1$ for(int i = 0; i<sigfigs-1; i++) { s += "0"; //$NON-NLS-1$ } format.applyPattern("0"+d+s+"E0"); //$NON-NLS-1$ //$NON-NLS-2$ } else { // decimal format int n; if(range<1) { n = sigfigs; } else if(range<10) { n = sigfigs-1; } else if(range<100) { n = sigfigs-2; } else { n = sigfigs-3; } String s = ""; //$NON-NLS-1$ for(int i = 0; i<n; i++) { s += "0"; //$NON-NLS-1$ } if(s.equals("")) { //$NON-NLS-1$ format.applyPattern("0"); //$NON-NLS-1$ } else { format.applyPattern("0"+d+s); //$NON-NLS-1$ } } } /** * Sets the number of significant figures for this number field. * * @param sigfigs the number of significant figures (between 2 and 6) */ public void setSigFigs(int sigfigs) { if(this.sigfigs==sigfigs) { return; } ranges = new double[] {0.1, 10, 100, 1000}; sigfigs = Math.max(sigfigs, 2); this.sigfigs = Math.min(sigfigs, 6); char d = '.'; if(sigfigs==2) { patterns[0] = "0"+d+"0E0"; // value < 1 //$NON-NLS-1$ //$NON-NLS-2$ patterns[1] = "0"+d+"0"; // value < 10 //$NON-NLS-1$ //$NON-NLS-2$ patterns[2] = "0"; // value < 100 //$NON-NLS-1$ patterns[3] = "0"+d+"0E0"; // value < 1000 //$NON-NLS-1$ //$NON-NLS-2$ patterns[4] = "0"+d+"0E0"; // value > 1000 //$NON-NLS-1$ //$NON-NLS-2$ } else if(sigfigs==3) { patterns[0] = "0"+d+"00E0"; // value < 1 //$NON-NLS-1$ //$NON-NLS-2$ patterns[1] = "0"+d+"00"; // value < 10 //$NON-NLS-1$ //$NON-NLS-2$ patterns[2] = "0"+d+"0"; // value < 100 //$NON-NLS-1$ //$NON-NLS-2$ patterns[3] = "0"; // value < 1000 //$NON-NLS-1$ patterns[4] = "0"+d+"00E0"; // value > 1000 //$NON-NLS-1$ //$NON-NLS-2$ } else if(sigfigs>=4) { patterns[0] = "0"+d+"000E0"; // value < 1 //$NON-NLS-1$ //$NON-NLS-2$ patterns[1] = "0"+d+"000"; // value < 10 //$NON-NLS-1$ //$NON-NLS-2$ patterns[2] = "0"+d+"00"; // value < 100 //$NON-NLS-1$ //$NON-NLS-2$ patterns[3] = "0"+d+"0"; // value < 1000 //$NON-NLS-1$ //$NON-NLS-2$ patterns[4] = "0"+d+"000E0"; // value > 1000 //$NON-NLS-1$ //$NON-NLS-2$ int n = sigfigs-4; for(int i = 0; i<n; i++) { for(int j = 0; j<patterns.length; j++) { patterns[j] = "0"+d+"0"+patterns[j].substring(2); //$NON-NLS-1$ //$NON-NLS-2$ } } } } /** * Sets a minimum value for this field. * * @param min the minimum allowed value */ public void setMinValue(double min) { if(Double.isNaN(min)) { minValue = null; } else { minValue = new Double(min); } } /** * Sets a maximum value for this field. * * @param max the maximum allowed value */ public void setMaxValue(double max) { if(Double.isNaN(max)) { maxValue = null; } else { maxValue = new Double(max); } } /** * Sets the units. * * @param units the units */ public void setUnits(String units) { // replace old units with new in text double val = getValue(); this.units = units; setValue(val); } /** * Gets the units. * * @return units the units */ public String getUnits() { return units; } public void setConversionFactor(double factor) { conversionFactor = factor; setValue(prevValue); } public double getConversionFactor() { return conversionFactor; } /** * Gets the format for this field. * * @return the format */ public DecimalFormat getFormat() { return format; } /** * Sets the format for a specified value. * * @param value the value to be displayed */ public void setFormatFor(double value) { if(fixedPattern) { return; } value = Math.abs(value); if (value==0) { if (sigfigs==1) { format.applyPattern(INTEGER_PATTERN); } else if (sigfigs==2) { format.applyPattern(DECIMAL_1_PATTERN); } else if (sigfigs==3) { format.applyPattern(DECIMAL_2_PATTERN); } else { format.applyPattern(DECIMAL_3_PATTERN); } } else if(value<ranges[0]) { format.applyPattern(patterns[0]); } else if(value<ranges[1]) { format.applyPattern(patterns[1]); } else if(value<ranges[2]) { format.applyPattern(patterns[2]); } else if(value<ranges[3]) { format.applyPattern(patterns[3]); } else { format.applyPattern(patterns[4]); } } /** * Sets the patterns for this field. The patterns are applied as follows: * value<0.1: patterns[0] * value<10: patterns[1] * value<100: patterns[2] * value<1000: patterns[3] * value>=1000: patterns[4] * * @param patterns the desired patterns */ public void setPatterns(String[] patterns) { setPatterns(patterns, new double[] {0.1, 10, 100, 1000}); } /** * Sets the patterns and limits for this field. The patterns are applied as follows: * value<limits[0]: patterns[0] * value<limits[1]: patterns[1] * value<limits[2]: patterns[2] * value<limits[3]: patterns[3] * value>=limits[3]: patterns[4] * * @param patterns the desired patterns * @param limits the limits that determine which pattern to use */ public void setPatterns(String[] patterns, double[] limits) { if(patterns.length>4 && limits.length>3) { this.patterns = patterns; ranges = limits; } } /** * Sets a fixed user pattern. * * @param pattern the desired pattern (may be null) */ public void setFixedPattern(String pattern) { if (pattern==null) pattern = ""; //$NON-NLS-1$ pattern = pattern.trim(); if (pattern.equals(userPattern)) return; userPattern = pattern; if (userPattern.equals("")) { //$NON-NLS-1$ fixedPattern = fixedPatternByDefault; setFormatFor(getValue()); } else { fixedPattern = true; format.applyPattern(userPattern); } setValue(prevValue); } /** * Gets the fixed user pattern. * * @return the pattern */ public String getFixedPattern() { return userPattern; } } /* * Open Source Physics software is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be released * under the GNU GPL license. * * This software 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */