source: josm/trunk/src/org/openstreetmap/josm/command/Command.java@ 5170

Last change on this file since 5170 was 5060, checked in by simon04, 12 years ago

fix #7470 - tune warning of nodes out of downloaded bbox

  • Property svn:eol-style set to native
File size: 8.3 KB
Line 
1//License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.command;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.GridBagLayout;
7import java.awt.geom.Area;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.HashMap;
11import java.util.LinkedHashMap;
12import java.util.Map;
13import java.util.Map.Entry;
14
15import javax.swing.JLabel;
16import javax.swing.JOptionPane;
17import javax.swing.JPanel;
18import javax.swing.tree.DefaultMutableTreeNode;
19import javax.swing.tree.MutableTreeNode;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
24import org.openstreetmap.josm.data.osm.PrimitiveData;
25import org.openstreetmap.josm.data.osm.Relation;
26import org.openstreetmap.josm.data.osm.Way;
27import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
28import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
29import org.openstreetmap.josm.gui.layer.Layer;
30import org.openstreetmap.josm.gui.layer.OsmDataLayer;
31import org.openstreetmap.josm.tools.CheckParameterUtil;
32
33/**
34 * Classes implementing Command modify a dataset in a specific way. A command is
35 * one atomic action on a specific dataset, such as move or delete.
36 *
37 * The command remembers the {@see OsmDataLayer} it is operating on.
38 *
39 * @author imi
40 */
41abstract public class Command extends PseudoCommand {
42
43 private static final class CloneVisitor extends AbstractVisitor {
44 public final Map<OsmPrimitive, PrimitiveData> orig = new LinkedHashMap<OsmPrimitive, PrimitiveData>();
45
46 public void visit(Node n) {
47 orig.put(n, n.save());
48 }
49 public void visit(Way w) {
50 orig.put(w, w.save());
51 }
52 public void visit(Relation e) {
53 orig.put(e, e.save());
54 }
55 }
56
57 /** the map of OsmPrimitives in the original state to OsmPrimitives in cloned state */
58 private Map<OsmPrimitive, PrimitiveData> cloneMap = new HashMap<OsmPrimitive, PrimitiveData>();
59
60 /** the layer which this command is applied to */
61 private OsmDataLayer layer;
62
63 public Command() {
64 this.layer = Main.map.mapView.getEditLayer();
65 }
66
67 /**
68 * Creates a new command in the context of a specific data layer
69 *
70 * @param layer the data layer. Must not be null.
71 * @throws IllegalArgumentException thrown if layer is null
72 */
73 public Command(OsmDataLayer layer) throws IllegalArgumentException {
74 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
75 this.layer = layer;
76 }
77
78 /**
79 * Executes the command on the dataset. This implementation will remember all
80 * primitives returned by fillModifiedData for restoring them on undo.
81 */
82 public boolean executeCommand() {
83 CloneVisitor visitor = new CloneVisitor();
84 Collection<OsmPrimitive> all = new ArrayList<OsmPrimitive>();
85 fillModifiedData(all, all, all);
86 for (OsmPrimitive osm : all) {
87 osm.visit(visitor);
88 }
89 cloneMap = visitor.orig;
90 return true;
91 }
92
93 /**
94 * Undoes the command.
95 * It can be assumed that all objects are in the same state they were before.
96 * It can also be assumed that executeCommand was called exactly once before.
97 *
98 * This implementation undoes all objects stored by a former call to executeCommand.
99 */
100 public void undoCommand() {
101 for (Entry<OsmPrimitive, PrimitiveData> e : cloneMap.entrySet()) {
102 OsmPrimitive primitive = e.getKey();
103 if (primitive.getDataSet() != null) {
104 e.getKey().load(e.getValue());
105 }
106 }
107 }
108
109 /**
110 * Called when a layer has been removed to have the command remove itself from
111 * any buffer if it is not longer applicable to the dataset (e.g. it was part of
112 * the removed layer)
113 *
114 * @param oldLayer the old layer
115 * @return true if this command
116 */
117 public boolean invalidBecauselayerRemoved(Layer oldLayer) {
118 if (!(oldLayer instanceof OsmDataLayer))
119 return false;
120 return layer == oldLayer;
121 }
122
123 /**
124 * Lets other commands access the original version
125 * of the object. Usually for undoing.
126 */
127 public PrimitiveData getOrig(OsmPrimitive osm) {
128 PrimitiveData o = cloneMap.get(osm);
129 if (o != null)
130 return o;
131 for (OsmPrimitive t : cloneMap.keySet()) {
132 PrimitiveData to = cloneMap.get(t);
133 }
134 return o;
135 }
136
137 /**
138 * Replies the layer this command is (or was) applied to.
139 *
140 */
141 protected OsmDataLayer getLayer() {
142 return layer;
143 }
144
145 /**
146 * Fill in the changed data this command operates on.
147 * Add to the lists, don't clear them.
148 *
149 * @param modified The modified primitives
150 * @param deleted The deleted primitives
151 * @param added The added primitives
152 */
153 abstract public void fillModifiedData(Collection<OsmPrimitive> modified,
154 Collection<OsmPrimitive> deleted,
155 Collection<OsmPrimitive> added);
156
157 /**
158 * Return the primitives that take part in this command.
159 */
160 @Override public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
161 return cloneMap.keySet();
162 }
163
164 /**
165 * Check whether user is about to operate on data outside of the download area.
166 * Request confirmation if he is.
167 *
168 * @param operation the operation name which is used for setting some preferences
169 * @param dialogTitle the title of the dialog being displayed
170 * @param outsideDialogMessage the message text to be displayed when data is outside of the download area
171 * @param incompleteDialogMessage the message text to be displayed when data is incomplete
172 * @param area the area used to determine whether data is outlying
173 * @param primitives the primitives to operate on
174 * @param ignore {@code null} or a primitive to be ignored
175 * @return true, if operating on outlying primitives is OK; false, otherwise
176 */
177 public static boolean checkAndConfirmOutlyingOperation(String operation,
178 String dialogTitle, String outsideDialogMessage, String incompleteDialogMessage,
179 Area area, Collection<? extends OsmPrimitive> primitives,
180 Collection<? extends OsmPrimitive> ignore) {
181 boolean outside = false;
182 boolean incomplete = false;
183 for (OsmPrimitive osm : primitives) {
184 if (osm.isIncomplete()) {
185 incomplete = true;
186 } else if (area != null && isOutlying(osm, area)
187 && (ignore == null || !ignore.contains(osm))) {
188 outside = true;
189 }
190 }
191 if (outside) {
192 JPanel msg = new JPanel(new GridBagLayout());
193 msg.add(new JLabel("<html>" + outsideDialogMessage + "</html>"));
194 boolean answer = ConditionalOptionPaneUtil.showConfirmationDialog(
195 operation + "_outside_nodes",
196 Main.parent,
197 msg,
198 dialogTitle,
199 JOptionPane.YES_NO_OPTION,
200 JOptionPane.QUESTION_MESSAGE,
201 JOptionPane.YES_OPTION);
202 if(!answer)
203 return false;
204 }
205 if (incomplete) {
206 JPanel msg = new JPanel(new GridBagLayout());
207 msg.add(new JLabel("<html>" + incompleteDialogMessage + "</html>"));
208 boolean answer = ConditionalOptionPaneUtil.showConfirmationDialog(
209 operation + "_incomplete",
210 Main.parent,
211 msg,
212 dialogTitle,
213 JOptionPane.YES_NO_OPTION,
214 JOptionPane.QUESTION_MESSAGE,
215 JOptionPane.YES_OPTION);
216 if(!answer)
217 return false;
218 }
219 return true;
220 }
221
222 private static boolean isOutlying(OsmPrimitive osm, Area area) {
223 if (osm instanceof Node) {
224 return !osm.isNewOrUndeleted() && !area.contains(((Node) osm).getCoor());
225 } else if (osm instanceof Way) {
226 for (Node n : ((Way) osm).getNodes()) {
227 if (isOutlying(n, area)) {
228 return true;
229 }
230 }
231 return false;
232 }
233 return false;
234 }
235}
Note: See TracBrowser for help on using the repository browser.