Changeset 2543 in josm


Ignore:
Timestamp:
2009-11-29T11:36:48+01:00 (15 years ago)
Author:
Gubaer
Message:

fixed #3494: shift+d form should be accept coma as well as dots

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/AddNodeAction.java

    r2531 r2543  
    55import static org.openstreetmap.josm.tools.I18n.tr;
    66
     7import java.awt.BorderLayout;
     8import java.awt.Color;
     9import java.awt.Component;
     10import java.awt.FlowLayout;
    711import java.awt.GridBagLayout;
    812import java.awt.event.ActionEvent;
     13import java.awt.event.FocusEvent;
     14import java.awt.event.FocusListener;
    915import java.awt.event.KeyEvent;
    1016import java.awt.event.WindowAdapter;
    1117import java.awt.event.WindowEvent;
    12 
     18import java.text.NumberFormat;
     19import java.text.ParsePosition;
     20import java.util.Locale;
     21import java.util.logging.Logger;
     22
     23import javax.swing.AbstractAction;
     24import javax.swing.BorderFactory;
     25import javax.swing.JComponent;
    1326import javax.swing.JDialog;
    1427import javax.swing.JLabel;
     
    1629import javax.swing.JPanel;
    1730import javax.swing.JTextField;
     31import javax.swing.KeyStroke;
     32import javax.swing.UIManager;
     33import javax.swing.event.DocumentEvent;
     34import javax.swing.event.DocumentListener;
    1835
    1936import org.openstreetmap.josm.Main;
    2037import org.openstreetmap.josm.command.AddCommand;
     38import org.openstreetmap.josm.data.coor.CoordinateFormat;
    2139import org.openstreetmap.josm.data.coor.LatLon;
    2240import org.openstreetmap.josm.data.osm.Node;
     41import org.openstreetmap.josm.gui.SideButton;
     42import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
     43import org.openstreetmap.josm.gui.help.HelpUtil;
    2344import org.openstreetmap.josm.tools.GBC;
     45import org.openstreetmap.josm.tools.ImageProvider;
    2446import org.openstreetmap.josm.tools.Shortcut;
     47import org.openstreetmap.josm.tools.WindowGeometry;
    2548
    2649/**
     
    2952 */
    3053public final class AddNodeAction extends JosmAction {
     54    static private final Logger logger = Logger.getLogger(AddNodeAction.class.getName());
    3155
    3256    public AddNodeAction() {
     
    3761    }
    3862
    39     private String normalizeUserInputForLatLonValue(String value) {
    40         if (value == null) return null;
    41         value = value.trim();
    42         return value.replaceAll("\u00B0", ""); // the degree symbol
    43     }
    4463
    4564    public void actionPerformed(ActionEvent e) {
    46         // we abort if we are not in the context of an OsmDataLayer
    47         //
    4865        if (!isEnabled())
    4966            return;
    5067
    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
    10679        Main.main.undoRedo.add(new AddCommand(nnew));
    10780        getCurrentDataSet().setSelected(nnew);
     
    11386        setEnabled(getEditLayer() != null);
    11487    }
     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    }
    115337}
Note: See TracChangeset for help on using the changeset viewer.