[3669] | 1 | // License: GPL. See LICENSE file for details.
|
---|
| 2 | package org.openstreetmap.josm.gui.dialogs;
|
---|
| 3 |
|
---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 5 |
|
---|
| 6 | import java.awt.event.ActionEvent;
|
---|
| 7 | import java.awt.event.ActionListener;
|
---|
| 8 | import java.awt.event.KeyEvent;
|
---|
| 9 | import java.awt.event.MouseAdapter;
|
---|
| 10 | import java.awt.event.MouseEvent;
|
---|
| 11 | import java.io.IOException;
|
---|
| 12 | import java.lang.reflect.InvocationTargetException;
|
---|
| 13 | import java.util.ArrayList;
|
---|
| 14 | import java.util.Collection;
|
---|
| 15 | import java.util.Enumeration;
|
---|
| 16 | import java.util.HashSet;
|
---|
| 17 | import java.util.LinkedList;
|
---|
| 18 | import java.util.List;
|
---|
| 19 | import java.util.Set;
|
---|
| 20 |
|
---|
[5028] | 21 | import javax.swing.AbstractAction;
|
---|
[5200] | 22 | import javax.swing.JComponent;
|
---|
[3669] | 23 | import javax.swing.JMenuItem;
|
---|
| 24 | import javax.swing.JOptionPane;
|
---|
| 25 | import javax.swing.JPopupMenu;
|
---|
| 26 | import javax.swing.SwingUtilities;
|
---|
| 27 | import javax.swing.event.TreeSelectionEvent;
|
---|
| 28 | import javax.swing.event.TreeSelectionListener;
|
---|
| 29 | import javax.swing.tree.DefaultMutableTreeNode;
|
---|
| 30 | import javax.swing.tree.TreePath;
|
---|
| 31 |
|
---|
| 32 | import org.openstreetmap.josm.Main;
|
---|
| 33 | import org.openstreetmap.josm.actions.AutoScaleAction;
|
---|
| 34 | import org.openstreetmap.josm.command.Command;
|
---|
| 35 | import org.openstreetmap.josm.data.SelectionChangedListener;
|
---|
| 36 | import org.openstreetmap.josm.data.osm.DataSet;
|
---|
| 37 | import org.openstreetmap.josm.data.osm.Node;
|
---|
| 38 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
| 39 | import org.openstreetmap.josm.data.osm.WaySegment;
|
---|
| 40 | import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
|
---|
| 41 | import org.openstreetmap.josm.data.validation.OsmValidator;
|
---|
| 42 | import org.openstreetmap.josm.data.validation.TestError;
|
---|
| 43 | import org.openstreetmap.josm.data.validation.ValidatorVisitor;
|
---|
| 44 | import org.openstreetmap.josm.gui.MapView;
|
---|
| 45 | import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
|
---|
| 46 | import org.openstreetmap.josm.gui.PleaseWaitRunnable;
|
---|
| 47 | import org.openstreetmap.josm.gui.SideButton;
|
---|
| 48 | import org.openstreetmap.josm.gui.dialogs.validator.ValidatorTreePanel;
|
---|
[3671] | 49 | import org.openstreetmap.josm.gui.layer.Layer;
|
---|
| 50 | import org.openstreetmap.josm.gui.layer.OsmDataLayer;
|
---|
[3669] | 51 | import org.openstreetmap.josm.gui.preferences.ValidatorPreference;
|
---|
| 52 | import org.openstreetmap.josm.gui.progress.ProgressMonitor;
|
---|
| 53 | import org.openstreetmap.josm.io.OsmTransferException;
|
---|
[5028] | 54 | import org.openstreetmap.josm.tools.ImageProvider;
|
---|
[5200] | 55 | import org.openstreetmap.josm.tools.InputMapUtils;
|
---|
[3669] | 56 | import org.openstreetmap.josm.tools.Shortcut;
|
---|
| 57 | import org.xml.sax.SAXException;
|
---|
| 58 |
|
---|
| 59 | /**
|
---|
| 60 | * A small tool dialog for displaying the current errors. The selection manager
|
---|
| 61 | * respects clicks into the selection list. Ctrl-click will remove entries from
|
---|
| 62 | * the list while single click will make the clicked entry the only selection.
|
---|
| 63 | *
|
---|
| 64 | * @author frsantos
|
---|
| 65 | */
|
---|
[5028] | 66 | public class ValidatorDialog extends ToggleDialog implements SelectionChangedListener, LayerChangeListener {
|
---|
[3669] | 67 | /** Serializable ID */
|
---|
| 68 | private static final long serialVersionUID = 2952292777351992696L;
|
---|
| 69 |
|
---|
| 70 | /** The display tree */
|
---|
| 71 | public ValidatorTreePanel tree;
|
---|
| 72 |
|
---|
[3671] | 73 | /** The fix button */
|
---|
[3669] | 74 | private SideButton fixButton;
|
---|
[3671] | 75 | /** The ignore button */
|
---|
[3669] | 76 | private SideButton ignoreButton;
|
---|
[3671] | 77 | /** The select button */
|
---|
[3669] | 78 | private SideButton selectButton;
|
---|
| 79 |
|
---|
| 80 | private JPopupMenu popupMenu;
|
---|
| 81 | private TestError popupMenuError = null;
|
---|
| 82 |
|
---|
| 83 | /** Last selected element */
|
---|
| 84 | private DefaultMutableTreeNode lastSelectedNode = null;
|
---|
| 85 |
|
---|
[5415] | 86 | private OsmDataLayer linkedLayer;
|
---|
| 87 |
|
---|
[3669] | 88 | /**
|
---|
| 89 | * Constructor
|
---|
| 90 | */
|
---|
| 91 | public ValidatorDialog() {
|
---|
[4462] | 92 | super(tr("Validation Results"), "validator", tr("Open the validation window."),
|
---|
[4035] | 93 | Shortcut.registerShortcut("subwindow:validator", tr("Toggle: {0}", tr("Validation results")),
|
---|
[5631] | 94 | KeyEvent.VK_V, Shortcut.ALT_SHIFT), 150, false, ValidatorPreference.class);
|
---|
[3669] | 95 |
|
---|
| 96 | popupMenu = new JPopupMenu();
|
---|
| 97 |
|
---|
| 98 | JMenuItem zoomTo = new JMenuItem(tr("Zoom to problem"));
|
---|
| 99 | zoomTo.addActionListener(new ActionListener() {
|
---|
[3671] | 100 | @Override
|
---|
[3669] | 101 | public void actionPerformed(ActionEvent e) {
|
---|
| 102 | zoomToProblem();
|
---|
| 103 | }
|
---|
| 104 | });
|
---|
| 105 | popupMenu.add(zoomTo);
|
---|
| 106 |
|
---|
| 107 | tree = new ValidatorTreePanel();
|
---|
| 108 | tree.addMouseListener(new ClickWatch());
|
---|
| 109 | tree.addTreeSelectionListener(new SelectionWatch());
|
---|
[5200] | 110 | InputMapUtils.unassignCtrlShiftUpDown(tree, JComponent.WHEN_FOCUSED);
|
---|
[5463] | 111 |
|
---|
[4363] | 112 | List<SideButton> buttons = new LinkedList<SideButton>();
|
---|
[5028] | 113 |
|
---|
| 114 | selectButton = new SideButton(new AbstractAction() {
|
---|
| 115 | {
|
---|
[5532] | 116 | putValue(NAME, tr("Select"));
|
---|
[5028] | 117 | putValue(SHORT_DESCRIPTION, tr("Set the selected elements on the map to the selected items in the list above."));
|
---|
| 118 | putValue(SMALL_ICON, ImageProvider.get("dialogs","select"));
|
---|
| 119 | }
|
---|
| 120 | @Override
|
---|
| 121 | public void actionPerformed(ActionEvent e) {
|
---|
| 122 | setSelectedItems();
|
---|
| 123 | }
|
---|
| 124 | });
|
---|
[5200] | 125 | InputMapUtils.addEnterAction(tree, selectButton.getAction());
|
---|
[5463] | 126 |
|
---|
[3669] | 127 | selectButton.setEnabled(false);
|
---|
[4363] | 128 | buttons.add(selectButton);
|
---|
| 129 |
|
---|
[5526] | 130 | buttons.add(new SideButton(Main.main.validator.validateAction));
|
---|
[4363] | 131 |
|
---|
[5028] | 132 | fixButton = new SideButton(new AbstractAction() {
|
---|
| 133 | {
|
---|
[5532] | 134 | putValue(NAME, tr("Fix"));
|
---|
[5028] | 135 | putValue(SHORT_DESCRIPTION, tr("Fix the selected issue."));
|
---|
| 136 | putValue(SMALL_ICON, ImageProvider.get("dialogs","fix"));
|
---|
| 137 | }
|
---|
| 138 | @Override
|
---|
| 139 | public void actionPerformed(ActionEvent e) {
|
---|
| 140 | fixErrors(e);
|
---|
| 141 | }
|
---|
| 142 | });
|
---|
[3669] | 143 | fixButton.setEnabled(false);
|
---|
[4363] | 144 | buttons.add(fixButton);
|
---|
| 145 |
|
---|
[3669] | 146 | if (Main.pref.getBoolean(ValidatorPreference.PREF_USE_IGNORE, true)) {
|
---|
[5028] | 147 | ignoreButton = new SideButton(new AbstractAction() {
|
---|
| 148 | {
|
---|
[5532] | 149 | putValue(NAME, tr("Ignore"));
|
---|
[5028] | 150 | putValue(SHORT_DESCRIPTION, tr("Ignore the selected issue next time."));
|
---|
| 151 | putValue(SMALL_ICON, ImageProvider.get("dialogs","fix"));
|
---|
| 152 | }
|
---|
| 153 | @Override
|
---|
| 154 | public void actionPerformed(ActionEvent e) {
|
---|
| 155 | ignoreErrors(e);
|
---|
| 156 | }
|
---|
| 157 | });
|
---|
[3669] | 158 | ignoreButton.setEnabled(false);
|
---|
[4363] | 159 | buttons.add(ignoreButton);
|
---|
[3669] | 160 | } else {
|
---|
| 161 | ignoreButton = null;
|
---|
| 162 | }
|
---|
[4363] | 163 | createLayout(tree, true, buttons);
|
---|
[3669] | 164 | }
|
---|
| 165 |
|
---|
| 166 | @Override
|
---|
| 167 | public void showNotify() {
|
---|
| 168 | DataSet.addSelectionListener(this);
|
---|
| 169 | DataSet ds = Main.main.getCurrentDataSet();
|
---|
| 170 | if (ds != null) {
|
---|
[5360] | 171 | updateSelection(ds.getAllSelected());
|
---|
[3669] | 172 | }
|
---|
| 173 | MapView.addLayerChangeListener(this);
|
---|
| 174 | Layer activeLayer = Main.map.mapView.getActiveLayer();
|
---|
| 175 | if (activeLayer != null) {
|
---|
| 176 | activeLayerChange(null, activeLayer);
|
---|
| 177 | }
|
---|
| 178 | }
|
---|
| 179 |
|
---|
| 180 | @Override
|
---|
| 181 | public void hideNotify() {
|
---|
| 182 | MapView.removeLayerChangeListener(this);
|
---|
| 183 | DataSet.removeSelectionListener(this);
|
---|
| 184 | }
|
---|
| 185 |
|
---|
| 186 | @Override
|
---|
| 187 | public void setVisible(boolean v) {
|
---|
[3671] | 188 | if (tree != null) {
|
---|
[3669] | 189 | tree.setVisible(v);
|
---|
[3671] | 190 | }
|
---|
[3669] | 191 | super.setVisible(v);
|
---|
| 192 | Main.map.repaint();
|
---|
| 193 | }
|
---|
| 194 |
|
---|
| 195 | /**
|
---|
| 196 | * Fix selected errors
|
---|
| 197 | *
|
---|
| 198 | * @param e
|
---|
| 199 | */
|
---|
| 200 | @SuppressWarnings("unchecked")
|
---|
| 201 | private void fixErrors(ActionEvent e) {
|
---|
| 202 | TreePath[] selectionPaths = tree.getSelectionPaths();
|
---|
| 203 | if (selectionPaths == null)
|
---|
| 204 | return;
|
---|
| 205 |
|
---|
| 206 | Set<DefaultMutableTreeNode> processedNodes = new HashSet<DefaultMutableTreeNode>();
|
---|
| 207 |
|
---|
| 208 | LinkedList<TestError> errorsToFix = new LinkedList<TestError>();
|
---|
| 209 | for (TreePath path : selectionPaths) {
|
---|
| 210 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
|
---|
[3671] | 211 | if (node == null) {
|
---|
[3669] | 212 | continue;
|
---|
[3671] | 213 | }
|
---|
[3669] | 214 |
|
---|
| 215 | Enumeration<DefaultMutableTreeNode> children = node.breadthFirstEnumeration();
|
---|
| 216 | while (children.hasMoreElements()) {
|
---|
| 217 | DefaultMutableTreeNode childNode = children.nextElement();
|
---|
[3671] | 218 | if (processedNodes.contains(childNode)) {
|
---|
[3669] | 219 | continue;
|
---|
[3671] | 220 | }
|
---|
[3669] | 221 |
|
---|
| 222 | processedNodes.add(childNode);
|
---|
| 223 | Object nodeInfo = childNode.getUserObject();
|
---|
| 224 | if (nodeInfo instanceof TestError) {
|
---|
| 225 | errorsToFix.add((TestError)nodeInfo);
|
---|
| 226 | }
|
---|
| 227 | }
|
---|
| 228 | }
|
---|
| 229 |
|
---|
| 230 | // run fix task asynchronously
|
---|
| 231 | //
|
---|
| 232 | FixTask fixTask = new FixTask(errorsToFix);
|
---|
| 233 | Main.worker.submit(fixTask);
|
---|
| 234 | }
|
---|
| 235 |
|
---|
| 236 | /**
|
---|
| 237 | * Set selected errors to ignore state
|
---|
| 238 | *
|
---|
| 239 | * @param e
|
---|
| 240 | */
|
---|
| 241 | @SuppressWarnings("unchecked")
|
---|
| 242 | private void ignoreErrors(ActionEvent e) {
|
---|
| 243 | int asked = JOptionPane.DEFAULT_OPTION;
|
---|
| 244 | boolean changed = false;
|
---|
| 245 | TreePath[] selectionPaths = tree.getSelectionPaths();
|
---|
| 246 | if (selectionPaths == null)
|
---|
| 247 | return;
|
---|
| 248 |
|
---|
| 249 | Set<DefaultMutableTreeNode> processedNodes = new HashSet<DefaultMutableTreeNode>();
|
---|
| 250 | for (TreePath path : selectionPaths) {
|
---|
| 251 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
|
---|
[3671] | 252 | if (node == null) {
|
---|
[3669] | 253 | continue;
|
---|
[3671] | 254 | }
|
---|
[3669] | 255 |
|
---|
| 256 | Object mainNodeInfo = node.getUserObject();
|
---|
| 257 | if (!(mainNodeInfo instanceof TestError)) {
|
---|
| 258 | Set<String> state = new HashSet<String>();
|
---|
| 259 | // ask if the whole set should be ignored
|
---|
| 260 | if (asked == JOptionPane.DEFAULT_OPTION) {
|
---|
| 261 | String[] a = new String[] { tr("Whole group"), tr("Single elements"), tr("Nothing") };
|
---|
| 262 | asked = JOptionPane.showOptionDialog(Main.parent, tr("Ignore whole group or individual elements?"),
|
---|
| 263 | tr("Ignoring elements"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null,
|
---|
| 264 | a, a[1]);
|
---|
| 265 | }
|
---|
| 266 | if (asked == JOptionPane.YES_NO_OPTION) {
|
---|
| 267 | Enumeration<DefaultMutableTreeNode> children = node.breadthFirstEnumeration();
|
---|
| 268 | while (children.hasMoreElements()) {
|
---|
| 269 | DefaultMutableTreeNode childNode = children.nextElement();
|
---|
[3671] | 270 | if (processedNodes.contains(childNode)) {
|
---|
[3669] | 271 | continue;
|
---|
[3671] | 272 | }
|
---|
[3669] | 273 |
|
---|
| 274 | processedNodes.add(childNode);
|
---|
| 275 | Object nodeInfo = childNode.getUserObject();
|
---|
| 276 | if (nodeInfo instanceof TestError) {
|
---|
| 277 | TestError err = (TestError) nodeInfo;
|
---|
| 278 | err.setIgnored(true);
|
---|
| 279 | changed = true;
|
---|
| 280 | state.add(node.getDepth() == 1 ? err.getIgnoreSubGroup() : err.getIgnoreGroup());
|
---|
| 281 | }
|
---|
| 282 | }
|
---|
| 283 | for (String s : state) {
|
---|
| 284 | OsmValidator.addIgnoredError(s);
|
---|
| 285 | }
|
---|
| 286 | continue;
|
---|
[3671] | 287 | } else if (asked == JOptionPane.CANCEL_OPTION) {
|
---|
[3669] | 288 | continue;
|
---|
[3671] | 289 | }
|
---|
[3669] | 290 | }
|
---|
| 291 |
|
---|
| 292 | Enumeration<DefaultMutableTreeNode> children = node.breadthFirstEnumeration();
|
---|
| 293 | while (children.hasMoreElements()) {
|
---|
| 294 | DefaultMutableTreeNode childNode = children.nextElement();
|
---|
[3671] | 295 | if (processedNodes.contains(childNode)) {
|
---|
[3669] | 296 | continue;
|
---|
[3671] | 297 | }
|
---|
[3669] | 298 |
|
---|
| 299 | processedNodes.add(childNode);
|
---|
| 300 | Object nodeInfo = childNode.getUserObject();
|
---|
| 301 | if (nodeInfo instanceof TestError) {
|
---|
| 302 | TestError error = (TestError) nodeInfo;
|
---|
| 303 | String state = error.getIgnoreState();
|
---|
| 304 | if (state != null) {
|
---|
| 305 | OsmValidator.addIgnoredError(state);
|
---|
| 306 | }
|
---|
| 307 | changed = true;
|
---|
| 308 | error.setIgnored(true);
|
---|
| 309 | }
|
---|
| 310 | }
|
---|
| 311 | }
|
---|
| 312 | if (changed) {
|
---|
| 313 | tree.resetErrors();
|
---|
| 314 | OsmValidator.saveIgnoredErrors();
|
---|
| 315 | Main.map.repaint();
|
---|
| 316 | }
|
---|
| 317 | }
|
---|
| 318 |
|
---|
| 319 | private void showPopupMenu(MouseEvent e) {
|
---|
| 320 | if (!e.isPopupTrigger())
|
---|
| 321 | return;
|
---|
| 322 | popupMenuError = null;
|
---|
| 323 | TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
|
---|
| 324 | if (selPath == null)
|
---|
| 325 | return;
|
---|
| 326 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) selPath.getPathComponent(selPath.getPathCount() - 1);
|
---|
| 327 | if (!(node.getUserObject() instanceof TestError))
|
---|
| 328 | return;
|
---|
| 329 | popupMenuError = (TestError) node.getUserObject();
|
---|
| 330 | popupMenu.show(e.getComponent(), e.getX(), e.getY());
|
---|
| 331 | }
|
---|
| 332 |
|
---|
| 333 | private void zoomToProblem() {
|
---|
| 334 | if (popupMenuError == null)
|
---|
| 335 | return;
|
---|
| 336 | ValidatorBoundingXYVisitor bbox = new ValidatorBoundingXYVisitor();
|
---|
[5671] | 337 | bbox.visit(popupMenuError);
|
---|
[3669] | 338 | if (bbox.getBounds() == null)
|
---|
| 339 | return;
|
---|
[4707] | 340 | bbox.enlargeBoundingBox(Main.pref.getDouble("validator.zoom-enlarge-bbox", 0.0002));
|
---|
[3669] | 341 | Main.map.mapView.recalculateCenterScale(bbox);
|
---|
| 342 | }
|
---|
| 343 |
|
---|
| 344 | /**
|
---|
| 345 | * Sets the selection of the map to the current selected items.
|
---|
| 346 | */
|
---|
| 347 | @SuppressWarnings("unchecked")
|
---|
| 348 | private void setSelectedItems() {
|
---|
| 349 | if (tree == null)
|
---|
| 350 | return;
|
---|
| 351 |
|
---|
| 352 | Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>(40);
|
---|
| 353 |
|
---|
| 354 | TreePath[] selectedPaths = tree.getSelectionPaths();
|
---|
| 355 | if (selectedPaths == null)
|
---|
| 356 | return;
|
---|
| 357 |
|
---|
| 358 | for (TreePath path : selectedPaths) {
|
---|
| 359 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
|
---|
| 360 | Enumeration<DefaultMutableTreeNode> children = node.breadthFirstEnumeration();
|
---|
| 361 | while (children.hasMoreElements()) {
|
---|
| 362 | DefaultMutableTreeNode childNode = children.nextElement();
|
---|
| 363 | Object nodeInfo = childNode.getUserObject();
|
---|
| 364 | if (nodeInfo instanceof TestError) {
|
---|
| 365 | TestError error = (TestError) nodeInfo;
|
---|
[4672] | 366 | sel.addAll(error.getSelectablePrimitives());
|
---|
[3669] | 367 | }
|
---|
| 368 | }
|
---|
| 369 | }
|
---|
[5415] | 370 | DataSet ds = Main.main.getCurrentDataSet();
|
---|
| 371 | if (ds != null) {
|
---|
| 372 | ds.setSelected(sel);
|
---|
| 373 | }
|
---|
[3669] | 374 | }
|
---|
| 375 |
|
---|
| 376 | /**
|
---|
| 377 | * Checks for fixes in selected element and, if needed, adds to the sel
|
---|
| 378 | * parameter all selected elements
|
---|
| 379 | *
|
---|
| 380 | * @param sel
|
---|
| 381 | * The collection where to add all selected elements
|
---|
| 382 | * @param addSelected
|
---|
| 383 | * if true, add all selected elements to collection
|
---|
| 384 | * @return whether the selected elements has any fix
|
---|
| 385 | */
|
---|
| 386 | @SuppressWarnings("unchecked")
|
---|
| 387 | private boolean setSelection(Collection<OsmPrimitive> sel, boolean addSelected) {
|
---|
| 388 | boolean hasFixes = false;
|
---|
| 389 |
|
---|
| 390 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
|
---|
| 391 | if (lastSelectedNode != null && !lastSelectedNode.equals(node)) {
|
---|
| 392 | Enumeration<DefaultMutableTreeNode> children = lastSelectedNode.breadthFirstEnumeration();
|
---|
| 393 | while (children.hasMoreElements()) {
|
---|
| 394 | DefaultMutableTreeNode childNode = children.nextElement();
|
---|
| 395 | Object nodeInfo = childNode.getUserObject();
|
---|
| 396 | if (nodeInfo instanceof TestError) {
|
---|
| 397 | TestError error = (TestError) nodeInfo;
|
---|
| 398 | error.setSelected(false);
|
---|
| 399 | }
|
---|
| 400 | }
|
---|
| 401 | }
|
---|
| 402 |
|
---|
| 403 | lastSelectedNode = node;
|
---|
| 404 | if (node == null)
|
---|
| 405 | return hasFixes;
|
---|
| 406 |
|
---|
| 407 | Enumeration<DefaultMutableTreeNode> children = node.breadthFirstEnumeration();
|
---|
| 408 | while (children.hasMoreElements()) {
|
---|
| 409 | DefaultMutableTreeNode childNode = children.nextElement();
|
---|
| 410 | Object nodeInfo = childNode.getUserObject();
|
---|
| 411 | if (nodeInfo instanceof TestError) {
|
---|
| 412 | TestError error = (TestError) nodeInfo;
|
---|
| 413 | error.setSelected(true);
|
---|
| 414 |
|
---|
| 415 | hasFixes = hasFixes || error.isFixable();
|
---|
| 416 | if (addSelected) {
|
---|
[5463] | 417 | // sel.addAll(error.getPrimitives()); // was selecting already deleted primitives! see #6640
|
---|
| 418 | sel.addAll(error.getSelectablePrimitives());
|
---|
[3669] | 419 | }
|
---|
| 420 | }
|
---|
| 421 | }
|
---|
| 422 | selectButton.setEnabled(true);
|
---|
[3671] | 423 | if (ignoreButton != null) {
|
---|
[3669] | 424 | ignoreButton.setEnabled(true);
|
---|
[3671] | 425 | }
|
---|
[3669] | 426 |
|
---|
| 427 | return hasFixes;
|
---|
| 428 | }
|
---|
| 429 |
|
---|
| 430 | @Override
|
---|
| 431 | public void activeLayerChange(Layer oldLayer, Layer newLayer) {
|
---|
| 432 | if (newLayer instanceof OsmDataLayer) {
|
---|
[5415] | 433 | linkedLayer = (OsmDataLayer)newLayer;
|
---|
| 434 | tree.setErrorList(linkedLayer.validationErrors);
|
---|
[3669] | 435 | }
|
---|
| 436 | }
|
---|
| 437 |
|
---|
| 438 | @Override
|
---|
| 439 | public void layerAdded(Layer newLayer) {}
|
---|
| 440 |
|
---|
| 441 | @Override
|
---|
[5415] | 442 | public void layerRemoved(Layer oldLayer) {
|
---|
| 443 | if (oldLayer == linkedLayer) {
|
---|
| 444 | tree.setErrorList(new ArrayList<TestError>());
|
---|
| 445 | }
|
---|
| 446 | }
|
---|
[3669] | 447 |
|
---|
| 448 | /**
|
---|
| 449 | * Watches for clicks.
|
---|
| 450 | */
|
---|
| 451 | public class ClickWatch extends MouseAdapter {
|
---|
| 452 | @Override
|
---|
| 453 | public void mouseClicked(MouseEvent e) {
|
---|
| 454 | fixButton.setEnabled(false);
|
---|
[3671] | 455 | if (ignoreButton != null) {
|
---|
[3669] | 456 | ignoreButton.setEnabled(false);
|
---|
[3671] | 457 | }
|
---|
[3669] | 458 | selectButton.setEnabled(false);
|
---|
| 459 |
|
---|
| 460 | boolean isDblClick = e.getClickCount() > 1;
|
---|
| 461 |
|
---|
| 462 | Collection<OsmPrimitive> sel = isDblClick ? new HashSet<OsmPrimitive>(40) : null;
|
---|
| 463 |
|
---|
| 464 | boolean hasFixes = setSelection(sel, isDblClick);
|
---|
| 465 | fixButton.setEnabled(hasFixes);
|
---|
| 466 |
|
---|
| 467 | if (isDblClick) {
|
---|
| 468 | Main.main.getCurrentDataSet().setSelected(sel);
|
---|
[3671] | 469 | if(Main.pref.getBoolean("validator.autozoom", false)) {
|
---|
[3669] | 470 | AutoScaleAction.zoomTo(sel);
|
---|
[3671] | 471 | }
|
---|
[3669] | 472 | }
|
---|
| 473 | }
|
---|
| 474 |
|
---|
| 475 | @Override
|
---|
| 476 | public void mousePressed(MouseEvent e) {
|
---|
| 477 | showPopupMenu(e);
|
---|
| 478 | }
|
---|
| 479 |
|
---|
| 480 | @Override
|
---|
| 481 | public void mouseReleased(MouseEvent e) {
|
---|
| 482 | showPopupMenu(e);
|
---|
| 483 | }
|
---|
| 484 |
|
---|
| 485 | }
|
---|
| 486 |
|
---|
| 487 | /**
|
---|
| 488 | * Watches for tree selection.
|
---|
| 489 | */
|
---|
| 490 | public class SelectionWatch implements TreeSelectionListener {
|
---|
[3671] | 491 | @Override
|
---|
[3669] | 492 | public void valueChanged(TreeSelectionEvent e) {
|
---|
| 493 | fixButton.setEnabled(false);
|
---|
[3671] | 494 | if (ignoreButton != null) {
|
---|
[3669] | 495 | ignoreButton.setEnabled(false);
|
---|
[3671] | 496 | }
|
---|
[3669] | 497 | selectButton.setEnabled(false);
|
---|
| 498 |
|
---|
| 499 | boolean hasFixes = setSelection(null, false);
|
---|
| 500 | fixButton.setEnabled(hasFixes);
|
---|
| 501 | Main.map.repaint();
|
---|
| 502 | }
|
---|
| 503 | }
|
---|
| 504 |
|
---|
| 505 | public static class ValidatorBoundingXYVisitor extends BoundingXYVisitor implements ValidatorVisitor {
|
---|
[3671] | 506 | @Override
|
---|
[3669] | 507 | public void visit(OsmPrimitive p) {
|
---|
| 508 | if (p.isUsable()) {
|
---|
| 509 | p.visit(this);
|
---|
| 510 | }
|
---|
| 511 | }
|
---|
| 512 |
|
---|
[3671] | 513 | @Override
|
---|
[3669] | 514 | public void visit(WaySegment ws) {
|
---|
| 515 | if (ws.lowerIndex < 0 || ws.lowerIndex + 1 >= ws.way.getNodesCount())
|
---|
| 516 | return;
|
---|
| 517 | visit(ws.way.getNodes().get(ws.lowerIndex));
|
---|
| 518 | visit(ws.way.getNodes().get(ws.lowerIndex + 1));
|
---|
| 519 | }
|
---|
| 520 |
|
---|
[3671] | 521 | @Override
|
---|
[3669] | 522 | public void visit(List<Node> nodes) {
|
---|
| 523 | for (Node n: nodes) {
|
---|
| 524 | visit(n);
|
---|
| 525 | }
|
---|
| 526 | }
|
---|
[5671] | 527 |
|
---|
| 528 | @Override
|
---|
| 529 | public void visit(TestError error) {
|
---|
| 530 | if (error != null) {
|
---|
| 531 | error.visitHighlighted(this);
|
---|
| 532 | }
|
---|
| 533 | }
|
---|
[3669] | 534 | }
|
---|
| 535 |
|
---|
| 536 | public void updateSelection(Collection<? extends OsmPrimitive> newSelection) {
|
---|
| 537 | if (!Main.pref.getBoolean(ValidatorPreference.PREF_FILTER_BY_SELECTION, false))
|
---|
| 538 | return;
|
---|
[3671] | 539 | if (newSelection.isEmpty()) {
|
---|
[3669] | 540 | tree.setFilter(null);
|
---|
[3671] | 541 | }
|
---|
[3669] | 542 | HashSet<OsmPrimitive> filter = new HashSet<OsmPrimitive>(newSelection);
|
---|
| 543 | tree.setFilter(filter);
|
---|
| 544 | }
|
---|
| 545 |
|
---|
[3671] | 546 | @Override
|
---|
[3669] | 547 | public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
|
---|
| 548 | updateSelection(newSelection);
|
---|
| 549 | }
|
---|
| 550 |
|
---|
| 551 | /**
|
---|
[5266] | 552 | * Task for fixing a collection of {@link TestError}s. Can be run asynchronously.
|
---|
[3669] | 553 | *
|
---|
| 554 | *
|
---|
| 555 | */
|
---|
| 556 | class FixTask extends PleaseWaitRunnable {
|
---|
| 557 | private Collection<TestError> testErrors;
|
---|
| 558 | private boolean canceled;
|
---|
| 559 |
|
---|
| 560 | public FixTask(Collection<TestError> testErrors) {
|
---|
| 561 | super(tr("Fixing errors ..."), false /* don't ignore exceptions */);
|
---|
| 562 | this.testErrors = testErrors == null ? new ArrayList<TestError> (): testErrors;
|
---|
| 563 | }
|
---|
| 564 |
|
---|
| 565 | @Override
|
---|
| 566 | protected void cancel() {
|
---|
| 567 | this.canceled = true;
|
---|
| 568 | }
|
---|
| 569 |
|
---|
| 570 | @Override
|
---|
| 571 | protected void finish() {
|
---|
| 572 | // do nothing
|
---|
| 573 | }
|
---|
[5673] | 574 |
|
---|
| 575 | protected void fixError(TestError error) throws InterruptedException, InvocationTargetException {
|
---|
| 576 | if (error.isFixable()) {
|
---|
| 577 | final Command fixCommand = error.getFix();
|
---|
| 578 | if (fixCommand != null) {
|
---|
| 579 | SwingUtilities.invokeAndWait(new Runnable() {
|
---|
| 580 | @Override
|
---|
| 581 | public void run() {
|
---|
| 582 | Main.main.undoRedo.addNoRedraw(fixCommand);
|
---|
| 583 | }
|
---|
| 584 | });
|
---|
| 585 | }
|
---|
| 586 | // It is wanted to ignore an error if it said fixable, even if fixCommand was null
|
---|
| 587 | // This is to fix #5764 and #5773: a delete command, for example, may be null if all concerned primitives have already been deleted
|
---|
| 588 | error.setIgnored(true);
|
---|
| 589 | }
|
---|
| 590 | }
|
---|
[3669] | 591 |
|
---|
| 592 | @Override
|
---|
| 593 | protected void realRun() throws SAXException, IOException,
|
---|
| 594 | OsmTransferException {
|
---|
| 595 | ProgressMonitor monitor = getProgressMonitor();
|
---|
| 596 | try {
|
---|
| 597 | monitor.setTicksCount(testErrors.size());
|
---|
| 598 | int i=0;
|
---|
[5673] | 599 | SwingUtilities.invokeAndWait(new Runnable() {
|
---|
| 600 | @Override
|
---|
| 601 | public void run() {
|
---|
| 602 | Main.main.getCurrentDataSet().beginUpdate();
|
---|
| 603 | }
|
---|
| 604 | });
|
---|
| 605 | try {
|
---|
| 606 | for (TestError error: testErrors) {
|
---|
| 607 | i++;
|
---|
| 608 | monitor.subTask(tr("Fixing ({0}/{1}): ''{2}''", i, testErrors.size(),error.getMessage()));
|
---|
| 609 | if (this.canceled)
|
---|
| 610 | return;
|
---|
| 611 | fixError(error);
|
---|
| 612 | monitor.worked(1);
|
---|
| 613 | }
|
---|
| 614 | } finally {
|
---|
| 615 | SwingUtilities.invokeAndWait(new Runnable() {
|
---|
| 616 | @Override
|
---|
| 617 | public void run() {
|
---|
| 618 | Main.main.getCurrentDataSet().endUpdate();
|
---|
[5287] | 619 | }
|
---|
[5673] | 620 | });
|
---|
[3669] | 621 | }
|
---|
| 622 | monitor.subTask(tr("Updating map ..."));
|
---|
| 623 | SwingUtilities.invokeAndWait(new Runnable() {
|
---|
[3671] | 624 | @Override
|
---|
[3669] | 625 | public void run() {
|
---|
| 626 | Main.main.undoRedo.afterAdd();
|
---|
| 627 | Main.map.repaint();
|
---|
| 628 | tree.resetErrors();
|
---|
| 629 | Main.main.getCurrentDataSet().fireSelectionChanged();
|
---|
| 630 | }
|
---|
| 631 | });
|
---|
| 632 | } catch(InterruptedException e) {
|
---|
| 633 | // FIXME: signature of realRun should have a generic checked exception we
|
---|
| 634 | // could throw here
|
---|
| 635 | throw new RuntimeException(e);
|
---|
| 636 | } catch(InvocationTargetException e) {
|
---|
| 637 | throw new RuntimeException(e);
|
---|
| 638 | } finally {
|
---|
| 639 | monitor.finishTask();
|
---|
| 640 | }
|
---|
| 641 | }
|
---|
| 642 | }
|
---|
| 643 | }
|
---|