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

Last change on this file since 4077 was 4064, checked in by bastiK, 13 years ago

applied #6250 (patch by akks) - move selection history to dataset

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