/*******************************************************************************
* Copyright (c) MOBAC developers
*
* This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package mobac.gui.components;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import mobac.utilities.I18nUtils;
import mobac.utilities.Utilities;
import mobac.utilities.geo.CoordinateDms2Format;
public class JCoordinateField extends JTextField {
private static final long serialVersionUID = 1L;
private static final Color ERROR_COLOR = new Color(255, 100, 100);
private static final String INVALID_TEXT = I18nUtils.localizedStringForKey("lp_coords_invalid_text");
private JCoordinateListener coordinateListener;
private boolean inputIsValid = true;
private NumberFormat numberFormat;
private final double min;
private final double max;
public JCoordinateField(double min, double max) {
super(10);
this.min = min;
this.max = max;
numberFormat = new CoordinateDms2Format(new DecimalFormatSymbols());
coordinateListener = new JCoordinateListener();
coordinateListener.checkCoordinate(null);
}
@Override
public Point getToolTipLocation(MouseEvent event) {
if (getToolTipText().length() > 0)
return super.getToolTipLocation(event);
else
// We don't want a tool tip but Java does not allow to disable it?
// -> show it at a point where no user will ever see it
return new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
public void setCoordinate(double value) {
try {
// We know that the number is valid, therefore we can skip the check
// -> saves CPU power while selecting via preview map
boolean newValid = true;
coordinateListener.setEnabled(false);
if (Double.isNaN(value)) {
super.setText("");
newValid = false;
} else {
super.setText(numberFormat.format(value));
}
if (newValid != inputIsValid)
coordinateListener.changeValidMode(true);
} finally {
coordinateListener.setEnabled(true);
}
}
public double getCoordinate() throws ParseException {
ParsePosition pos = new ParsePosition(0);
String text = JCoordinateField.this.getText();
Number num = numberFormat.parse(text, pos);
if (num == null || pos.getErrorIndex() >= 0 || Double.isNaN(num.doubleValue()))
throw new ParseException(text, pos.getErrorIndex());
return num.doubleValue();
}
public double getCoordinateOrNaN() {
ParsePosition pos = new ParsePosition(0);
String text = JCoordinateField.this.getText();
Number num = numberFormat.parse(text, pos);
if (num == null || pos.getErrorIndex() >= 0)
return Double.NaN;
return num.doubleValue();
}
public boolean isInputValid() {
return inputIsValid;
}
public NumberFormat getNumberFormat() {
return numberFormat;
}
public void setNumberFormat(NumberFormat numberFormat) {
double coord = getCoordinateOrNaN();
this.numberFormat = numberFormat;
setCoordinate(coord);
}
protected class JCoordinateListener implements DocumentListener {
private Color defaultColor;
private boolean enabled;
private JCoordinateListener() {
enabled = true;
defaultColor = JCoordinateField.this.getBackground();
JCoordinateField.this.getDocument().addDocumentListener(this);
}
private void checkCoordinate(DocumentEvent de) {
if (!enabled)
return;
boolean valid = false;
try {
ParsePosition pos = new ParsePosition(0);
String text = JCoordinateField.this.getText();
Number num = numberFormat.parse(text, pos);
if (num == null) {
valid = false;
return;
}
double d = num.doubleValue();
valid = (!Double.isNaN(d)) && (d >= min) && (d <= max);
} catch (Exception e) {
valid = false;
}
if (valid != inputIsValid)
changeValidMode(valid);
}
private void changeValidMode(boolean valid) {
Color newC = valid ? defaultColor : ERROR_COLOR;
JCoordinateField.this.setBackground(newC);
String toolTip = valid ? "" : String.format(INVALID_TEXT, numberFormat.format(min),
numberFormat.format(max));
JCoordinateField.this.setToolTipText(toolTip);
if (toolTip.length() > 0)
Utilities.showTooltipNow(JCoordinateField.this);
inputIsValid = valid;
}
public void changedUpdate(DocumentEvent e) {
checkCoordinate(e);
}
public void insertUpdate(DocumentEvent e) {
checkCoordinate(e);
}
public void removeUpdate(DocumentEvent e) {
checkCoordinate(e);
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}