source: josm/trunk/src/org/openstreetmap/josm/gui/download/BoundingBoxSelection.java@ 13050

Last change on this file since 13050 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: 11.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.download;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Dimension;
8import java.awt.GridBagLayout;
9import java.awt.event.ActionEvent;
10import java.awt.event.ActionListener;
11import java.awt.event.FocusAdapter;
12import java.awt.event.FocusEvent;
13import java.awt.event.MouseAdapter;
14import java.awt.event.MouseEvent;
15
16import javax.swing.BorderFactory;
17import javax.swing.JButton;
18import javax.swing.JLabel;
19import javax.swing.JPanel;
20import javax.swing.UIManager;
21import javax.swing.border.Border;
22import javax.swing.event.DocumentEvent;
23import javax.swing.event.DocumentListener;
24import javax.swing.text.JTextComponent;
25
26import org.openstreetmap.josm.data.Bounds;
27import org.openstreetmap.josm.data.coor.LatLon;
28import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
29import org.openstreetmap.josm.gui.widgets.JosmTextArea;
30import org.openstreetmap.josm.gui.widgets.JosmTextField;
31import org.openstreetmap.josm.tools.GBC;
32import org.openstreetmap.josm.tools.JosmDecimalFormatSymbolsProvider;
33import org.openstreetmap.josm.tools.Logging;
34import org.openstreetmap.josm.tools.OsmUrlToBounds;
35
36/**
37 * Bounding box selector.
38 *
39 * Provides max/min lat/lon input fields as well as the "URL from www.openstreetmap.org" text field.
40 *
41 * @author Frederik Ramm
42 *
43 */
44public class BoundingBoxSelection implements DownloadSelection {
45
46 private JosmTextField[] latlon;
47 private final JosmTextArea tfOsmUrl = new JosmTextArea();
48 private final JosmTextArea showUrl = new JosmTextArea();
49 private DownloadDialog parent;
50
51 protected void registerBoundingBoxBuilder() {
52 BoundingBoxBuilder bboxbuilder = new BoundingBoxBuilder();
53 for (JosmTextField ll : latlon) {
54 ll.addFocusListener(bboxbuilder);
55 ll.addActionListener(bboxbuilder);
56 }
57 }
58
59 protected void buildDownloadAreaInputFields() {
60 latlon = new JosmTextField[4];
61 for (int i = 0; i < 4; i++) {
62 latlon[i] = new JosmTextField(11);
63 latlon[i].setMinimumSize(new Dimension(100, new JosmTextField().getMinimumSize().height));
64 latlon[i].addFocusListener(new SelectAllOnFocusHandler(latlon[i]));
65 }
66 LatValueChecker latChecker = new LatValueChecker(latlon[0]);
67 latlon[0].addFocusListener(latChecker);
68 latlon[0].addActionListener(latChecker);
69
70 latChecker = new LatValueChecker(latlon[2]);
71 latlon[2].addFocusListener(latChecker);
72 latlon[2].addActionListener(latChecker);
73
74 LonValueChecker lonChecker = new LonValueChecker(latlon[1]);
75 latlon[1].addFocusListener(lonChecker);
76 latlon[1].addActionListener(lonChecker);
77
78 lonChecker = new LonValueChecker(latlon[3]);
79 latlon[3].addFocusListener(lonChecker);
80 latlon[3].addActionListener(lonChecker);
81
82 registerBoundingBoxBuilder();
83 }
84
85 @Override
86 public void addGui(final DownloadDialog gui) {
87 buildDownloadAreaInputFields();
88 final JPanel dlg = new JPanel(new GridBagLayout());
89
90 tfOsmUrl.getDocument().addDocumentListener(new OsmUrlRefresher());
91
92 // select content on receiving focus. this seems to be the default in the
93 // windows look+feel but not for others. needs invokeLater to avoid strange
94 // side effects that will cancel out the newly made selection otherwise.
95 tfOsmUrl.addFocusListener(new SelectAllOnFocusHandler(tfOsmUrl));
96 tfOsmUrl.setLineWrap(true);
97 tfOsmUrl.setBorder(latlon[0].getBorder());
98
99 dlg.add(new JLabel(tr("min lat")), GBC.std().insets(10, 20, 5, 0));
100 dlg.add(latlon[0], GBC.std().insets(0, 20, 0, 0));
101 dlg.add(new JLabel(tr("min lon")), GBC.std().insets(10, 20, 5, 0));
102 dlg.add(latlon[1], GBC.eol().insets(0, 20, 0, 0));
103 dlg.add(new JLabel(tr("max lat")), GBC.std().insets(10, 0, 5, 0));
104 dlg.add(latlon[2], GBC.std());
105 dlg.add(new JLabel(tr("max lon")), GBC.std().insets(10, 0, 5, 0));
106 dlg.add(latlon[3], GBC.eol());
107
108 final JButton btnClear = new JButton(tr("Clear textarea"));
109 btnClear.addMouseListener(new MouseAdapter() {
110 @Override
111 public void mouseClicked(MouseEvent arg0) {
112 tfOsmUrl.setText("");
113 }
114 });
115 dlg.add(btnClear, GBC.eol().insets(10, 20, 0, 0));
116
117 dlg.add(new JLabel(tr("URL from www.openstreetmap.org (you can paste an URL here to download the area)")),
118 GBC.eol().insets(10, 5, 5, 0));
119 dlg.add(tfOsmUrl, GBC.eop().insets(10, 0, 5, 0).fill());
120 dlg.add(showUrl, GBC.eop().insets(10, 0, 5, 5));
121 showUrl.setEditable(false);
122 showUrl.setBackground(dlg.getBackground());
123 showUrl.addFocusListener(new SelectAllOnFocusHandler(showUrl));
124
125 if (gui != null)
126 gui.addDownloadAreaSelector(dlg, tr("Bounding Box"));
127 this.parent = gui;
128 }
129
130 @Override
131 public void setDownloadArea(Bounds area) {
132 updateBboxFields(area);
133 updateUrl(area);
134 }
135
136 /**
137 * Replies the download area.
138 * @return The download area
139 */
140 public Bounds getDownloadArea() {
141 double[] values = new double[4];
142 for (int i = 0; i < 4; i++) {
143 try {
144 values[i] = JosmDecimalFormatSymbolsProvider.parseDouble(latlon[i].getText());
145 } catch (NumberFormatException ex) {
146 return null;
147 }
148 }
149 if (!LatLon.isValidLat(values[0]) || !LatLon.isValidLon(values[1]))
150 return null;
151 if (!LatLon.isValidLat(values[2]) || !LatLon.isValidLon(values[3]))
152 return null;
153 return new Bounds(values);
154 }
155
156 private boolean parseURL(DownloadDialog gui) {
157 Bounds b = OsmUrlToBounds.parse(tfOsmUrl.getText());
158 if (b == null) return false;
159 gui.boundingBoxChanged(b, this);
160 updateBboxFields(b);
161 updateUrl(b);
162 return true;
163 }
164
165 private void updateBboxFields(Bounds area) {
166 if (area == null) return;
167 latlon[0].setText(DecimalDegreesCoordinateFormat.INSTANCE.latToString(area.getMin()));
168 latlon[1].setText(DecimalDegreesCoordinateFormat.INSTANCE.lonToString(area.getMin()));
169 latlon[2].setText(DecimalDegreesCoordinateFormat.INSTANCE.latToString(area.getMax()));
170 latlon[3].setText(DecimalDegreesCoordinateFormat.INSTANCE.lonToString(area.getMax()));
171 for (JosmTextField tf: latlon) {
172 resetErrorMessage(tf);
173 }
174 }
175
176 private void updateUrl(Bounds area) {
177 if (area == null) return;
178 showUrl.setText(OsmUrlToBounds.getURL(area));
179 }
180
181 private final Border errorBorder = BorderFactory.createLineBorder(Color.RED, 1);
182
183 protected void setErrorMessage(JosmTextField tf, String msg) {
184 tf.setBorder(errorBorder);
185 tf.setToolTipText(msg);
186 }
187
188 protected void resetErrorMessage(JosmTextField tf) {
189 tf.setBorder(UIManager.getBorder("TextField.border"));
190 tf.setToolTipText(null);
191 }
192
193 class LatValueChecker extends FocusAdapter implements ActionListener {
194 private final JosmTextField tfLatValue;
195
196 LatValueChecker(JosmTextField tfLatValue) {
197 this.tfLatValue = tfLatValue;
198 }
199
200 protected void check() {
201 double value = 0;
202 try {
203 value = JosmDecimalFormatSymbolsProvider.parseDouble(tfLatValue.getText());
204 } catch (NumberFormatException ex) {
205 setErrorMessage(tfLatValue, tr("The string ''{0}'' is not a valid double value.", tfLatValue.getText()));
206 return;
207 }
208 if (!LatLon.isValidLat(value)) {
209 setErrorMessage(tfLatValue, tr("Value for latitude in range [-90,90] required.", tfLatValue.getText()));
210 return;
211 }
212 resetErrorMessage(tfLatValue);
213 }
214
215 @Override
216 public void focusLost(FocusEvent e) {
217 check();
218 }
219
220 @Override
221 public void actionPerformed(ActionEvent e) {
222 check();
223 }
224 }
225
226 class LonValueChecker extends FocusAdapter implements ActionListener {
227 private final JosmTextField tfLonValue;
228
229 LonValueChecker(JosmTextField tfLonValue) {
230 this.tfLonValue = tfLonValue;
231 }
232
233 protected void check() {
234 double value = 0;
235 try {
236 value = JosmDecimalFormatSymbolsProvider.parseDouble(tfLonValue.getText());
237 } catch (NumberFormatException ex) {
238 setErrorMessage(tfLonValue, tr("The string ''{0}'' is not a valid double value.", tfLonValue.getText()));
239 return;
240 }
241 if (!LatLon.isValidLon(value)) {
242 setErrorMessage(tfLonValue, tr("Value for longitude in range [-180,180] required.", tfLonValue.getText()));
243 return;
244 }
245 resetErrorMessage(tfLonValue);
246 }
247
248 @Override
249 public void focusLost(FocusEvent e) {
250 check();
251 }
252
253 @Override
254 public void actionPerformed(ActionEvent e) {
255 check();
256 }
257 }
258
259 static class SelectAllOnFocusHandler extends FocusAdapter {
260 private final JTextComponent tfTarget;
261
262 SelectAllOnFocusHandler(JTextComponent tfTarget) {
263 this.tfTarget = tfTarget;
264 }
265
266 @Override
267 public void focusGained(FocusEvent e) {
268 tfTarget.selectAll();
269 }
270 }
271
272 class OsmUrlRefresher implements DocumentListener {
273 @Override
274 public void changedUpdate(DocumentEvent e) {
275 parseURL(parent);
276 }
277
278 @Override
279 public void insertUpdate(DocumentEvent e) {
280 parseURL(parent);
281 }
282
283 @Override
284 public void removeUpdate(DocumentEvent e) {
285 parseURL(parent);
286 }
287 }
288
289 class BoundingBoxBuilder extends FocusAdapter implements ActionListener {
290 protected Bounds build() {
291 double minlon, minlat, maxlon, maxlat;
292 try {
293 minlat = JosmDecimalFormatSymbolsProvider.parseDouble(latlon[0].getText().trim());
294 minlon = JosmDecimalFormatSymbolsProvider.parseDouble(latlon[1].getText().trim());
295 maxlat = JosmDecimalFormatSymbolsProvider.parseDouble(latlon[2].getText().trim());
296 maxlon = JosmDecimalFormatSymbolsProvider.parseDouble(latlon[3].getText().trim());
297 } catch (NumberFormatException e) {
298 Logging.trace(e);
299 return null;
300 }
301 if (!LatLon.isValidLon(minlon) || !LatLon.isValidLon(maxlon)
302 || !LatLon.isValidLat(minlat) || !LatLon.isValidLat(maxlat))
303 return null;
304 if (minlon > maxlon)
305 return null;
306 if (minlat > maxlat)
307 return null;
308 return new Bounds(minlat, minlon, maxlat, maxlon);
309 }
310
311 protected void refreshBounds() {
312 Bounds b = build();
313 parent.boundingBoxChanged(b, BoundingBoxSelection.this);
314 }
315
316 @Override
317 public void focusLost(FocusEvent e) {
318 refreshBounds();
319 }
320
321 @Override
322 public void actionPerformed(ActionEvent e) {
323 refreshBounds();
324 }
325 }
326}
Note: See TracBrowser for help on using the repository browser.