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

Last change on this file since 9997 was 9233, checked in by simon04, 8 years ago

fix #4602 - Add "Do not show again" to purge action

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