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

Revision 5060, 8.3 KB checked in by simon04, 2 months ago (diff)

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

  • Property svn:eol-style set to native
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.