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

Last change on this file since 655 was 655, checked in by ramack, 16 years ago

patch by bruce89, closes #812; thanks bruce

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