Ticket #21951: 21951.patch
| File 21951.patch, 10.6 KB (added by , 4 years ago) |
|---|
-
src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java
diff --git a/src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java b/src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java index 959f73d1d0..1c10a7c575 100644
a b public class RelationChecker extends Test implements TaggingPresetListener { 154 154 .message(tr("Route scheme is unspecified. Add {0} ({1}=public_transport; {2}=legacy)", "public_transport:version", "2", "1")) 155 155 .primitives(n) 156 156 .build()); 157 } else if ( n.hasKey("type") && allroles.isEmpty()) {157 } else if ((n.hasKey("type") && allroles.isEmpty()) || !n.hasKey("type")) { 158 158 errors.add(TestError.builder(this, Severity.OTHER, RELATION_UNKNOWN) 159 159 .message(tr("Relation type is unknown")) 160 160 .primitives(n) -
src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java b/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java index 743e05f4e0..c5e10ad531 100644
a b package org.openstreetmap.josm.gui.dialogs.relation; 3 3 4 4 import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 5 5 import static org.openstreetmap.josm.tools.I18n.tr; 6 import static org.openstreetmap.josm.tools.I18n.trn; 6 7 7 8 import java.awt.BorderLayout; 8 9 import java.awt.Component; … … import java.util.ArrayList; 26 27 import java.util.Arrays; 27 28 import java.util.Collection; 28 29 import java.util.Collections; 30 import java.util.Comparator; 29 31 import java.util.EnumSet; 30 32 import java.util.List; 31 33 import java.util.Set; 34 import java.util.concurrent.Future; 35 import java.util.concurrent.atomic.AtomicReference; 32 36 import java.util.stream.Collectors; 33 37 34 38 import javax.swing.AbstractAction; … … import javax.swing.JTabbedPane; 47 51 import javax.swing.JTable; 48 52 import javax.swing.JToolBar; 49 53 import javax.swing.KeyStroke; 54 import javax.swing.event.TableModelListener; 55 import javax.swing.table.TableModel; 50 56 51 57 import org.openstreetmap.josm.actions.JosmAction; 52 58 import org.openstreetmap.josm.command.ChangeMembersCommand; … … import org.openstreetmap.josm.command.Command; 54 60 import org.openstreetmap.josm.data.UndoRedoHandler; 55 61 import org.openstreetmap.josm.data.UndoRedoHandler.CommandQueueListener; 56 62 import org.openstreetmap.josm.data.osm.DefaultNameFormatter; 63 import org.openstreetmap.josm.data.osm.IRelation; 57 64 import org.openstreetmap.josm.data.osm.OsmPrimitive; 58 65 import org.openstreetmap.josm.data.osm.Relation; 59 66 import org.openstreetmap.josm.data.osm.RelationMember; 60 67 import org.openstreetmap.josm.data.osm.Tag; 68 import org.openstreetmap.josm.data.validation.TestError; 61 69 import org.openstreetmap.josm.data.validation.tests.RelationChecker; 62 70 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 63 71 import org.openstreetmap.josm.gui.MainApplication; … … import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; 105 113 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler; 106 114 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType; 107 115 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets; 116 import org.openstreetmap.josm.gui.util.GuiHelper; 108 117 import org.openstreetmap.josm.gui.util.WindowGeometry; 109 118 import org.openstreetmap.josm.spi.preferences.Config; 110 119 import org.openstreetmap.josm.tools.CheckParameterUtil; 120 import org.openstreetmap.josm.tools.GBC; 121 import org.openstreetmap.josm.tools.ImageProvider; 111 122 import org.openstreetmap.josm.tools.InputMapUtils; 112 123 import org.openstreetmap.josm.tools.Logging; 113 124 import org.openstreetmap.josm.tools.Shortcut; … … public class GenericRelationEditor extends RelationEditor implements CommandQueu 132 143 private final SelectionTableModel selectionTableModel; 133 144 134 145 private final AutoCompletingTextField tfRole; 146 private final RelationEditorActionAccess actionAccess; 135 147 136 148 /** 137 149 * the menu item in the windows menu. Required to properly hide on dialog close. … … public class GenericRelationEditor extends RelationEditor implements CommandQueu 262 274 selectedTabPane = sourceTabbedPane.getSelectedComponent(); 263 275 }); 264 276 265 IRelationEditorActionAccessactionAccess = new RelationEditorActionAccess();277 actionAccess = new RelationEditorActionAccess(); 266 278 267 279 refreshAction = new RefreshAction(actionAccess); 268 280 applyAction = new ApplyAction(actionAccess); 269 281 selectAction = new SelectAction(actionAccess); 270 282 duplicateAction = new DuplicateRelationAction(actionAccess); 271 283 deleteAction = new DeleteCurrentRelationAction(actionAccess); 284 285 this.memberTableModel.addTableModelListener(applyAction); 286 this.tagEditorPanel.getModel().addTableModelListener(l -> { 287 // Short circuit -- the tag editor will fire an update event on selection change, even if nothing actually 288 // changed. 289 if (l != null && l.getSource() instanceof TagEditorModel && !((TagEditorModel) l.getSource()).isDirty()) { 290 return; 291 } 292 applyAction.tableChanged(l); 293 }); 294 272 295 addPropertyChangeListener(deleteAction); 273 296 274 297 okAction = new OKAction(actionAccess); … … public class GenericRelationEditor extends RelationEditor implements CommandQueu 276 299 277 300 getContentPane().add(buildToolBar(refreshAction, applyAction, selectAction, duplicateAction, deleteAction), BorderLayout.NORTH); 278 301 getContentPane().add(tabbedPane, BorderLayout.CENTER); 279 getContentPane().add(buildOkCancelButtonPanel(okAction, cancelAction), BorderLayout.SOUTH);302 getContentPane().add(buildOkCancelButtonPanel(okAction, deleteAction, cancelAction), BorderLayout.SOUTH); 280 303 281 304 setSize(findMaxDialogSize()); 282 305 … … public class GenericRelationEditor extends RelationEditor implements CommandQueu 407 430 * 408 431 * @return the panel with the OK and the Cancel button 409 432 */ 410 protected static JPanel buildOkCancelButtonPanel(OKAction okAction, CancelAction cancelAction) { 411 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER)); 412 pnl.add(new JButton(okAction)); 433 protected JPanel buildOkCancelButtonPanel(OKAction okAction, DeleteCurrentRelationAction deleteAction, 434 CancelAction cancelAction) { 435 JPanel mainPanel = new JPanel(new GridBagLayout()); 436 final JLabel errorLabel = new JLabel(" "); 437 final JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER)); 438 mainPanel.add(errorLabel, GBC.eol().anchor(GridBagConstraints.CENTER)); 439 mainPanel.add(pnl, GBC.eol().anchor(GridBagConstraints.CENTER)); 440 final JButton okButton = new JButton(okAction); 441 final JButton deleteButton = new JButton(deleteAction); 442 okButton.setPreferredSize(deleteButton.getPreferredSize()); 443 pnl.add(okButton); 444 pnl.add(deleteButton); 413 445 pnl.add(new JButton(cancelAction)); 414 446 pnl.add(new JButton(new ContextSensitiveHelpAction(ht("/Dialog/RelationEditor")))); 415 return pnl; 447 // Keep users from saving invalid relations -- a relation MUST have at least a tag with the key "type" 448 // AND must contain at least one other OSM object. 449 final AtomicReference<Future<?>> testErrorsUpdate = new AtomicReference<>(); 450 final TableModelListener listener = l -> { 451 final IRelation<?> newRelation = this.actionAccess.getChangedRelation(); 452 updateOkPanel(newRelation, okButton, deleteButton); 453 final Future<?> oldFuture = testErrorsUpdate.getAndSet(MainApplication.worker.submit(() -> { 454 GuiHelper.runInEDTAndWait(() -> errorLabel.setText(tr("Validation in progress"))); 455 final Collection<TestError> testErrors = newRelation.validate(); 456 GuiHelper.runInEDTAndWait(() -> updateErrorMessage(testErrors, errorLabel)); 457 })); 458 if (oldFuture != null) { 459 oldFuture.cancel(true); 460 } 461 }; 462 listener.tableChanged(null); 463 this.memberTableModel.addTableModelListener(listener); 464 this.tagEditorPanel.getModel().addTableModelListener(l -> { 465 // Short circuit -- the tag editor will fire an update event on selection change, even if nothing actually 466 // changed. 467 if (l != null && l.getSource() instanceof TagEditorModel && !((TagEditorModel) l.getSource()).isDirty()) { 468 return; 469 } 470 listener.tableChanged(l); 471 }); 472 return mainPanel; 473 } 474 475 /** 476 * Update the OK panel area 477 * @param newRelation What the new relation would "look" like if it were to be saved now 478 * @param okButton The OK button 479 * @param deleteButton The delete button 480 */ 481 private void updateOkPanel(IRelation<?> newRelation, JButton okButton, JButton deleteButton) { 482 okButton.setVisible(newRelation.isUseful() || this.getRelationSnapshot() == null); 483 deleteButton.setVisible(!newRelation.isUseful() && this.getRelationSnapshot() != null); 484 if (this.getRelationSnapshot() == null && !newRelation.isUseful()) { 485 okButton.setText(tr("Delete")); 486 } else { 487 okButton.setText(tr("OK")); 488 } 489 } 490 491 /** 492 * Update the error message 493 * @param newRelation What the new relation would "look" like if it were to be saved now 494 * @param errorLabel The label to update 495 */ 496 private void updateErrorMessage(Collection<TestError> errors, JLabel errorLabel) { 497 if (errors.isEmpty()) { 498 // Setting " " helps keep the label in place for layout calculations 499 errorLabel.setText(" "); 500 errorLabel.setIcon(null); 501 } else { 502 final TestError error = errors.stream() 503 .min(Comparator.comparingInt(testError -> testError.getSeverity().getLevel())) 504 .orElse(errors.iterator().next()); 505 final StringBuilder sb = new StringBuilder(); 506 if (error.getDescription() != null) { 507 sb.append(error.getDescription()).append(": "); 508 } 509 sb.append(error.getMessage()); 510 if (errors.size() > 1) { 511 sb.append(trn(" with {0} more problem", " with {0} more problems", errors.size() - 1, errors.size() - 1)); 512 } 513 errorLabel.setIcon(ImageProvider.get("data", error.getSeverity().getIcon())); 514 errorLabel.setText(sb.toString()); 515 } 416 516 } 417 517 418 518 /**
