From cadddb644f00349f85588e9537d78b5f18236ac5 Mon Sep 17 00:00:00 2001 From: Florian Kargl Date: Thu, 17 Mar 2022 02:01:25 +0100 Subject: [PATCH] Add 'replace selected' action to relation editor --- .../dialogs/relation/replaceselectedright.svg | 15 +++++ .../relation/GenericRelationEditor.java | 6 +- .../dialogs/relation/MemberTableModel.java | 36 ++++++++++++ .../actions/AddFromSelectionAction.java | 57 +++++++++++++++---- .../actions/ReplaceSelectedAction.java | 57 +++++++++++++++++++ 5 files changed, 158 insertions(+), 13 deletions(-) create mode 100644 resources/images/dialogs/relation/replaceselectedright.svg create mode 100644 src/org/openstreetmap/josm/gui/dialogs/relation/actions/ReplaceSelectedAction.java diff --git a/resources/images/dialogs/relation/replaceselectedright.svg b/resources/images/dialogs/relation/replaceselectedright.svg new file mode 100644 index 00000000000..e5e6b5e3e29 --- /dev/null +++ b/resources/images/dialogs/relation/replaceselectedright.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java b/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java index f597a078c0d..74aca9559c6 100644 --- a/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java +++ b/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java @@ -88,6 +88,7 @@ import org.openstreetmap.josm.gui.dialogs.relation.actions.RefreshAction; import org.openstreetmap.josm.gui.dialogs.relation.actions.RemoveAction; import org.openstreetmap.josm.gui.dialogs.relation.actions.RemoveSelectedAction; +import org.openstreetmap.josm.gui.dialogs.relation.actions.ReplaceSelectedAction; import org.openstreetmap.josm.gui.dialogs.relation.actions.ReverseAction; import org.openstreetmap.josm.gui.dialogs.relation.actions.SelectAction; import org.openstreetmap.josm.gui.dialogs.relation.actions.SelectPrimitivesForSelectedMembersAction; @@ -737,10 +738,13 @@ protected static JToolBar buildSelectionControlButtonToolbar(IRelationEditorActi new AddSelectedAtEndAction(editorAccess) )); groups.add(buildNativeGroup(20, + new ReplaceSelectedAction(editorAccess) + )); + groups.add(buildNativeGroup(30, new SelectedMembersForSelectionAction(editorAccess), new SelectPrimitivesForSelectedMembersAction(editorAccess) )); - groups.add(buildNativeGroup(30, + groups.add(buildNativeGroup(40, new RemoveSelectedAction(editorAccess) )); groups.addAll(RelationEditorHooks.getSelectActions()); diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java b/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java index 962ccc25aff..1c5bcfe8c83 100644 --- a/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java +++ b/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java @@ -520,6 +520,42 @@ public void updateRole(int[] idx, String role) { addToSelectedMembers(selected); } + /** + * updates the referenced primitive of the members given by the index in index + * + * @param index the index to update + * @param newPrimitive the new primitive + * @since xxx + */ + public void updateMemberPrimitive(int index, OsmPrimitive newPrimitive) { + if (index >= members.size()) { + return; + } + + RelationMember newMember = new RelationMember(members.get(index).getRole(), newPrimitive); + updateMember(index, newMember); + } + + /** + * replace the member at index with a new one + * + * @param index the index to update + * @param newMember the new member + * @since xxx + */ + public void updateMember(int index, RelationMember newMember) { + if (index >= members.size()) { + return; + } + + RelationMember oldMember = members.get(index); + if (oldMember.equals(newMember)) + return; + + setValue(index, newMember); + fireTableDataChanged(); + } + /** * Get the currently selected relation members * diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/actions/AddFromSelectionAction.java b/src/org/openstreetmap/josm/gui/dialogs/relation/actions/AddFromSelectionAction.java index b1740cc06ac..2d729b840c0 100644 --- a/src/org/openstreetmap/josm/gui/dialogs/relation/actions/AddFromSelectionAction.java +++ b/src/org/openstreetmap/josm/gui/dialogs/relation/actions/AddFromSelectionAction.java @@ -29,28 +29,61 @@ protected boolean isPotentialDuplicate(OsmPrimitive primitive) { return editorAccess.getMemberTableModel().hasMembersReferringTo(Collections.singleton(primitive)); } + /** + * Check and filter a list of primitives before adding them as relation members. + * Prompt users for confirmation when duplicates are detected and prevent relation loops. + * + * @param primitives The primitives to be checked and filtered + * @return The primitives to add to the relation. Never {@code null}, but may be an empty list. + * @throws AddAbortException when a relation loop is detected + */ protected List filterConfirmedPrimitives(List primitives) throws AddAbortException { + return filterConfirmedPrimitives(primitives, false); + } + + /** + * Check and filter a list of primitives before adding them as relation members. + * Prompt users for confirmation when duplicates are detected and prevent relation loops. + * + * @param primitives The primitives to be checked and filtered + * @param abortOnSkip If the user decides to not add a primitive or adding a primitive would + * cause a relation loop, abort (throw {@code AddAbortException}) + * @return The primitives to add to the relation. Never {@code null}, but may be an empty list. + * @throws AddAbortException when a relation loop is detected or {@code abortOnSkip} is + * {@code true} and the user decides to not add a primitive. + * @since xxx + */ + protected List filterConfirmedPrimitives(List primitives, boolean abortOnSkip) throws AddAbortException { if (Utils.isEmpty(primitives)) return primitives; List ret = new ArrayList<>(); ConditionalOptionPaneUtil.startBulkOperation("add_primitive_to_relation"); - for (OsmPrimitive primitive : primitives) { - if (primitive instanceof Relation) { - List loop = RelationChecker.checkAddMember(editorAccess.getEditor().getRelation(), (Relation) primitive); - if (!loop.isEmpty() && loop.get(0).equals(loop.get(loop.size() - 1))) { - GenericRelationEditor.warnOfCircularReferences(primitive, loop); - continue; + try { + for (OsmPrimitive primitive : primitives) { + if (primitive instanceof Relation) { + List loop = RelationChecker.checkAddMember(editorAccess.getEditor().getRelation(), (Relation) primitive); + if (!loop.isEmpty() && loop.get(0).equals(loop.get(loop.size() - 1))) { + GenericRelationEditor.warnOfCircularReferences(primitive, loop); + if (abortOnSkip) { + throw new AddAbortException(); + } + continue; + } } - } - if (isPotentialDuplicate(primitive)) { - if (GenericRelationEditor.confirmAddingPrimitive(primitive)) { + if (isPotentialDuplicate(primitive)) { + if (GenericRelationEditor.confirmAddingPrimitive(primitive)) { + ret.add(primitive); + } else if (abortOnSkip) { + throw new AddAbortException(); + } + } else { ret.add(primitive); } - } else { - ret.add(primitive); } + } finally { + ConditionalOptionPaneUtil.endBulkOperation("add_primitive_to_relation"); } - ConditionalOptionPaneUtil.endBulkOperation("add_primitive_to_relation"); + return ret; } } diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/actions/ReplaceSelectedAction.java b/src/org/openstreetmap/josm/gui/dialogs/relation/actions/ReplaceSelectedAction.java new file mode 100644 index 00000000000..097225878d3 --- /dev/null +++ b/src/org/openstreetmap/josm/gui/dialogs/relation/actions/ReplaceSelectedAction.java @@ -0,0 +1,57 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.gui.dialogs.relation.actions; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.awt.event.ActionEvent; +import java.util.List; + +import org.openstreetmap.josm.data.osm.OsmPrimitive; +import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor.AddAbortException; +import org.openstreetmap.josm.tools.ImageProvider; +import org.openstreetmap.josm.tools.Logging; + +/** + * Replace selected relation members with the objects selected in the current dataset + * @since xxx + */ +public class ReplaceSelectedAction extends AddFromSelectionAction { + + /** + * Constructs a new {@code ReplaceSelectedAction}. + * @param editorAccess An interface to access the relation editor contents. + */ + public ReplaceSelectedAction(IRelationEditorActionAccess editorAccess) { + super(editorAccess, IRelationEditorUpdateOn.MEMBER_TABLE_SELECTION, IRelationEditorUpdateOn.SELECTION_TABLE_CHANGE); + putValue(SHORT_DESCRIPTION, tr("Replace selected members with selected objects")); + new ImageProvider("dialogs/relation", "replaceselectedright").getResource().attachImageIcon(this, true); + updateEnabledState(); + } + + @Override + protected void updateEnabledState() { + int numSelected = getSelectionTableModel().getRowCount(); + setEnabled(numSelected > 0 && + numSelected == getMemberTableModel().getSelectedIndices().length); + } + + @Override + public void actionPerformed(ActionEvent e) { + try { + int[] selectedMemberIndices = getMemberTableModel().getSelectedIndices(); + List selection = getSelectionTableModel().getSelection(); + int numSelectedPrimitives = selection.size(); + if (numSelectedPrimitives != selectedMemberIndices.length) { + return; + } + + List filteredSelection = filterConfirmedPrimitives(selection, true); + + for (int i = 0; i < selectedMemberIndices.length; i++) { + getMemberTableModel().updateMemberPrimitive(selectedMemberIndices[i], filteredSelection.get(i)); + } + } catch (AddAbortException ex) { + Logging.trace(ex); + } + } +}