source: josm/src/org/openstreetmap/josm/actions/mapmode/AddNodeAction.java@ 314

Last change on this file since 314 was 314, checked in by framm, 17 years ago
  • fixed action description for "add node and connect"
File size: 7.8 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions.mapmode;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Cursor;
7import java.awt.event.KeyEvent;
8import java.awt.event.MouseEvent;
9import java.util.ArrayList;
10import java.util.Collection;
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.LinkedList;
14
15import javax.swing.JOptionPane;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.actions.GroupAction;
19import org.openstreetmap.josm.command.AddCommand;
20import org.openstreetmap.josm.command.ChangeCommand;
21import org.openstreetmap.josm.command.Command;
22import org.openstreetmap.josm.command.SequenceCommand;
23import org.openstreetmap.josm.data.coor.EastNorth;
24import org.openstreetmap.josm.data.osm.Node;
25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.data.osm.Segment;
27import org.openstreetmap.josm.data.osm.Way;
28import org.openstreetmap.josm.gui.MapFrame;
29import org.openstreetmap.josm.tools.ImageProvider;
30
31/**
32 * This mode adds a new node to the dataset. The user clicks on a place to add
33 * and there is it. Nothing more, nothing less.
34 *
35 * Newly created nodes are selected. Shift modifier does not cancel the old
36 * selection as usual.
37 *
38 * @author imi
39 *
40 */
41public class AddNodeAction extends MapMode {
42
43 enum Mode {node, nodesegment, autonode}
44 private final Mode mode;
45
46 public static class AddNodeGroup extends GroupAction {
47 public AddNodeGroup(MapFrame mf) {
48 super(KeyEvent.VK_N,0);
49 putValue("help", "Action/AddNode");
50 actions.add(new AddNodeAction(mf,tr("Add node"), Mode.node, tr("Add a new node to the map")));
51 actions.add(new AddNodeAction(mf, tr("Add node into segment"), Mode.nodesegment,tr( "Add a node into an existing segment")));
52 actions.add(new AddNodeAction(mf, tr("Add node and connect"), Mode.autonode,tr( "Add a node and connect it to the selected node (with CTRL: add node into segment; with SHIFT: re-use existing node)")));
53 setCurrent(0);
54 }
55 }
56
57 public AddNodeAction(MapFrame mapFrame, String name, Mode mode, String desc) {
58 super(name, "node/"+mode, desc, mapFrame, getCursor());
59 this.mode = mode;
60 putValue("help", "Action/AddNode/"+Character.toUpperCase(mode.toString().charAt(0))+mode.toString().substring(1));
61 }
62
63 private static Cursor getCursor() {
64 try {
65 return ImageProvider.getCursor("crosshair", null);
66 } catch (Exception e) {
67 }
68 return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
69 }
70
71 @Override public void enterMode() {
72 super.enterMode();
73 Main.map.mapView.addMouseListener(this);
74 }
75
76 @Override public void exitMode() {
77 super.exitMode();
78 Main.map.mapView.removeMouseListener(this);
79 }
80
81 /**
82 * If user clicked with the left button, add a node at the current mouse
83 * position.
84 *
85 * If in nodesegment mode, add the node to the line segment by splitting the
86 * segment. The new created segment will be inserted in every way the segment
87 * was part of.
88 */
89 @Override public void mouseClicked(MouseEvent e) {
90 if (e.getButton() != MouseEvent.BUTTON1)
91 return;
92
93 Node n = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY()));
94 if (n.coor.isOutSideWorld()) {
95 JOptionPane.showMessageDialog(Main.parent,tr("Cannot add a node outside of the world."));
96 return;
97 }
98
99 Command c = new AddCommand(n);
100 if (mode == Mode.nodesegment) {
101 Segment s = Main.map.mapView.getNearestSegment(e.getPoint());
102 if (s == null)
103 return;
104
105 // see if another segment is also near
106 Segment other = Main.map.mapView.getNearestSegment(e.getPoint(), Collections.singleton(s));
107
108 if (other == null && (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) == 0) {
109 // moving the new point to the perpendicular point
110 // FIXME: when two segments are split, should move the new point to the
111 // intersection point!
112 EastNorth A = s.from.eastNorth;
113 EastNorth B = s.to.eastNorth;
114 double ab = A.distance(B);
115 double nb = n.eastNorth.distance(B);
116 double na = n.eastNorth.distance(A);
117 double q = (nb-na+ab)/ab/2;
118 n.eastNorth = new EastNorth(B.east() + q*(A.east()-B.east()), B.north() + q*(A.north()-B.north()));
119 n.coor = Main.proj.eastNorth2latlon(n.eastNorth);
120 }
121
122 Collection<Command> cmds = new LinkedList<Command>();
123 cmds.add(c);
124
125 // split the first segment
126 splitSegmentAtNode(s, n, cmds);
127
128 // if a second segment was found, split that as well
129 if (other != null) splitSegmentAtNode(other, n, cmds);
130
131 c = new SequenceCommand(tr((other == null) ?
132 "Add node into segment" : "Add common node into two segments"), cmds);
133 }
134
135 // Add a node and connecting segment.
136 if (mode == Mode.autonode) {
137
138 Segment insertInto = null;
139 Node reuseNode = null;
140
141 // If CTRL is held, insert the node into a potentially existing segment
142 if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) {
143 insertInto = Main.map.mapView.getNearestSegment(e.getPoint());
144 if (insertInto == null)
145 return;
146 }
147 // If ALT is held, instead of creating a new node, re-use an existing
148 // node (making this action identical to AddSegmentAction with the
149 // small difference that the node used will then be selected to allow
150 // continuation of the "add node and connect" stuff)
151 else if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
152 OsmPrimitive clicked = Main.map.mapView.getNearest(e.getPoint(), false);
153 if (clicked == null || !(clicked instanceof Node))
154 return;
155 reuseNode = (Node) clicked;
156 }
157
158 Collection<OsmPrimitive> selection = Main.ds.getSelected();
159 if (selection.size() == 1 && selection.iterator().next() instanceof Node) {
160 Node n1 = (Node)selection.iterator().next();
161 Collection<Command> cmds = new LinkedList<Command>();
162
163 if (reuseNode != null) {
164 // in re-use node mode, n1 must not be identical to clicked node
165 if (n1 == reuseNode) return;
166 // replace newly created node with existing node
167 n = reuseNode;
168 } else {
169 // only add the node creation command if we're not re-using
170 cmds.add(c);
171 }
172
173 Segment s = new Segment(n1, n);
174
175 if (insertInto != null)
176 splitSegmentAtNode(insertInto, n, cmds);
177
178 cmds.add(new AddCommand(s));
179
180 Way way = getWayForNode(n1);
181 if (way != null) {
182 Way newWay = new Way(way);
183 if (way.segments.get(0).from == n1) {
184 Node tmp = s.from;
185 s.from = s.to;
186 s.to = tmp;
187 newWay.segments.add(0, s);
188 } else
189 newWay.segments.add(s);
190 cmds.add(new ChangeCommand(way, newWay));
191 }
192
193 c = new SequenceCommand(tr((insertInto == null) ? "Add node and connect" : "Add node into segment and connect"), cmds);
194 }
195 }
196
197 Main.main.undoRedo.add(c);
198 Main.ds.setSelected(n);
199 Main.map.mapView.repaint();
200 }
201
202 /**
203 * @return If the node is part of exactly one way, return this.
204 * <code>null</code> otherwise.
205 */
206 private Way getWayForNode(Node n) {
207 Way way = null;
208 for (Way w : Main.ds.ways) {
209 for (Segment s : w.segments) {
210 if (s.from == n || s.to == n) {
211 if (way != null)
212 return null;
213 if (s.from == s.to)
214 return null;
215 way = w;
216 }
217 }
218 }
219 return way;
220 }
221
222 private void splitSegmentAtNode(Segment s, Node n, Collection<Command> cmds) {
223 Segment s1 = new Segment(s);
224 s1.to = n;
225 Segment s2 = new Segment(s.from, s.to);
226 s2.from = n;
227 if (s.keys != null)
228 s2.keys = new HashMap<String, String>(s.keys);
229
230 cmds.add(new ChangeCommand(s, s1));
231 cmds.add(new AddCommand(s2));
232
233 // Add the segment to every way
234 for (Way wold : Main.ds.ways) {
235 if (wold.segments.contains(s)) {
236 Way wnew = new Way(wold);
237 Collection<Segment> segs = new ArrayList<Segment>(wnew.segments);
238 wnew.segments.clear();
239 for (Segment waySeg : segs) {
240 wnew.segments.add(waySeg);
241 if (waySeg == s)
242 wnew.segments.add(s2);
243 }
244 cmds.add(new ChangeCommand(wold, wnew));
245 }
246 }
247 }
248}
Note: See TracBrowser for help on using the repository browser.