source: josm/trunk/src/org/openstreetmap/josm/gui/widgets/BoundingBoxSelectionPanel.java@ 13652

Last change on this file since 13652 was 13050, checked in by Don-vip, 6 years ago

fix #14602 - allow both dot and comma decimal separator everywhere possible for user-entered values

  • Property svn:eol-style set to native
File size: 8.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.widgets;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Dimension;
7import java.awt.GridBagConstraints;
8import java.awt.GridBagLayout;
9import java.awt.Insets;
10
11import javax.swing.BorderFactory;
12import javax.swing.JLabel;
13import javax.swing.JPanel;
14import javax.swing.event.DocumentEvent;
15import javax.swing.event.DocumentListener;
16import javax.swing.text.JTextComponent;
17
18import org.openstreetmap.josm.data.Bounds;
19import org.openstreetmap.josm.data.coor.LatLon;
20import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
21import org.openstreetmap.josm.tools.GBC;
22import org.openstreetmap.josm.tools.JosmDecimalFormatSymbolsProvider;
23import org.openstreetmap.josm.tools.Logging;
24import org.openstreetmap.josm.tools.OsmUrlToBounds;
25
26/**
27 * A panel that allows the user to input the coordinates of a lat/lon box
28 */
29public class BoundingBoxSelectionPanel extends JPanel {
30
31 private JosmTextField[] tfLatLon;
32 private final JosmTextField tfOsmUrl = new JosmTextField();
33
34 protected void buildInputFields() {
35 tfLatLon = new JosmTextField[4];
36 for (int i = 0; i < 4; i++) {
37 tfLatLon[i] = new JosmTextField(11);
38 tfLatLon[i].setMinimumSize(new Dimension(100, new JosmTextField().getMinimumSize().height));
39 SelectAllOnFocusGainedDecorator.decorate(tfLatLon[i]);
40 }
41 LatitudeValidator.decorate(tfLatLon[0]);
42 LatitudeValidator.decorate(tfLatLon[2]);
43 LongitudeValidator.decorate(tfLatLon[1]);
44 LongitudeValidator.decorate(tfLatLon[3]);
45 }
46
47 protected final void build() {
48 buildInputFields();
49 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
50 setLayout(new GridBagLayout());
51 tfOsmUrl.getDocument().addDocumentListener(new OsmUrlRefresher());
52
53 // select content on receiving focus. this seems to be the default in the
54 // windows look+feel but not for others. needs invokeLater to avoid strange
55 // side effects that will cancel out the newly made selection otherwise.
56 tfOsmUrl.addFocusListener(new SelectAllOnFocusGainedDecorator());
57
58 add(new JLabel(tr("Min. latitude")), GBC.std().insets(0, 0, 3, 5));
59 add(tfLatLon[0], GBC.std().insets(0, 0, 3, 5));
60 add(new JLabel(tr("Min. longitude")), GBC.std().insets(0, 0, 3, 5));
61 add(tfLatLon[1], GBC.eol());
62 add(new JLabel(tr("Max. latitude")), GBC.std().insets(0, 0, 3, 5));
63 add(tfLatLon[2], GBC.std().insets(0, 0, 3, 5));
64 add(new JLabel(tr("Max. longitude")), GBC.std().insets(0, 0, 3, 5));
65 add(tfLatLon[3], GBC.eol());
66
67 GridBagConstraints gc = new GridBagConstraints();
68 gc.gridx = 0;
69 gc.gridy = 2;
70 gc.gridwidth = 4;
71 gc.fill = GridBagConstraints.HORIZONTAL;
72 gc.weightx = 1.0;
73 gc.insets = new Insets(10, 0, 0, 3);
74 add(new JMultilineLabel(tr("URL from www.openstreetmap.org (you can paste a download URL here to specify a bounding box)")), gc);
75
76 gc.gridy = 3;
77 gc.insets = new Insets(3, 0, 0, 3);
78 add(tfOsmUrl, gc);
79 }
80
81 /**
82 * Constructs a new {@code BoundingBoxSelectionPanel}.
83 */
84 public BoundingBoxSelectionPanel() {
85 build();
86 }
87
88 /**
89 * Sets the bounding box to the given area
90 * @param area The new input values
91 */
92 public void setBoundingBox(Bounds area) {
93 updateBboxFields(area);
94 }
95
96 /**
97 * Get the bounding box the user selected
98 * @return The box or <code>null</code> if no valid data was input.
99 */
100 public Bounds getBoundingBox() {
101 double minlon, minlat, maxlon, maxlat;
102 try {
103 minlat = JosmDecimalFormatSymbolsProvider.parseDouble(tfLatLon[0].getText().trim());
104 minlon = JosmDecimalFormatSymbolsProvider.parseDouble(tfLatLon[1].getText().trim());
105 maxlat = JosmDecimalFormatSymbolsProvider.parseDouble(tfLatLon[2].getText().trim());
106 maxlon = JosmDecimalFormatSymbolsProvider.parseDouble(tfLatLon[3].getText().trim());
107 } catch (NumberFormatException e) {
108 Logging.trace(e);
109 return null;
110 }
111 if (!LatLon.isValidLon(minlon) || !LatLon.isValidLon(maxlon)
112 || !LatLon.isValidLat(minlat) || !LatLon.isValidLat(maxlat))
113 return null;
114 if (minlon > maxlon)
115 return null;
116 if (minlat > maxlat)
117 return null;
118 return new Bounds(minlon, minlat, maxlon, maxlat);
119 }
120
121 private boolean parseURL() {
122 Bounds b = OsmUrlToBounds.parse(tfOsmUrl.getText());
123 if (b == null) return false;
124 updateBboxFields(b);
125 return true;
126 }
127
128 private void updateBboxFields(Bounds area) {
129 if (area == null) return;
130 tfLatLon[0].setText(DecimalDegreesCoordinateFormat.INSTANCE.latToString(area.getMin()));
131 tfLatLon[1].setText(DecimalDegreesCoordinateFormat.INSTANCE.lonToString(area.getMin()));
132 tfLatLon[2].setText(DecimalDegreesCoordinateFormat.INSTANCE.latToString(area.getMax()));
133 tfLatLon[3].setText(DecimalDegreesCoordinateFormat.INSTANCE.lonToString(area.getMax()));
134 }
135
136 private static class LatitudeValidator extends AbstractTextComponentValidator {
137
138 public static void decorate(JTextComponent tc) {
139 new LatitudeValidator(tc);
140 }
141
142 LatitudeValidator(JTextComponent tc) {
143 super(tc);
144 }
145
146 @Override
147 public void validate() {
148 double value = 0;
149 try {
150 value = JosmDecimalFormatSymbolsProvider.parseDouble(getComponent().getText());
151 } catch (NumberFormatException ex) {
152 feedbackInvalid(tr("The string ''{0}'' is not a valid double value.", getComponent().getText()));
153 Logging.trace(ex);
154 return;
155 }
156 if (!LatLon.isValidLat(value)) {
157 feedbackInvalid(tr("Value for latitude in range [-90,90] required.", getComponent().getText()));
158 return;
159 }
160 feedbackValid("");
161 }
162
163 @Override
164 public boolean isValid() {
165 try {
166 return LatLon.isValidLat(JosmDecimalFormatSymbolsProvider.parseDouble(getComponent().getText()));
167 } catch (NumberFormatException ex) {
168 Logging.trace(ex);
169 return false;
170 }
171 }
172 }
173
174 private static class LongitudeValidator extends AbstractTextComponentValidator {
175
176 public static void decorate(JTextComponent tc) {
177 new LongitudeValidator(tc);
178 }
179
180 LongitudeValidator(JTextComponent tc) {
181 super(tc);
182 }
183
184 @Override
185 public void validate() {
186 double value = 0;
187 try {
188 value = JosmDecimalFormatSymbolsProvider.parseDouble(getComponent().getText());
189 } catch (NumberFormatException ex) {
190 feedbackInvalid(tr("The string ''{0}'' is not a valid double value.", getComponent().getText()));
191 Logging.trace(ex);
192 return;
193 }
194 if (!LatLon.isValidLon(value)) {
195 feedbackInvalid(tr("Value for longitude in range [-180,180] required.", getComponent().getText()));
196 return;
197 }
198 feedbackValid("");
199 }
200
201 @Override
202 public boolean isValid() {
203 try {
204 return LatLon.isValidLon(JosmDecimalFormatSymbolsProvider.parseDouble(getComponent().getText()));
205 } catch (NumberFormatException ex) {
206 Logging.trace(ex);
207 return false;
208 }
209 }
210 }
211
212 class OsmUrlRefresher implements DocumentListener {
213 @Override
214 public void changedUpdate(DocumentEvent e) {
215 parseURL();
216 }
217
218 @Override
219 public void insertUpdate(DocumentEvent e) {
220 parseURL();
221 }
222
223 @Override
224 public void removeUpdate(DocumentEvent e) {
225 parseURL();
226 }
227 }
228}
Note: See TracBrowser for help on using the repository browser.