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

Last change on this file since 4806 was 4461, checked in by simon04, 13 years ago

fix #5407 - simplify way: dialog asking to delete nodes outside boundary is missing "never ask again" checkbox

  • Property svn:eol-style set to native
File size: 8.9 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 * Provide a description that can be presented in a list or tree view.
166 * This override will be removed when
167 * <code>description()</code> is removed.
168 */
169 @Override public Object getDescription() {
170 return ((DefaultMutableTreeNode) description()).getUserObject();
171 }
172
173 /**
174 * @deprecated use getDescription() and getChildren() instead
175 */
176 @Deprecated
177 public MutableTreeNode description() {
178 return null;
179 }
180
181 /**
182 * Check whether user is about to operate on data outside of the download area.
183 * Request confirmation if he is.
184 *
185 * @param operation the operation name which is used for setting some preferences
186 * @param dialogTitle the title of the dialog being displayed
187 * @param outsideDialogMessage the message text to be displayed when data is outside of the download area
188 * @param incompleteDialogMessage the message text to be displayed when data is incomplete
189 * @param area the area used to determine whether data is outlying
190 * @param primitives the primitives to operate on
191 * @param ignore {@code null} or a primitive to be ignored
192 * @return true, if operating on outlying primitives is OK; false, otherwise
193 */
194 public static boolean checkAndConfirmOutlyingOperation(String operation,
195 String dialogTitle, String outsideDialogMessage, String incompleteDialogMessage,
196 Area area, Collection<? extends OsmPrimitive> primitives, OsmPrimitive ignore) {
197 boolean outside = false;
198 boolean incomplete = false;
199 if (area != null) {
200 for (OsmPrimitive osm : primitives) {
201 if (osm.isIncomplete()) {
202 incomplete = true;
203 } else if (isOutlying(osm, area)
204 && (ignore == null || !ignore.equals(osm))) {
205 outside = true;
206 }
207 }
208 } else {
209 for (OsmPrimitive osm : primitives) {
210 if (osm.isIncomplete()) {
211 incomplete = true;
212 }
213 }
214 }
215 if (outside) {
216 JPanel msg = new JPanel(new GridBagLayout());
217 msg.add(new JLabel("<html>" + outsideDialogMessage + "</html>"));
218 boolean answer = ConditionalOptionPaneUtil.showConfirmationDialog(
219 operation + "_outside_nodes",
220 Main.parent,
221 msg,
222 dialogTitle,
223 JOptionPane.YES_NO_OPTION,
224 JOptionPane.QUESTION_MESSAGE,
225 JOptionPane.YES_OPTION);
226 if(!answer)
227 return false;
228 }
229 if (incomplete) {
230 JPanel msg = new JPanel(new GridBagLayout());
231 msg.add(new JLabel("<html>" + incompleteDialogMessage + "</html>"));
232 boolean answer = ConditionalOptionPaneUtil.showConfirmationDialog(
233 operation + "_incomplete",
234 Main.parent,
235 msg,
236 dialogTitle,
237 JOptionPane.YES_NO_OPTION,
238 JOptionPane.QUESTION_MESSAGE,
239 JOptionPane.YES_OPTION);
240 if(!answer)
241 return false;
242 }
243 return true;
244 }
245
246 private static boolean isOutlying(OsmPrimitive osm, Area area) {
247 if (osm instanceof Node) {
248 return !osm.isNewOrUndeleted() && !area.contains(((Node) osm).getCoor());
249 } else if (osm instanceof Way) {
250 for (Node n : ((Way) osm).getNodes()) {
251 if (isOutlying(n, area)) {
252 return true;
253 }
254 }
255 return false;
256 }
257 return false;
258 }
259}
Note: See TracBrowser for help on using the repository browser.