Changeset 3266 in josm for trunk/src


Ignore:
Timestamp:
2010-05-22T00:58:27+02:00 (9 years ago)
Author:
mjulius
Message:

New action: MoveNodeAction
allows to move a node by entering lat and lon

Location:
trunk/src/org/openstreetmap/josm
Files:
2 added
3 edited

Legend:

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

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

    r3262 r3266  
    1212import javax.swing.JLabel;
    1313
     14import org.openstreetmap.josm.data.coor.CachedLatLon;
     15import org.openstreetmap.josm.data.coor.EastNorth;
    1416import org.openstreetmap.josm.data.coor.LatLon;
    1517import org.openstreetmap.josm.data.osm.Node;
     
    5557        this(Collections.singleton(osm), x, y);
    5658    }
     59
     60    public MoveCommand(Node node, LatLon position) {
     61        this(Collections.singleton((OsmPrimitive) node), node.getEastNorth().sub(new CachedLatLon(position).getEastNorth()));
     62    }
     63
     64    public MoveCommand(Collection<OsmPrimitive> objects, EastNorth offset) {
     65        this(objects, offset.getX(), offset.getY());
     66    }
     67
    5768    /**
    5869     * Create a MoveCommand and assign the initial object set and movement vector.
  • trunk/src/org/openstreetmap/josm/gui/MainMenu.java

    r2923 r3266  
    4545import org.openstreetmap.josm.actions.MergeSelectionAction;
    4646import org.openstreetmap.josm.actions.MirrorAction;
     47import org.openstreetmap.josm.actions.MoveNodeAction;
    4748import org.openstreetmap.josm.actions.NewAction;
    4849import org.openstreetmap.josm.actions.OpenFileAction;
     
    141142    public final JosmAction mirror = new MirrorAction();
    142143    public final AddNodeAction addnode = new AddNodeAction();
     144    public final MoveNodeAction movenode = new MoveNodeAction();
    143145    public final JosmAction createCircle = new CreateCircleAction();
    144146    public final JosmAction mergeNodes = new MergeNodesAction();
     
    291293        toolsMenu.addSeparator();
    292294        add(toolsMenu, addnode);
     295        add(toolsMenu, movenode);
    293296        add(toolsMenu, createCircle);
    294297        toolsMenu.addSeparator();
Note: See TracChangeset for help on using the changeset viewer.