source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/LatLonDialog.java@ 12678

Last change on this file since 12678 was 12678, checked in by Don-vip, 7 years ago

see #15182 - move WindowGeometry from tools to gui.util

  • Property svn:eol-style set to native
File size: 13.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Component;
8import java.awt.GridBagLayout;
9import java.awt.event.FocusEvent;
10import java.awt.event.FocusListener;
11import java.awt.event.WindowAdapter;
12import java.awt.event.WindowEvent;
13import java.util.Arrays;
14import java.util.Optional;
15
16import javax.swing.BorderFactory;
17import javax.swing.JLabel;
18import javax.swing.JPanel;
19import javax.swing.JSeparator;
20import javax.swing.JTabbedPane;
21import javax.swing.UIManager;
22import javax.swing.event.DocumentEvent;
23import javax.swing.event.DocumentListener;
24
25import org.openstreetmap.josm.Main;
26import org.openstreetmap.josm.data.coor.CoordinateFormat;
27import org.openstreetmap.josm.data.coor.EastNorth;
28import org.openstreetmap.josm.data.coor.LatLon;
29import org.openstreetmap.josm.gui.ExtendedDialog;
30import org.openstreetmap.josm.gui.util.WindowGeometry;
31import org.openstreetmap.josm.gui.widgets.HtmlPanel;
32import org.openstreetmap.josm.gui.widgets.JosmTextField;
33import org.openstreetmap.josm.tools.GBC;
34import org.openstreetmap.josm.tools.Logging;
35import org.openstreetmap.josm.tools.Utils;
36
37/**
38 * A dialog that lets the user add a node at the coordinates he enters.
39 */
40public class LatLonDialog extends ExtendedDialog {
41 private static final Color BG_COLOR_ERROR = new Color(255, 224, 224);
42
43 /**
44 * The tabs that define the coordinate mode.
45 */
46 public JTabbedPane tabs;
47 private JosmTextField tfLatLon, tfEastNorth;
48 private LatLon latLonCoordinates;
49 private EastNorth eastNorthCoordinates;
50
51 protected JPanel buildLatLon() {
52 JPanel pnl = new JPanel(new GridBagLayout());
53 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
54
55 pnl.add(new JLabel(tr("Coordinates:")), GBC.std().insets(0, 10, 5, 0));
56 tfLatLon = new JosmTextField(24);
57 pnl.add(tfLatLon, GBC.eol().insets(0, 10, 0, 0).fill(GBC.HORIZONTAL).weight(1.0, 0.0));
58
59 pnl.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 5));
60
61 pnl.add(new HtmlPanel(
62 Utils.join("<br/>", Arrays.asList(
63 tr("Enter the coordinates for the new node."),
64 tr("You can separate longitude and latitude with space, comma or semicolon."),
65 tr("Use positive numbers or N, E characters to indicate North or East cardinal direction."),
66 tr("For South and West cardinal directions you can use either negative numbers or S, W characters."),
67 tr("Coordinate value can be in one of three formats:")
68 )) +
69 Utils.joinAsHtmlUnorderedList(Arrays.asList(
70 tr("<i>degrees</i><tt>&deg;</tt>"),
71 tr("<i>degrees</i><tt>&deg;</tt> <i>minutes</i><tt>&#39;</tt>"),
72 tr("<i>degrees</i><tt>&deg;</tt> <i>minutes</i><tt>&#39;</tt> <i>seconds</i><tt>&quot</tt>")
73 )) +
74 Utils.join("<br/><br/>", Arrays.asList(
75 tr("Symbols <tt>&deg;</tt>, <tt>&#39;</tt>, <tt>&prime;</tt>, <tt>&quot;</tt>, <tt>&Prime;</tt> are optional."),
76 tr("You can also use the syntax <tt>lat=\"...\" lon=\"...\"</tt> or <tt>lat=''...'' lon=''...''</tt>."),
77 tr("Some examples:")
78 )) +
79 "<table><tr><td>" +
80 Utils.joinAsHtmlUnorderedList(Arrays.asList(
81 "49.29918&deg; 19.24788&deg;",
82 "N 49.29918 E 19.24788",
83 "W 49&deg;29.918&#39; S 19&deg;24.788&#39;",
84 "N 49&deg;29&#39;04&quot; E 19&deg;24&#39;43&quot;",
85 "49.29918 N, 19.24788 E",
86 "49&deg;29&#39;21&quot; N 19&deg;24&#39;38&quot; E",
87 "49 29 51, 19 24 18",
88 "49 29, 19 24",
89 "E 49 29, N 19 24"
90 )) +
91 "</td><td>" +
92 Utils.joinAsHtmlUnorderedList(Arrays.asList(
93 "49&deg; 29; 19&deg; 24",
94 "N 49&deg; 29, W 19&deg; 24",
95 "49&deg; 29.5 S, 19&deg; 24.6 E",
96 "N 49 29.918 E 19 15.88",
97 "49 29.4 19 24.5",
98 "-49 29.4 N -19 24.5 W",
99 "48 deg 42&#39; 52.13\" N, 21 deg 11&#39; 47.60\" E",
100 "lat=\"49.29918\" lon=\"19.24788\"",
101 "lat='49.29918' lon='19.24788'"
102 )) +
103 "</td></tr></table>"),
104 GBC.eol().fill().weight(1.0, 1.0));
105
106 // parse and verify input on the fly
107 //
108 LatLonInputVerifier inputVerifier = new LatLonInputVerifier();
109 tfLatLon.getDocument().addDocumentListener(inputVerifier);
110
111 // select the text in the field on focus
112 //
113 TextFieldFocusHandler focusHandler = new TextFieldFocusHandler();
114 tfLatLon.addFocusListener(focusHandler);
115 return pnl;
116 }
117
118 private JPanel buildEastNorth() {
119 JPanel pnl = new JPanel(new GridBagLayout());
120 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
121
122 pnl.add(new JLabel(tr("Projected coordinates:")), GBC.std().insets(0, 10, 5, 0));
123 tfEastNorth = new JosmTextField(24);
124
125 pnl.add(tfEastNorth, GBC.eol().insets(0, 10, 0, 0).fill(GBC.HORIZONTAL).weight(1.0, 0.0));
126
127 pnl.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 5));
128
129 pnl.add(new HtmlPanel(
130 tr("Enter easting and northing (x and y) separated by space, comma or semicolon.")),
131 GBC.eol().fill(GBC.HORIZONTAL));
132
133 pnl.add(GBC.glue(1, 1), GBC.eol().fill().weight(1.0, 1.0));
134
135 EastNorthInputVerifier inputVerifier = new EastNorthInputVerifier();
136 tfEastNorth.getDocument().addDocumentListener(inputVerifier);
137
138 TextFieldFocusHandler focusHandler = new TextFieldFocusHandler();
139 tfEastNorth.addFocusListener(focusHandler);
140
141 return pnl;
142 }
143
144 protected void build() {
145 tabs = new JTabbedPane();
146 tabs.addTab(tr("Lat/Lon"), buildLatLon());
147 tabs.addTab(tr("East/North"), buildEastNorth());
148 tabs.getModel().addChangeListener(e -> {
149 switch (tabs.getModel().getSelectedIndex()) {
150 case 0: parseLatLonUserInput(); break;
151 case 1: parseEastNorthUserInput(); break;
152 default: throw new AssertionError();
153 }
154 });
155 setContent(tabs, false);
156 addWindowListener(new WindowAdapter() {
157 @Override
158 public void windowOpened(WindowEvent e) {
159 tfLatLon.requestFocusInWindow();
160 }
161 });
162 }
163
164 /**
165 * Creates a new {@link LatLonDialog}
166 * @param parent The parent
167 * @param title The title of this dialog
168 * @param help The help text to use
169 */
170 public LatLonDialog(Component parent, String title, String help) {
171 super(parent, title, tr("Ok"), tr("Cancel"));
172 setButtonIcons("ok", "cancel");
173 configureContextsensitiveHelp(help, true);
174
175 build();
176 setCoordinates(null);
177 }
178
179 /**
180 * Check if lat/lon mode is active
181 * @return <code>true</code> iff the user selects lat/lon coordinates
182 */
183 public boolean isLatLon() {
184 return tabs.getModel().getSelectedIndex() == 0;
185 }
186
187 /**
188 * Sets the coordinate fields to the given coordinates
189 * @param ll The lat/lon coordinates
190 */
191 public void setCoordinates(LatLon ll) {
192 LatLon llc = Optional.ofNullable(ll).orElse(LatLon.ZERO);
193 tfLatLon.setText(llc.latToString(CoordinateFormat.getDefaultFormat()) + ' ' +
194 llc.lonToString(CoordinateFormat.getDefaultFormat()));
195 EastNorth en = Main.getProjection().latlon2eastNorth(llc);
196 tfEastNorth.setText(Double.toString(en.east()) + ' ' + Double.toString(en.north()));
197 // Both latLonCoordinates and eastNorthCoordinates may have been reset to null if ll is out of the world
198 latLonCoordinates = llc;
199 eastNorthCoordinates = en;
200 setOkEnabled(true);
201 }
202
203 /**
204 * Gets the coordinates that are entered by the user.
205 * @return The coordinates
206 */
207 public LatLon getCoordinates() {
208 if (isLatLon()) {
209 return latLonCoordinates;
210 } else {
211 if (eastNorthCoordinates == null) return null;
212 return Main.getProjection().eastNorth2latlon(eastNorthCoordinates);
213 }
214 }
215
216 /**
217 * Gets the coordinates that are entered in the lat/lon field
218 * @return The lat/lon coordinates
219 */
220 public LatLon getLatLonCoordinates() {
221 return latLonCoordinates;
222 }
223
224 /**
225 * Gets the coordinates that are entered in the east/north field
226 * @return The east/north coordinates
227 */
228 public EastNorth getEastNorthCoordinates() {
229 return eastNorthCoordinates;
230 }
231
232 protected void setErrorFeedback(JosmTextField tf, String message) {
233 tf.setBorder(BorderFactory.createLineBorder(Color.RED, 1));
234 tf.setToolTipText(message);
235 tf.setBackground(BG_COLOR_ERROR);
236 }
237
238 protected void clearErrorFeedback(JosmTextField tf, String message) {
239 tf.setBorder(UIManager.getBorder("TextField.border"));
240 tf.setToolTipText(message);
241 tf.setBackground(UIManager.getColor("TextField.background"));
242 }
243
244 protected void parseLatLonUserInput() {
245 LatLon latLon;
246 try {
247 latLon = LatLon.parse(tfLatLon.getText());
248 if (!LatLon.isValidLat(latLon.lat()) || !LatLon.isValidLon(latLon.lon())) {
249 latLon = null;
250 }
251 } catch (IllegalArgumentException e) {
252 Logging.trace(e);
253 latLon = null;
254 }
255 if (latLon == null) {
256 setErrorFeedback(tfLatLon, tr("Please enter a GPS coordinates"));
257 latLonCoordinates = null;
258 setOkEnabled(false);
259 } else {
260 clearErrorFeedback(tfLatLon, tr("Please enter a GPS coordinates"));
261 latLonCoordinates = latLon;
262 setOkEnabled(true);
263 }
264 }
265
266 protected void parseEastNorthUserInput() {
267 EastNorth en;
268 try {
269 en = parseEastNorth(tfEastNorth.getText());
270 } catch (IllegalArgumentException e) {
271 Logging.trace(e);
272 en = null;
273 }
274 if (en == null) {
275 setErrorFeedback(tfEastNorth, tr("Please enter a Easting and Northing"));
276 latLonCoordinates = null;
277 setOkEnabled(false);
278 } else {
279 clearErrorFeedback(tfEastNorth, tr("Please enter a Easting and Northing"));
280 eastNorthCoordinates = en;
281 setOkEnabled(true);
282 }
283 }
284
285 private void setOkEnabled(boolean b) {
286 if (buttons != null && !buttons.isEmpty()) {
287 buttons.get(0).setEnabled(b);
288 }
289 }
290
291 @Override
292 public void setVisible(boolean visible) {
293 final String preferenceKey = getClass().getName() + ".geometry";
294 if (visible) {
295 new WindowGeometry(
296 preferenceKey,
297 WindowGeometry.centerInWindow(getParent(), getSize())
298 ).applySafe(this);
299 } else {
300 new WindowGeometry(this).remember(preferenceKey);
301 }
302 super.setVisible(visible);
303 }
304
305 class LatLonInputVerifier implements DocumentListener {
306 @Override
307 public void changedUpdate(DocumentEvent e) {
308 parseLatLonUserInput();
309 }
310
311 @Override
312 public void insertUpdate(DocumentEvent e) {
313 parseLatLonUserInput();
314 }
315
316 @Override
317 public void removeUpdate(DocumentEvent e) {
318 parseLatLonUserInput();
319 }
320 }
321
322 class EastNorthInputVerifier implements DocumentListener {
323 @Override
324 public void changedUpdate(DocumentEvent e) {
325 parseEastNorthUserInput();
326 }
327
328 @Override
329 public void insertUpdate(DocumentEvent e) {
330 parseEastNorthUserInput();
331 }
332
333 @Override
334 public void removeUpdate(DocumentEvent e) {
335 parseEastNorthUserInput();
336 }
337 }
338
339 static class TextFieldFocusHandler implements FocusListener {
340 @Override
341 public void focusGained(FocusEvent e) {
342 Component c = e.getComponent();
343 if (c instanceof JosmTextField) {
344 JosmTextField tf = (JosmTextField) c;
345 tf.selectAll();
346 }
347 }
348
349 @Override
350 public void focusLost(FocusEvent e) {
351 // Not used
352 }
353 }
354
355 /**
356 * Parses a east/north coordinate string
357 * @param s The coordinates
358 * @return The east/north coordinates or <code>null</code> on error.
359 */
360 public static EastNorth parseEastNorth(String s) {
361 String[] en = s.split("[;, ]+");
362 if (en.length != 2) return null;
363 try {
364 double east = Double.parseDouble(en[0]);
365 double north = Double.parseDouble(en[1]);
366 return new EastNorth(east, north);
367 } catch (NumberFormatException nfe) {
368 return null;
369 }
370 }
371
372 /**
373 * Gets the text entered in the lat/lon text field.
374 * @return The text the user entered
375 */
376 public String getLatLonText() {
377 return tfLatLon.getText();
378 }
379
380 /**
381 * Set the text in the lat/lon text field.
382 * @param text The new text
383 */
384 public void setLatLonText(String text) {
385 tfLatLon.setText(text);
386 }
387
388 /**
389 * Gets the text entered in the east/north text field.
390 * @return The text the user entered
391 */
392 public String getEastNorthText() {
393 return tfEastNorth.getText();
394 }
395
396 /**
397 * Set the text in the east/north text field.
398 * @param text The new text
399 */
400 public void setEastNorthText(String text) {
401 tfEastNorth.setText(text);
402 }
403}
Note: See TracBrowser for help on using the repository browser.