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

Last change on this file since 3363 was 3363, checked in by stoecker, 14 years ago

fix #5204 - wrong window title

File size: 10.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.BorderLayout;
8import java.awt.Color;
9import java.awt.Component;
10import java.awt.FlowLayout;
11import java.awt.GridBagLayout;
12import java.awt.event.ActionEvent;
13import java.awt.event.FocusEvent;
14import java.awt.event.FocusListener;
15import java.awt.event.KeyEvent;
16import java.awt.event.WindowAdapter;
17import java.awt.event.WindowEvent;
18import java.text.NumberFormat;
19import java.text.ParsePosition;
20import java.util.Locale;
21
22import javax.swing.AbstractAction;
23import javax.swing.BorderFactory;
24import javax.swing.JComponent;
25import javax.swing.JDialog;
26import javax.swing.JLabel;
27import javax.swing.JOptionPane;
28import javax.swing.JPanel;
29import javax.swing.JTextField;
30import javax.swing.KeyStroke;
31import javax.swing.UIManager;
32import javax.swing.event.DocumentEvent;
33import javax.swing.event.DocumentListener;
34
35import org.openstreetmap.josm.Main;
36import org.openstreetmap.josm.data.coor.CoordinateFormat;
37import org.openstreetmap.josm.data.coor.LatLon;
38import org.openstreetmap.josm.gui.SideButton;
39import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
40import org.openstreetmap.josm.gui.help.HelpUtil;
41import org.openstreetmap.josm.tools.GBC;
42import org.openstreetmap.josm.tools.ImageProvider;
43import org.openstreetmap.josm.tools.WindowGeometry;
44
45public class LatLonDialog extends JDialog {
46 private static final Color BG_COLOR_ERROR = new Color(255,224,224);
47
48 private JTextField tfLat;
49 private JTextField tfLon;
50 private String help;
51 private boolean canceled = false;
52 private LatLon coordinates;
53 private OKAction actOK;
54 private CancelAction actCancel;
55
56 protected JPanel buildInputForm() {
57 JPanel pnl = new JPanel(new GridBagLayout());
58 pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
59 pnl.add(new JLabel("<html>"+
60 tr("Enter the coordinates for the new node.") +
61 "<br>" + tr("Use decimal degrees.") +
62 "<br>" + tr("Negative values denote Western/Southern hemisphere.")),
63 GBC.eol());
64
65 pnl.add(new JLabel(tr("Latitude")), GBC.std().insets(0,10,5,0));
66 tfLat = new JTextField(12);
67 pnl.add(tfLat, GBC.eol().insets(0,10,0,0));
68 pnl.add(new JLabel(tr("Longitude")), GBC.std().insets(0,0,5,10));
69 tfLon = new JTextField(12);
70 pnl.add(tfLon, GBC.eol().insets(0,0,0,10));
71
72 // parse and verify input on the fly
73 //
74 LatLonInputVerifier inputVerifier = new LatLonInputVerifier();
75 tfLat.getDocument().addDocumentListener(inputVerifier);
76 tfLon.getDocument().addDocumentListener(inputVerifier);
77
78 // select the text in the field on focus
79 //
80 TextFieldFocusHandler focusHandler = new TextFieldFocusHandler();
81 tfLat.addFocusListener(focusHandler);
82 tfLon.addFocusListener(focusHandler);
83 return pnl;
84 }
85
86 protected JPanel buildButtonRow() {
87 JPanel pnl = new JPanel(new FlowLayout());
88
89 SideButton btn;
90 pnl.add(btn = new SideButton(actOK = new OKAction()));
91 makeButtonRespondToEnter(btn);
92 pnl.add(btn = new SideButton(actCancel = new CancelAction()));
93 makeButtonRespondToEnter(btn);
94 pnl.add(new SideButton(new ContextSensitiveHelpAction(help)));
95 return pnl;
96 }
97
98 protected void makeButtonRespondToEnter(SideButton btn) {
99 btn.setFocusable(true);
100 btn.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter");
101 btn.getActionMap().put("enter", btn.getAction());
102 }
103
104 protected void build() {
105 getContentPane().setLayout(new BorderLayout());
106 getContentPane().add(buildInputForm(), BorderLayout.CENTER);
107 getContentPane().add(buildButtonRow(), BorderLayout.SOUTH);
108 pack();
109
110 // make dialog respond to ESCAPE
111 //
112 getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0), "escape");
113 getRootPane().getActionMap().put("escape", actCancel);
114
115 // make dialog respond to F1
116 //
117 HelpUtil.setHelpContext(getRootPane(), help);
118 }
119
120 public LatLonDialog(Component parent, String title, String help) {
121 super(JOptionPane.getFrameForComponent(parent), true /* modal */);
122 this.help = help;
123 setTitle(title);
124 build();
125 addWindowListener(new WindowEventHandler());
126 setCoordinates(null);
127 }
128
129 public void setCoordinates(LatLon coordinates) {
130 if (coordinates == null) {
131 coordinates = new LatLon(0,0);
132 }
133 this.coordinates = coordinates;
134 tfLat.setText(coordinates.latToString(CoordinateFormat.DECIMAL_DEGREES));
135 tfLon.setText(coordinates.lonToString(CoordinateFormat.DECIMAL_DEGREES));
136 actOK.setEnabled(true);
137 }
138
139 public LatLon getCoordinates() {
140 return coordinates;
141 }
142
143 protected void setErrorFeedback(JTextField tf, String message) {
144 tf.setBorder(BorderFactory.createLineBorder(Color.RED, 1));
145 tf.setToolTipText(message);
146 tf.setBackground(BG_COLOR_ERROR);
147 }
148
149 protected void clearErrorFeedback(JTextField tf, String message) {
150 tf.setBorder(UIManager.getBorder("TextField.border"));
151 tf.setToolTipText(message);
152 tf.setBackground(UIManager.getColor("TextField.background"));
153 }
154
155 protected Double parseDoubleFromUserInput(String input) {
156 if (input == null) return null;
157 // remove white space and an optional degree symbol
158 //
159 input = input.trim();
160 input = input.replaceAll("\u00B0", ""); // the degree symbol
161
162 // try to parse using the current locale
163 //
164 NumberFormat f = NumberFormat.getNumberInstance();
165 Number n=null;
166 ParsePosition pp = new ParsePosition(0);
167 n = f.parse(input,pp);
168 if (pp.getErrorIndex() >= 0 || pp.getIndex()<input.length()) {
169 // fall back - try to parse with the english locale
170 //
171 pp = new ParsePosition(0);
172 f = NumberFormat.getNumberInstance(Locale.ENGLISH);
173 n = f.parse(input, pp);
174 if (pp.getErrorIndex() >= 0 || pp.getIndex()<input.length())
175 return null;
176 }
177 return n== null ? null : n.doubleValue();
178 }
179
180 protected Double parseLatFromUserInput() {
181 Double d = parseDoubleFromUserInput(tfLat.getText());
182 if (d == null || ! LatLon.isValidLat(d)) {
183 setErrorFeedback(tfLat, tr("Please enter a valid latitude in the range -90..90"));
184 return null;
185 } else {
186 clearErrorFeedback(tfLat,tr("Please enter a latitude in the range -90..90"));
187 }
188 return d;
189 }
190
191 protected Double parseLonFromUserInput() {
192 Double d = parseDoubleFromUserInput(tfLon.getText());
193 if (d == null || ! LatLon.isValidLon(d)) {
194 setErrorFeedback(tfLon, tr("Please enter a valid longitude in the range -180..180"));
195 return null;
196 } else {
197 clearErrorFeedback(tfLon,tr("Please enter a longitude in the range -180..180"));
198 }
199 return d;
200 }
201
202 protected void parseUserInput() {
203 Double lat = parseLatFromUserInput();
204 Double lon = parseLonFromUserInput();
205 if (lat == null || lon == null) {
206 coordinates = null;
207 actOK.setEnabled(false);
208 } else {
209 coordinates = new LatLon(lat,lon);
210 actOK.setEnabled(true);
211 }
212 }
213
214 public boolean isCanceled() {
215 return canceled;
216 }
217
218 protected void setCanceled(boolean canceled) {
219 this.canceled = canceled;
220 }
221
222 @Override
223 public void setVisible(boolean visible) {
224 if (visible) {
225 setCanceled(false);
226 WindowGeometry.centerInWindow(Main.parent, getSize()).applySafe(this);
227 }
228 super.setVisible(visible);
229 }
230
231 class OKAction extends AbstractAction {
232 public OKAction() {
233 putValue(NAME, tr("OK"));
234 putValue(SHORT_DESCRIPTION, tr("Close the dialog and create a new node"));
235 putValue(SMALL_ICON, ImageProvider.get("ok"));
236 }
237
238 public void actionPerformed(ActionEvent e) {
239 setCanceled(false);
240 setVisible(false);
241 }
242 }
243
244 class CancelAction extends AbstractAction {
245 public CancelAction() {
246 putValue(NAME, tr("Cancel"));
247 putValue(SHORT_DESCRIPTION, tr("Close the dialog, do not create a new node"));
248 putValue(SMALL_ICON, ImageProvider.get("cancel"));
249 }
250
251 public void actionPerformed(ActionEvent e) {
252 setCanceled(true);
253 setVisible(false);
254 }
255 }
256
257 class LatLonInputVerifier implements DocumentListener {
258 public void changedUpdate(DocumentEvent e) {
259 parseUserInput();
260 }
261
262 public void insertUpdate(DocumentEvent e) {
263 parseUserInput();
264 }
265
266 public void removeUpdate(DocumentEvent e) {
267 parseUserInput();
268 }
269 }
270
271 static class TextFieldFocusHandler implements FocusListener {
272 public void focusGained(FocusEvent e) {
273 Component c = e.getComponent();
274 if (c instanceof JTextField) {
275 JTextField tf = (JTextField)c;
276 tf.selectAll();
277 }
278 }
279 public void focusLost(FocusEvent e) {}
280 }
281
282 class WindowEventHandler extends WindowAdapter {
283 @Override
284 public void windowClosing(WindowEvent e) {
285 setCanceled(true);
286 setVisible(false);
287 }
288
289 @Override
290 public void windowOpened(WindowEvent e) {
291 tfLat.requestFocusInWindow();
292 }
293 }
294
295}
Note: See TracBrowser for help on using the repository browser.