Changeset 2543 in josm
- Timestamp:
- 2009-11-29T11:36:48+01:00 (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/AddNodeAction.java
r2531 r2543 5 5 import static org.openstreetmap.josm.tools.I18n.tr; 6 6 7 import java.awt.BorderLayout; 8 import java.awt.Color; 9 import java.awt.Component; 10 import java.awt.FlowLayout; 7 11 import java.awt.GridBagLayout; 8 12 import java.awt.event.ActionEvent; 13 import java.awt.event.FocusEvent; 14 import java.awt.event.FocusListener; 9 15 import java.awt.event.KeyEvent; 10 16 import java.awt.event.WindowAdapter; 11 17 import java.awt.event.WindowEvent; 12 18 import java.text.NumberFormat; 19 import java.text.ParsePosition; 20 import java.util.Locale; 21 import java.util.logging.Logger; 22 23 import javax.swing.AbstractAction; 24 import javax.swing.BorderFactory; 25 import javax.swing.JComponent; 13 26 import javax.swing.JDialog; 14 27 import javax.swing.JLabel; … … 16 29 import javax.swing.JPanel; 17 30 import javax.swing.JTextField; 31 import javax.swing.KeyStroke; 32 import javax.swing.UIManager; 33 import javax.swing.event.DocumentEvent; 34 import javax.swing.event.DocumentListener; 18 35 19 36 import org.openstreetmap.josm.Main; 20 37 import org.openstreetmap.josm.command.AddCommand; 38 import org.openstreetmap.josm.data.coor.CoordinateFormat; 21 39 import org.openstreetmap.josm.data.coor.LatLon; 22 40 import org.openstreetmap.josm.data.osm.Node; 41 import org.openstreetmap.josm.gui.SideButton; 42 import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction; 43 import org.openstreetmap.josm.gui.help.HelpUtil; 23 44 import org.openstreetmap.josm.tools.GBC; 45 import org.openstreetmap.josm.tools.ImageProvider; 24 46 import org.openstreetmap.josm.tools.Shortcut; 47 import org.openstreetmap.josm.tools.WindowGeometry; 25 48 26 49 /** … … 29 52 */ 30 53 public final class AddNodeAction extends JosmAction { 54 static private final Logger logger = Logger.getLogger(AddNodeAction.class.getName()); 31 55 32 56 public AddNodeAction() { … … 37 61 } 38 62 39 private String normalizeUserInputForLatLonValue(String value) {40 if (value == null) return null;41 value = value.trim();42 return value.replaceAll("\u00B0", ""); // the degree symbol43 }44 63 45 64 public void actionPerformed(ActionEvent e) { 46 // we abort if we are not in the context of an OsmDataLayer47 //48 65 if (!isEnabled()) 49 66 return; 50 67 51 JPanel p = new JPanel(new GridBagLayout()); 52 p.add(new JLabel("<html>"+ 53 tr("Enter the coordinates for the new node.") + 54 "<br>" + tr("Use decimal degrees.") + 55 "<br>" + tr("Negative values denote Western/Southern hemisphere.")), 56 GBC.eol()); 57 58 p.add(new JLabel(tr("Latitude")), GBC.std().insets(0,10,5,0)); 59 final JTextField lat = new JTextField(12); 60 p.add(lat, GBC.eol().insets(0,10,0,0)); 61 p.add(new JLabel(tr("Longitude")), GBC.std().insets(0,0,5,10)); 62 final JTextField lon = new JTextField(12); 63 p.add(lon, GBC.eol().insets(0,0,0,10)); 64 65 Node nnew = null; 66 67 while(nnew == null) { 68 JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION); 69 JDialog dialog = pane.createDialog(Main.parent, tr("Add Node...")); 70 dialog.addWindowListener(new WindowAdapter() { 71 @Override 72 public void windowGainedFocus(WindowEvent e) { 73 lat.selectAll(); 74 lat.requestFocusInWindow(); 75 } 76 } 77 ); 78 dialog.setVisible(true); 79 80 if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue())) 81 return; 82 try { 83 LatLon ll = new LatLon( 84 Double.parseDouble( 85 normalizeUserInputForLatLonValue(lat.getText()) 86 ), 87 Double.parseDouble( 88 normalizeUserInputForLatLonValue(lon.getText()) 89 ) 90 ); 91 if (!ll.isOutSideWorld()) { 92 nnew = new Node(ll); 93 } 94 } catch (Exception ex) { 95 ex.printStackTrace(); 96 JOptionPane.showMessageDialog( 97 Main.parent, 98 tr("Could not create a node with the entered latitude/longitude."), 99 tr("Illegal lat/lon value"), 100 JOptionPane.ERROR_MESSAGE 101 ); 102 } 103 } 104 105 /* Now execute the commands to add the dupicated contents of the paste buffer to the map */ 68 LatLonDialog dialog = new LatLonDialog(Main.parent); 69 dialog.setVisible(true); 70 if (dialog.isCanceled()) 71 return; 72 73 LatLon coordinates = dialog.getCoordinates(); 74 if (coordinates == null) 75 return; 76 Node nnew = new Node(coordinates); 77 78 // add the node 106 79 Main.main.undoRedo.add(new AddCommand(nnew)); 107 80 getCurrentDataSet().setSelected(nnew); … … 113 86 setEnabled(getEditLayer() != null); 114 87 } 88 89 static private class LatLonDialog extends JDialog { 90 private static final Color BG_COLOR_ERROR = new Color(255,224,224); 91 92 private JTextField tfLat; 93 private JTextField tfLon; 94 private boolean canceled = false; 95 private LatLon coordinates; 96 private OKAction actOK; 97 private CancelAction actCancel; 98 99 protected JPanel buildInputForm() { 100 JPanel pnl = new JPanel(new GridBagLayout()); 101 pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 102 pnl.add(new JLabel("<html>"+ 103 tr("Enter the coordinates for the new node.") + 104 "<br>" + tr("Use decimal degrees.") + 105 "<br>" + tr("Negative values denote Western/Southern hemisphere.")), 106 GBC.eol()); 107 108 pnl.add(new JLabel(tr("Latitude")), GBC.std().insets(0,10,5,0)); 109 tfLat = new JTextField(12); 110 pnl.add(tfLat, GBC.eol().insets(0,10,0,0)); 111 pnl.add(new JLabel(tr("Longitude")), GBC.std().insets(0,0,5,10)); 112 tfLon = new JTextField(12); 113 pnl.add(tfLon, GBC.eol().insets(0,0,0,10)); 114 115 // parse and verify input on the fly 116 // 117 LatLonInputVerifier inputVerifier = new LatLonInputVerifier(); 118 tfLat.getDocument().addDocumentListener(inputVerifier); 119 tfLon.getDocument().addDocumentListener(inputVerifier); 120 121 // select the text in the field on focus 122 // 123 TextFieldFocusHandler focusHandler = new TextFieldFocusHandler(); 124 tfLat.addFocusListener(focusHandler); 125 tfLon.addFocusListener(focusHandler); 126 return pnl; 127 } 128 129 protected JPanel buildButtonRow() { 130 JPanel pnl = new JPanel(new FlowLayout()); 131 132 SideButton btn; 133 pnl.add(btn = new SideButton(actOK = new OKAction())); 134 makeButtonRespondToEnter(btn); 135 pnl.add(btn = new SideButton(actCancel = new CancelAction())); 136 makeButtonRespondToEnter(btn); 137 pnl.add(new SideButton(new ContextSensitiveHelpAction(ht("/Actions/AddNode")))); 138 return pnl; 139 } 140 141 protected void makeButtonRespondToEnter(SideButton btn) { 142 btn.setFocusable(true); 143 btn.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter"); 144 btn.getActionMap().put("enter", btn.getAction()); 145 } 146 147 protected void build() { 148 getContentPane().setLayout(new BorderLayout()); 149 getContentPane().add(buildInputForm(), BorderLayout.CENTER); 150 getContentPane().add(buildButtonRow(), BorderLayout.SOUTH); 151 pack(); 152 153 // make dialog respond to ESCAPE 154 // 155 getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0), "escape"); 156 getRootPane().getActionMap().put("escape", actCancel); 157 158 // make dialog respond to F1 159 // 160 HelpUtil.setHelpContext(getRootPane(), ht("/Actions/AddNode")); 161 } 162 163 public LatLonDialog(Component parent) { 164 super(JOptionPane.getFrameForComponent(parent), true /* modal */); 165 setTitle(tr("Add Node...")); 166 build(); 167 addWindowListener(new WindowEventHandler()); 168 setCoordinates(null); 169 } 170 171 public void setCoordinates(LatLon coordinates) { 172 if (coordinates == null) { 173 coordinates = new LatLon(0,0); 174 } 175 this.coordinates = coordinates; 176 tfLat.setText(coordinates.latToString(CoordinateFormat.DECIMAL_DEGREES)); 177 tfLon.setText(coordinates.lonToString(CoordinateFormat.DECIMAL_DEGREES)); 178 actOK.setEnabled(true); 179 } 180 181 public LatLon getCoordinates() { 182 return coordinates; 183 } 184 185 protected void setErrorFeedback(JTextField tf, String message) { 186 tf.setBorder(BorderFactory.createLineBorder(Color.RED, 1)); 187 tf.setToolTipText(message); 188 tf.setBackground(BG_COLOR_ERROR); 189 } 190 191 protected void clearErrorFeedback(JTextField tf, String message) { 192 tf.setBorder(UIManager.getBorder("TextField.border")); 193 tf.setToolTipText(message); 194 tf.setBackground(UIManager.getColor("TextField.background")); 195 } 196 197 protected Double parseDoubleFromUserInput(String input) { 198 if (input == null) return null; 199 // remove white space and an optional degree symbol 200 // 201 input = input.trim(); 202 input = input.replaceAll("\u00B0", ""); // the degree symbol 203 204 // try to parse using the current locale 205 // 206 NumberFormat f = NumberFormat.getNumberInstance(); 207 Number n=null; 208 ParsePosition pp = new ParsePosition(0); 209 n = f.parse(input,pp); 210 if (pp.getErrorIndex() >= 0 || pp.getIndex()<input.length()) { 211 // fall back - try to parse with the english locale 212 // 213 pp = new ParsePosition(0); 214 f = NumberFormat.getNumberInstance(Locale.ENGLISH); 215 n = f.parse(input, pp); 216 if (pp.getErrorIndex() >= 0 || pp.getIndex()<input.length()) 217 return null; 218 } 219 return n== null ? null : n.doubleValue(); 220 } 221 222 protected Double parseLatFromUserInput() { 223 Double d = parseDoubleFromUserInput(tfLat.getText()); 224 if (d == null || ! LatLon.isValidLat(d)) { 225 setErrorFeedback(tfLat, tr("Please enter a valid latitude in the range -90..90")); 226 return null; 227 } else { 228 clearErrorFeedback(tfLat,tr("Please enter a latitude in the range -90..90")); 229 } 230 return d; 231 } 232 233 protected Double parseLonFromUserInput() { 234 Double d = parseDoubleFromUserInput(tfLon.getText()); 235 if (d == null || ! LatLon.isValidLat(d)) { 236 setErrorFeedback(tfLon, tr("Please enter a valid longitude in the range -180..180")); 237 return null; 238 } else { 239 clearErrorFeedback(tfLon,tr("Please enter a longitude in the range -180..178")); 240 } 241 return d; 242 } 243 244 protected void parseUserInput() { 245 Double lat = parseLatFromUserInput(); 246 Double lon = parseLonFromUserInput(); 247 if (lat == null || lon == null) { 248 coordinates = null; 249 actOK.setEnabled(false); 250 } else { 251 coordinates = new LatLon(lat,lon); 252 actOK.setEnabled(true); 253 } 254 } 255 256 public boolean isCanceled() { 257 return canceled; 258 } 259 260 protected void setCanceled(boolean canceled) { 261 this.canceled = canceled; 262 } 263 264 @Override 265 public void setVisible(boolean visible) { 266 if (visible) { 267 setCanceled(false); 268 WindowGeometry.centerInWindow(Main.parent, getSize()).apply(this); 269 } 270 super.setVisible(visible); 271 } 272 273 class OKAction extends AbstractAction { 274 public OKAction() { 275 putValue(NAME, tr("OK")); 276 putValue(SHORT_DESCRIPTION, tr("Close the dialog and create a new node")); 277 putValue(SMALL_ICON, ImageProvider.get("ok")); 278 } 279 280 public void actionPerformed(ActionEvent e) { 281 setCanceled(false); 282 setVisible(false); 283 } 284 } 285 286 class CancelAction extends AbstractAction { 287 public CancelAction() { 288 putValue(NAME, tr("Cancel")); 289 putValue(SHORT_DESCRIPTION, tr("Close the dialog, don't create a new node")); 290 putValue(SMALL_ICON, ImageProvider.get("cancel")); 291 } 292 293 public void actionPerformed(ActionEvent e) { 294 setCanceled(true); 295 setVisible(false); 296 } 297 } 298 299 class LatLonInputVerifier implements DocumentListener { 300 public void changedUpdate(DocumentEvent e) { 301 parseUserInput(); 302 } 303 304 public void insertUpdate(DocumentEvent e) { 305 parseUserInput(); 306 } 307 308 public void removeUpdate(DocumentEvent e) { 309 parseUserInput(); 310 } 311 } 312 313 class TextFieldFocusHandler implements FocusListener { 314 public void focusGained(FocusEvent e) { 315 Component c = e.getComponent(); 316 if (c instanceof JTextField) { 317 JTextField tf = (JTextField)c; 318 tf.selectAll(); 319 } 320 } 321 public void focusLost(FocusEvent e) {} 322 } 323 324 class WindowEventHandler extends WindowAdapter { 325 @Override 326 public void windowClosing(WindowEvent e) { 327 setCanceled(true); 328 setVisible(false); 329 } 330 331 @Override 332 public void windowOpened(WindowEvent e) { 333 tfLat.requestFocusInWindow(); 334 } 335 } 336 } 115 337 }
Note:
See TracChangeset
for help on using the changeset viewer.