source: josm/trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java@ 1186

Last change on this file since 1186 was 1186, checked in by stoecker, 15 years ago

fixed bug #1442. Patch by Matthew W. S. Bell

  • Property svn:eol-style set to native
File size: 9.4 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.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.BasicStroke;
8import java.awt.Color;
9import java.awt.Cursor;
10import java.awt.Graphics;
11import java.awt.Graphics2D;
12import java.awt.Point;
13import java.awt.event.KeyEvent;
14import java.awt.event.MouseEvent;
15import java.awt.geom.GeneralPath;
16import java.util.Collection;
17import java.util.LinkedList;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.command.AddCommand;
21import org.openstreetmap.josm.command.ChangeCommand;
22import org.openstreetmap.josm.command.Command;
23import org.openstreetmap.josm.command.SequenceCommand;
24import org.openstreetmap.josm.data.coor.EastNorth;
25import org.openstreetmap.josm.data.osm.Node;
26import org.openstreetmap.josm.data.osm.Way;
27import org.openstreetmap.josm.data.osm.WaySegment;
28import org.openstreetmap.josm.gui.MapFrame;
29import org.openstreetmap.josm.gui.MapView;
30import org.openstreetmap.josm.gui.layer.MapViewPaintable;
31import org.openstreetmap.josm.tools.ImageProvider;
32import org.openstreetmap.josm.tools.Shortcut;
33
34/**
35 * Makes a rectangle from a line, or modifies a rectangle.
36 *
37 * This class currently contains some "sleeping" code copied from DrawAction (move and rotate)
38 * which can eventually be removed, but it may also get activated here and removed in DrawAction.
39 */
40public class ExtrudeAction extends MapMode implements MapViewPaintable {
41
42 enum Mode { EXTRUDE, rotate, select }
43 private Mode mode = null;
44 private long mouseDownTime = 0;
45 private WaySegment selectedSegment = null;
46 private Color selectedColor;
47
48 double xoff;
49 double yoff;
50 double distance;
51
52 /**
53 * The old cursor before the user pressed the mouse button.
54 */
55 private Cursor oldCursor;
56 /**
57 * The current position of the mouse
58 */
59 private Point mousePos;
60 /**
61 * The position of the mouse cursor when the drag action was initiated.
62 */
63 private Point initialMousePos;
64 /**
65 * The time which needs to pass between click and release before something
66 * counts as a move, in milliseconds
67 */
68 private int initialMoveDelay = 200;
69
70 /**
71 * Create a new SelectAction
72 * @param mapFrame The MapFrame this action belongs to.
73 */
74 public ExtrudeAction(MapFrame mapFrame) {
75 super(tr("Extrude"), "extrude/extrude", tr("Create areas"),
76 Shortcut.registerShortcut("mapmode:extrude", tr("Mode: {0}", tr("Extrude")), KeyEvent.VK_X, Shortcut.GROUP_EDIT),
77 mapFrame,
78 getCursor("normal", "rectangle", Cursor.DEFAULT_CURSOR));
79 putValue("help", "Action/Extrude/Extrude");
80 initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200);
81 selectedColor = Main.pref.getColor(marktr("selected"), Color.YELLOW);
82 }
83
84 private static Cursor getCursor(String name, String mod, int def) {
85 try {
86 return ImageProvider.getCursor(name, mod);
87 } catch (Exception e) {
88 }
89 return Cursor.getPredefinedCursor(def);
90 }
91
92 private void setCursor(Cursor c) {
93 if (oldCursor == null) {
94 oldCursor = Main.map.mapView.getCursor();
95 Main.map.mapView.setCursor(c);
96 }
97 }
98
99 private void restoreCursor() {
100 if (oldCursor != null) {
101 Main.map.mapView.setCursor(oldCursor);
102 oldCursor = null;
103 }
104 }
105
106 @Override public void enterMode() {
107 super.enterMode();
108 Main.map.mapView.addMouseListener(this);
109 Main.map.mapView.addMouseMotionListener(this);
110 }
111
112 @Override public void exitMode() {
113 super.exitMode();
114 Main.map.mapView.removeMouseListener(this);
115 Main.map.mapView.removeMouseMotionListener(this);
116 Main.map.mapView.removeTemporaryLayer(this);
117
118 }
119
120 /**
121 * If the left mouse button is pressed, move all currently selected
122 * objects (if one of them is under the mouse) or the current one under the
123 * mouse (which will become selected).
124 */
125 @Override public void mouseDragged(MouseEvent e) {
126 if (mode == Mode.select) return;
127
128 // do not count anything as a move if it lasts less than 100 milliseconds.
129 if ((mode == Mode.EXTRUDE) && (System.currentTimeMillis() - mouseDownTime < initialMoveDelay)) return;
130
131 if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0)
132 return;
133
134 if (mode == Mode.EXTRUDE) {
135 setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
136 }
137
138 if (mousePos == null) {
139 mousePos = e.getPoint();
140 return;
141 }
142
143 Main.map.mapView.repaint();
144 mousePos = e.getPoint();
145
146 }
147
148 public void paint(Graphics g, MapView mv) {
149 if (selectedSegment != null) {
150 Node n1 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex);
151 Node n2 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex + 1);
152
153 EastNorth en1 = n1.eastNorth;
154 EastNorth en2 = n2.eastNorth;
155 EastNorth en3 = mv.getEastNorth(mousePos.x, mousePos.y);
156
157 double u = ((en3.east() - en1.east()) * (en2.east() - en1.east()) +
158 (en3.north() - en1.north()) * (en2.north() - en1.north())) /
159 en2.distanceSq(en1);
160 // the point on the segment from which the distance to mouse pos is shortest
161 EastNorth base = new EastNorth(en1.east() + u * (en2.east() - en1.east()),
162 en1.north() + u * (en2.north() - en1.north()));
163
164 // find out the distance, in metres, between the base point and the mouse cursor
165 distance = Main.proj.eastNorth2latlon(base).greatCircleDistance(Main.proj.eastNorth2latlon(en3));
166 Main.map.statusLine.setDist(distance);
167 updateStatusLine();
168
169 // compute vertical and horizontal components.
170 xoff = en3.east() - base.east();
171 yoff = en3.north() - base.north();
172
173 Graphics2D g2 = (Graphics2D)g;
174 g2.setColor(selectedColor);
175 g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
176 GeneralPath b = new GeneralPath();
177 Point p1 = mv.getPoint(en1);
178 Point p2 = mv.getPoint(en2);
179 Point p3 = mv.getPoint(en1.add(xoff, yoff));
180 Point p4 = mv.getPoint(en2.add(xoff, yoff));
181
182 b.moveTo(p1.x, p1.y); b.lineTo(p3.x, p3.y);
183 b.lineTo(p4.x, p4.y); b.lineTo(p2.x, p2.y);
184 b.lineTo(p1.x, p1.y);
185 g2.draw(b);
186 g2.setStroke(new BasicStroke(1));
187 }
188 }
189
190 /**
191 */
192 @Override public void mousePressed(MouseEvent e) {
193 if (!(Boolean)this.getValue("active")) return;
194 if (e.getButton() != MouseEvent.BUTTON1)
195 return;
196 // boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
197 // boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
198 // boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
199
200 mouseDownTime = System.currentTimeMillis();
201
202 selectedSegment =
203 Main.map.mapView.getNearestWaySegment(e.getPoint());
204
205 mode = (selectedSegment == null) ? Mode.select : Mode.EXTRUDE;
206 oldCursor = Main.map.mapView.getCursor();
207
208 updateStatusLine();
209 Main.map.mapView.addTemporaryLayer(this);
210 Main.map.mapView.repaint();
211
212 mousePos = e.getPoint();
213 initialMousePos = e.getPoint();
214 }
215
216 /**
217 * Restore the old mouse cursor.
218 */
219 @Override public void mouseReleased(MouseEvent e) {
220 restoreCursor();
221 if (selectedSegment == null) return;
222 if (mousePos.distance(initialMousePos) > 10) {
223 Node n1 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex);
224 Node n2 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex+1);
225 EastNorth en3 = n2.eastNorth.add(xoff, yoff);
226 Node n3 = new Node(Main.proj.eastNorth2latlon(en3));
227 EastNorth en4 = n1.eastNorth.add(xoff, yoff);
228 Node n4 = new Node(Main.proj.eastNorth2latlon(en4));
229 Way wnew = new Way(selectedSegment.way);
230 wnew.nodes.add(selectedSegment.lowerIndex+1, n3);
231 wnew.nodes.add(selectedSegment.lowerIndex+1, n4);
232 if (wnew.nodes.size() == 4) wnew.nodes.add(n1);
233 Collection<Command> cmds = new LinkedList<Command>();
234 cmds.add(new AddCommand(n4));
235 cmds.add(new AddCommand(n3));
236 cmds.add(new ChangeCommand(selectedSegment.way, wnew));
237 Command c = new SequenceCommand(tr("Extrude Way"), cmds);
238 Main.main.undoRedo.add(c);
239 }
240
241 Main.map.mapView.removeTemporaryLayer(this);
242 selectedSegment = null;
243 mode = null;
244 updateStatusLine();
245 Main.map.mapView.repaint();
246 }
247
248 @Override public String getModeHelpText() {
249 if (mode == Mode.select) {
250 return tr("Release the mouse button to select the objects in the rectangle.");
251 } else if (mode == Mode.EXTRUDE) {
252 return tr("Draw a rectangle of the desired size, then release the mouse button.");
253 } else if (mode == Mode.rotate) {
254 return tr("Release the mouse button to stop rotating.");
255 } else {
256 return tr("Drag a way segment to make a rectangle.");
257 }
258 }
259}
Note: See TracBrowser for help on using the repository browser.