/******************************************************************************* * 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; } } }