source: josm/src/org/openstreetmap/josm/actions/mapmode/CombineAction.java@ 23

Last change on this file since 23 was 23, checked in by imi, 19 years ago
  • added commands to support undo later
  • added Edit-Layer concept
  • painting of deleted objects
File size: 5.9 KB
Line 
1package org.openstreetmap.josm.actions.mapmode;
2
3import java.awt.Color;
4import java.awt.Graphics;
5import java.awt.Point;
6import java.awt.event.KeyEvent;
7import java.awt.event.MouseEvent;
8
9import javax.swing.JOptionPane;
10
11import org.openstreetmap.josm.Main;
12import org.openstreetmap.josm.command.CombineCommand;
13import org.openstreetmap.josm.data.osm.LineSegment;
14import org.openstreetmap.josm.data.osm.Node;
15import org.openstreetmap.josm.data.osm.OsmPrimitive;
16import org.openstreetmap.josm.data.osm.Track;
17import org.openstreetmap.josm.gui.MapFrame;
18
19/**
20 * A MapMode that allows the user to combine two objects to a new one.
21 *
22 * When entering CombineAction, all selection is cleared.
23 *
24 * The user can select objects by dragging them to another object, so the object
25 * he pressed and the one he released the button are combined. No selection
26 * rectangle is supported.
27 *
28 * Even if the user don't press Alt, tracks instead of line segments are selected.
29 * This means, it is impossible to select non-pending line segments.
30 *
31 * Pressing Ctrl or Shift has no effect too.
32 *
33 * No object can be combined with an object it is already part of. E.g. line
34 * segment cannot be combined with a track it is part of. In case of such a
35 * constillation, the user is informed.
36 *
37 * When combining, the object the user pressed on is called <i>source</i> and
38 * the object the button was released on is called <i>target</i>.
39 *
40 * The following objects can be combined:
41 *
42 * - A line segment and a track can be combined if one of them is a pending line
43 * segment. This get integrated into the track.
44 * - Two tracks can be combined. The latter track get removed and all its
45 * segments are moved to the first track. This is only possible, if both
46 * tracks have no different value in any key.
47 * - Two areas can be combined, if they share at least one node, in which case
48 * the combined area span both areas. If the areas share more than one node,
49 * all lines between the areas get removed. This is only possible if both areas
50 * have no different value in any key.
51 *
52 * All other object combinations cannot be combined.
53 *
54 * TODO: This and AddLineSegmentAction are similar. Refactor both.
55 *
56 * @author imi
57 */
58public class CombineAction extends MapMode {
59
60 /**
61 * The object that was first selected as combine source.
62 */
63 private OsmPrimitive first;
64 /**
65 * The object that was last selected as combine target.
66 */
67 private OsmPrimitive second;
68 /**
69 * Whether a hint is drawn on screen or not.
70 */
71 private boolean combineHintDrawn = false;
72
73 /**
74 * Constructs a CombineAction. Mnemonic is "c".
75 */
76 public CombineAction(MapFrame mapFrame) {
77 super("Combine", "combine", "Combine objects together.", KeyEvent.VK_C, mapFrame);
78 }
79
80 @Override
81 public void registerListener() {
82 super.registerListener();
83 mv.addMouseListener(this);
84 mv.addMouseMotionListener(this);
85 Main.main.ds.clearSelection();
86 }
87
88 @Override
89 public void unregisterListener() {
90 super.unregisterListener();
91 mv.removeMouseListener(this);
92 mv.removeMouseMotionListener(this);
93 drawCombineHint(false);
94 }
95
96 /**
97 * If nothing is selected, select the object nearest to the mouse. Else
98 * start the "display possible combining" phase and draw a hint what would
99 * be combined if user releases the button.
100 */
101 @Override
102 public void mousePressed(MouseEvent e) {
103 if (e.getButton() != MouseEvent.BUTTON1)
104 return;
105
106 OsmPrimitive clicked = mv.getNearest(e.getPoint(), true);
107 if (clicked == null || clicked instanceof Node)
108 return;
109
110 drawCombineHint(false);
111 first = second = clicked;
112 }
113
114 /**
115 * Updates the drawn combine hint if necessary.
116 */
117 @Override
118 public void mouseDragged(MouseEvent e) {
119 if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0)
120 return;
121
122 OsmPrimitive clicked = mv.getNearest(e.getPoint(), true);
123 if (clicked == null || clicked == second || clicked instanceof Node)
124 return;
125
126 drawCombineHint(false);
127 second = clicked;
128 drawCombineHint(true);
129 }
130
131 /**
132 * Start combining (if there is something to combine).
133 */
134 @Override
135 public void mouseReleased(MouseEvent e) {
136 if (e.getButton() != MouseEvent.BUTTON1)
137 return;
138
139 if (first == null || second == null || first == second) {
140 first = null;
141 second = null;
142 return;
143 }
144
145 drawCombineHint(false);
146
147 if (first instanceof LineSegment && second instanceof LineSegment)
148 JOptionPane.showMessageDialog(Main.main, "Cannot combine two line segments. To create tracks use 'Add Track'.");
149 else if (first instanceof Track && second instanceof Track && !first.keyPropertiesMergable(second))
150 JOptionPane.showMessageDialog(Main.main, "Cannot combine because of different properties.");
151 else
152 mv.editLayer().add(new CombineCommand(first, second));
153 mv.repaint();
154 }
155
156 /**
157 * Draws or removes the combine hint using the combineHint structure.
158 *
159 * @param draw <code>true</code> to draw the hint or
160 * <code>false</code> to remove it.
161 */
162 private void drawCombineHint(boolean draw) {
163 if (draw == combineHintDrawn)
164 return;
165 if (first == null || second == null)
166 return;
167 if (second == first)
168 return;
169
170 Graphics g = mv.getGraphics();
171 g.setColor(Color.BLACK);
172 g.setXORMode(Color.WHITE);
173 draw(g, first);
174 draw(g, second);
175 combineHintDrawn = !combineHintDrawn;
176 }
177
178 /**
179 * Draw a hint for the specified primitive
180 * @param g The graphic to draw into
181 * @param osm The primitive to draw a hint for.
182 */
183 private void draw(Graphics g, OsmPrimitive osm) {
184 if (osm instanceof LineSegment) {
185 LineSegment ls = (LineSegment)osm;
186 Point start = mv.getScreenPoint(ls.start.coor);
187 Point end = mv.getScreenPoint(ls.end.coor);
188 if (Main.main.ds.pendingLineSegments.contains(osm) && g.getColor() == Color.GRAY)
189 g.drawLine(start.x, start.y, end.x, end.y);
190 else
191 g.drawLine(start.x, start.y, end.x, end.y);
192 } else if (osm instanceof Track) {
193 for (LineSegment ls : ((Track)osm).segments)
194 draw(g, ls);
195 }
196 }
197}
Note: See TracBrowser for help on using the repository browser.