source: josm/trunk/src/org/openstreetmap/josm/actions/PurgeAction.java@ 10598

Last change on this file since 10598 was 10413, checked in by Don-vip, 8 years ago

fix #12983 - replace calls to Main.main.get[Active|Edit]Layer() by Main.getLayerManager().get[Active|Edit]Layer() - gsoc-core

  • Property svn:eol-style set to native
File size: 13.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.Dimension;
8import java.awt.GraphicsEnvironment;
9import java.awt.GridBagLayout;
10import java.awt.Insets;
11import java.awt.event.ActionEvent;
12import java.awt.event.KeyEvent;
13import java.util.ArrayList;
14import java.util.Collection;
15import java.util.Collections;
16import java.util.Comparator;
17import java.util.HashSet;
18import java.util.List;
19import java.util.Set;
20
21import javax.swing.AbstractAction;
22import javax.swing.BorderFactory;
23import javax.swing.Box;
24import javax.swing.JButton;
25import javax.swing.JCheckBox;
26import javax.swing.JLabel;
27import javax.swing.JList;
28import javax.swing.JOptionPane;
29import javax.swing.JPanel;
30import javax.swing.JScrollPane;
31import javax.swing.JSeparator;
32
33import org.openstreetmap.josm.Main;
34import org.openstreetmap.josm.command.PurgeCommand;
35import org.openstreetmap.josm.data.osm.DataSet;
36import org.openstreetmap.josm.data.osm.Node;
37import org.openstreetmap.josm.data.osm.OsmPrimitive;
38import org.openstreetmap.josm.data.osm.Relation;
39import org.openstreetmap.josm.data.osm.RelationMember;
40import org.openstreetmap.josm.data.osm.Way;
41import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
42import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
43import org.openstreetmap.josm.gui.help.HelpUtil;
44import org.openstreetmap.josm.gui.layer.OsmDataLayer;
45import org.openstreetmap.josm.tools.GBC;
46import org.openstreetmap.josm.tools.ImageProvider;
47import org.openstreetmap.josm.tools.Shortcut;
48
49/**
50 * The action to purge the selected primitives, i.e. remove them from the
51 * data layer, or remove their content and make them incomplete.
52 *
53 * This means, the deleted flag is not affected and JOSM simply forgets
54 * about these primitives.
55 *
56 * This action is undo-able. In order not to break previous commands in the
57 * undo buffer, we must re-add the identical object (and not semantically
58 * equal ones).
59 */
60public class PurgeAction extends JosmAction {
61
62 /**
63 * Constructs a new {@code PurgeAction}.
64 */
65 public PurgeAction() {
66 /* translator note: other expressions for "purge" might be "forget", "clean", "obliterate", "prune" */
67 super(tr("Purge..."), "purge", tr("Forget objects but do not delete them on server when uploading."),
68 Shortcut.registerShortcut("system:purge", tr("Edit: {0}", tr("Purge")),
69 KeyEvent.VK_P, Shortcut.CTRL_SHIFT),
70 true);
71 putValue("help", HelpUtil.ht("/Action/Purge"));
72 }
73
74 protected transient OsmDataLayer layer;
75 protected JCheckBox cbClearUndoRedo;
76
77 protected transient Set<OsmPrimitive> toPurge;
78 /**
79 * finally, contains all objects that are purged
80 */
81 protected transient Set<OsmPrimitive> toPurgeChecked;
82 /**
83 * Subset of toPurgeChecked. Marks primitives that remain in the
84 * dataset, but incomplete.
85 */
86 protected transient Set<OsmPrimitive> makeIncomplete;
87 /**
88 * Subset of toPurgeChecked. Those that have not been in the selection.
89 */
90 protected transient List<OsmPrimitive> toPurgeAdditionally;
91
92 @Override
93 public void actionPerformed(ActionEvent e) {
94 if (!isEnabled())
95 return;
96
97 Collection<OsmPrimitive> sel = getLayerManager().getEditDataSet().getAllSelected();
98 layer = Main.getLayerManager().getEditLayer();
99
100 toPurge = new HashSet<>(sel);
101 toPurgeAdditionally = new ArrayList<>();
102 toPurgeChecked = new HashSet<>();
103
104 // Add referrer, unless the object to purge is not new
105 // and the parent is a relation
106 Set<OsmPrimitive> toPurgeRecursive = new HashSet<>();
107 while (!toPurge.isEmpty()) {
108
109 for (OsmPrimitive osm: toPurge) {
110 for (OsmPrimitive parent: osm.getReferrers()) {
111 if (toPurge.contains(parent) || toPurgeChecked.contains(parent) || toPurgeRecursive.contains(parent)) {
112 continue;
113 }
114 if (parent instanceof Way || (parent instanceof Relation && osm.isNew())) {
115 toPurgeAdditionally.add(parent);
116 toPurgeRecursive.add(parent);
117 }
118 }
119 toPurgeChecked.add(osm);
120 }
121 toPurge = toPurgeRecursive;
122 toPurgeRecursive = new HashSet<>();
123 }
124
125 makeIncomplete = new HashSet<>();
126
127 // Find the objects that will be incomplete after purging.
128 // At this point, all parents of new to-be-purged primitives are
129 // also to-be-purged and
130 // all parents of not-new to-be-purged primitives are either
131 // to-be-purged or of type relation.
132 TOP:
133 for (OsmPrimitive child : toPurgeChecked) {
134 if (child.isNew()) {
135 continue;
136 }
137 for (OsmPrimitive parent : child.getReferrers()) {
138 if (parent instanceof Relation && !toPurgeChecked.contains(parent)) {
139 makeIncomplete.add(child);
140 continue TOP;
141 }
142 }
143 }
144
145 // Add untagged way nodes. Do not add nodes that have other
146 // referrers not yet to-be-purged.
147 if (Main.pref.getBoolean("purge.add_untagged_waynodes", true)) {
148 Set<OsmPrimitive> wayNodes = new HashSet<>();
149 for (OsmPrimitive osm : toPurgeChecked) {
150 if (osm instanceof Way) {
151 Way w = (Way) osm;
152 NODE:
153 for (Node n : w.getNodes()) {
154 if (n.isTagged() || toPurgeChecked.contains(n)) {
155 continue;
156 }
157 for (OsmPrimitive ref : n.getReferrers()) {
158 if (ref != w && !toPurgeChecked.contains(ref)) {
159 continue NODE;
160 }
161 }
162 wayNodes.add(n);
163 }
164 }
165 }
166 toPurgeChecked.addAll(wayNodes);
167 toPurgeAdditionally.addAll(wayNodes);
168 }
169
170 if (Main.pref.getBoolean("purge.add_relations_with_only_incomplete_members", true)) {
171 Set<Relation> relSet = new HashSet<>();
172 for (OsmPrimitive osm : toPurgeChecked) {
173 for (OsmPrimitive parent : osm.getReferrers()) {
174 if (parent instanceof Relation
175 && !(toPurgeChecked.contains(parent))
176 && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relSet)) {
177 relSet.add((Relation) parent);
178 }
179 }
180 }
181
182 /**
183 * Add higher level relations (list gets extended while looping over it)
184 */
185 List<Relation> relLst = new ArrayList<>(relSet);
186 for (int i = 0; i < relLst.size(); ++i) { // foreach loop not applicable since list gets extended while looping over it
187 for (OsmPrimitive parent : relLst.get(i).getReferrers()) {
188 if (!(toPurgeChecked.contains(parent))
189 && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relLst)) {
190 relLst.add((Relation) parent);
191 }
192 }
193 }
194 relSet = new HashSet<>(relLst);
195 toPurgeChecked.addAll(relSet);
196 toPurgeAdditionally.addAll(relSet);
197 }
198
199 boolean modified = false;
200 for (OsmPrimitive osm : toPurgeChecked) {
201 if (osm.isModified()) {
202 modified = true;
203 break;
204 }
205 }
206
207 boolean clearUndoRedo = false;
208
209 if (!GraphicsEnvironment.isHeadless()) {
210 final boolean answer = ConditionalOptionPaneUtil.showConfirmationDialog(
211 "purge", Main.parent, buildPanel(modified), tr("Confirm Purging"),
212 JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_OPTION);
213 if (!answer)
214 return;
215
216 clearUndoRedo = cbClearUndoRedo.isSelected();
217 Main.pref.put("purge.clear_undo_redo", clearUndoRedo);
218 }
219
220 Main.main.undoRedo.add(new PurgeCommand(Main.getLayerManager().getEditLayer(), toPurgeChecked, makeIncomplete));
221
222 if (clearUndoRedo) {
223 Main.main.undoRedo.clean();
224 getLayerManager().getEditDataSet().clearSelectionHistory();
225 }
226 }
227
228 private JPanel buildPanel(boolean modified) {
229 JPanel pnl = new JPanel(new GridBagLayout());
230
231 pnl.add(Box.createRigidArea(new Dimension(400, 0)), GBC.eol().fill(GBC.HORIZONTAL));
232
233 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
234 pnl.add(new JLabel("<html>"+
235 tr("This operation makes JOSM forget the selected objects.<br> " +
236 "They will be removed from the layer, but <i>not</i> deleted<br> " +
237 "on the server when uploading.")+"</html>",
238 ImageProvider.get("purge"), JLabel.LEFT), GBC.eol().fill(GBC.HORIZONTAL));
239
240 if (!toPurgeAdditionally.isEmpty()) {
241 pnl.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 5));
242 pnl.add(new JLabel("<html>"+
243 tr("The following dependent objects will be purged<br> " +
244 "in addition to the selected objects:")+"</html>",
245 ImageProvider.get("warning-small"), JLabel.LEFT), GBC.eol().fill(GBC.HORIZONTAL));
246
247 Collections.sort(toPurgeAdditionally, new Comparator<OsmPrimitive>() {
248 @Override
249 public int compare(OsmPrimitive o1, OsmPrimitive o2) {
250 int type = o2.getType().compareTo(o1.getType());
251 if (type != 0)
252 return type;
253 return Long.compare(o1.getUniqueId(), o2.getUniqueId());
254 }
255 });
256 JList<OsmPrimitive> list = new JList<>(toPurgeAdditionally.toArray(new OsmPrimitive[toPurgeAdditionally.size()]));
257 /* force selection to be active for all entries */
258 list.setCellRenderer(new OsmPrimitivRenderer() {
259 @Override
260 public Component getListCellRendererComponent(JList<? extends OsmPrimitive> list,
261 OsmPrimitive value,
262 int index,
263 boolean isSelected,
264 boolean cellHasFocus) {
265 return super.getListCellRendererComponent(list, value, index, true, false);
266 }
267 });
268 JScrollPane scroll = new JScrollPane(list);
269 scroll.setPreferredSize(new Dimension(250, 300));
270 scroll.setMinimumSize(new Dimension(250, 300));
271 pnl.add(scroll, GBC.std().fill(GBC.BOTH).weight(1.0, 1.0));
272
273 JButton addToSelection = new JButton(new AbstractAction() {
274 {
275 putValue(SHORT_DESCRIPTION, tr("Add to selection"));
276 putValue(SMALL_ICON, ImageProvider.get("dialogs", "select"));
277 }
278
279 @Override
280 public void actionPerformed(ActionEvent e) {
281 layer.data.addSelected(toPurgeAdditionally);
282 }
283 });
284 addToSelection.setMargin(new Insets(0, 0, 0, 0));
285 pnl.add(addToSelection, GBC.eol().anchor(GBC.SOUTHWEST).weight(0.0, 1.0).insets(2, 0, 0, 3));
286 }
287
288 if (modified) {
289 pnl.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 5));
290 pnl.add(new JLabel("<html>"+tr("Some of the objects are modified.<br> " +
291 "Proceed, if these changes should be discarded."+"</html>"),
292 ImageProvider.get("warning-small"), JLabel.LEFT),
293 GBC.eol().fill(GBC.HORIZONTAL));
294 }
295
296 cbClearUndoRedo = new JCheckBox(tr("Clear Undo/Redo buffer"));
297 cbClearUndoRedo.setSelected(Main.pref.getBoolean("purge.clear_undo_redo", false));
298
299 pnl.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 5));
300 pnl.add(cbClearUndoRedo, GBC.eol());
301 return pnl;
302 }
303
304 @Override
305 protected void updateEnabledState() {
306 DataSet ds = getLayerManager().getEditDataSet();
307 if (ds == null) {
308 setEnabled(false);
309 } else {
310 setEnabled(!ds.selectionEmpty());
311 }
312 }
313
314 @Override
315 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
316 setEnabled(selection != null && !selection.isEmpty());
317 }
318
319 private static boolean hasOnlyIncompleteMembers(
320 Relation r, Collection<OsmPrimitive> toPurge, Collection<? extends OsmPrimitive> moreToPurge) {
321 for (RelationMember m : r.getMembers()) {
322 if (!m.getMember().isIncomplete() && !toPurge.contains(m.getMember()) && !moreToPurge.contains(m.getMember()))
323 return false;
324 }
325 return true;
326 }
327}
Note: See TracBrowser for help on using the repository browser.