diff --git a/src/org/openstreetmap/josm/data/osm/IRelation.java b/src/org/openstreetmap/josm/data/osm/IRelation.java
index 6d59bd427a..9a7897d36f 100644
a
|
b
|
public interface IRelation<M extends IRelationMember<?>> extends IPrimitive {
|
138 | 140 | return getMembers().stream().filter(rmv -> role.equals(rmv.getRole())) |
139 | 141 | .map(IRelationMember::getMember).collect(Collectors.toList()); |
140 | 142 | } |
| 143 | |
| 144 | /** |
| 145 | * Check if this relation is useful |
| 146 | * @return {@code true} if this relation is useful |
| 147 | * @since xxx |
| 148 | */ |
| 149 | default boolean isUseful() { |
| 150 | return !this.isEmpty() && this.hasKeys(); |
| 151 | } |
141 | 152 | } |
diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java b/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
index 743e05f4e0..f597a078c0 100644
a
|
b
|
import javax.swing.JTabbedPane;
|
47 | 47 | import javax.swing.JTable; |
48 | 48 | import javax.swing.JToolBar; |
49 | 49 | import javax.swing.KeyStroke; |
| 50 | import javax.swing.event.TableModelListener; |
50 | 51 | |
51 | 52 | import org.openstreetmap.josm.actions.JosmAction; |
52 | 53 | import org.openstreetmap.josm.command.ChangeMembersCommand; |
… |
… |
import org.openstreetmap.josm.command.Command;
|
54 | 55 | import org.openstreetmap.josm.data.UndoRedoHandler; |
55 | 56 | import org.openstreetmap.josm.data.UndoRedoHandler.CommandQueueListener; |
56 | 57 | import org.openstreetmap.josm.data.osm.DefaultNameFormatter; |
| 58 | import org.openstreetmap.josm.data.osm.IRelation; |
57 | 59 | import org.openstreetmap.josm.data.osm.OsmPrimitive; |
58 | 60 | import org.openstreetmap.josm.data.osm.Relation; |
59 | 61 | import org.openstreetmap.josm.data.osm.RelationMember; |
… |
… |
public class GenericRelationEditor extends RelationEditor implements CommandQueu
|
132 | 134 | private final SelectionTableModel selectionTableModel; |
133 | 135 | |
134 | 136 | private final AutoCompletingTextField tfRole; |
| 137 | private final RelationEditorActionAccess actionAccess; |
135 | 138 | |
136 | 139 | /** |
137 | 140 | * the menu item in the windows menu. Required to properly hide on dialog close. |
… |
… |
public class GenericRelationEditor extends RelationEditor implements CommandQueu
|
262 | 265 | selectedTabPane = sourceTabbedPane.getSelectedComponent(); |
263 | 266 | }); |
264 | 267 | |
265 | | IRelationEditorActionAccess actionAccess = new RelationEditorActionAccess(); |
| 268 | actionAccess = new RelationEditorActionAccess(); |
266 | 269 | |
267 | 270 | refreshAction = new RefreshAction(actionAccess); |
268 | 271 | applyAction = new ApplyAction(actionAccess); |
269 | 272 | selectAction = new SelectAction(actionAccess); |
270 | 273 | duplicateAction = new DuplicateRelationAction(actionAccess); |
271 | 274 | deleteAction = new DeleteCurrentRelationAction(actionAccess); |
| 275 | |
| 276 | this.memberTableModel.addTableModelListener(applyAction); |
| 277 | this.tagEditorPanel.getModel().addTableModelListener(applyAction); |
| 278 | |
272 | 279 | addPropertyChangeListener(deleteAction); |
273 | 280 | |
274 | 281 | okAction = new OKAction(actionAccess); |
… |
… |
public class GenericRelationEditor extends RelationEditor implements CommandQueu
|
276 | 283 | |
277 | 284 | getContentPane().add(buildToolBar(refreshAction, applyAction, selectAction, duplicateAction, deleteAction), BorderLayout.NORTH); |
278 | 285 | getContentPane().add(tabbedPane, BorderLayout.CENTER); |
279 | | getContentPane().add(buildOkCancelButtonPanel(okAction, cancelAction), BorderLayout.SOUTH); |
| 286 | getContentPane().add(buildOkCancelButtonPanel(okAction, deleteAction, cancelAction), BorderLayout.SOUTH); |
280 | 287 | |
281 | 288 | setSize(findMaxDialogSize()); |
282 | 289 | |
… |
… |
public class GenericRelationEditor extends RelationEditor implements CommandQueu
|
407 | 414 | * |
408 | 415 | * @return the panel with the OK and the Cancel button |
409 | 416 | */ |
410 | | protected static JPanel buildOkCancelButtonPanel(OKAction okAction, CancelAction cancelAction) { |
411 | | JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER)); |
412 | | pnl.add(new JButton(okAction)); |
| 417 | protected final JPanel buildOkCancelButtonPanel(OKAction okAction, DeleteCurrentRelationAction deleteAction, |
| 418 | CancelAction cancelAction) { |
| 419 | final JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER)); |
| 420 | final JButton okButton = new JButton(okAction); |
| 421 | final JButton deleteButton = new JButton(deleteAction); |
| 422 | okButton.setPreferredSize(deleteButton.getPreferredSize()); |
| 423 | pnl.add(okButton); |
| 424 | pnl.add(deleteButton); |
413 | 425 | pnl.add(new JButton(cancelAction)); |
414 | 426 | pnl.add(new JButton(new ContextSensitiveHelpAction(ht("/Dialog/RelationEditor")))); |
| 427 | // Keep users from saving invalid relations -- a relation MUST have at least a tag with the key "type" |
| 428 | // AND must contain at least one other OSM object. |
| 429 | final TableModelListener listener = l -> updateOkPanel(this.actionAccess.getChangedRelation(), okButton, deleteButton); |
| 430 | listener.tableChanged(null); |
| 431 | this.memberTableModel.addTableModelListener(listener); |
| 432 | this.tagEditorPanel.getModel().addTableModelListener(listener); |
415 | 433 | return pnl; |
416 | 434 | } |
417 | 435 | |
| 436 | /** |
| 437 | * Update the OK panel area |
| 438 | * @param newRelation What the new relation would "look" like if it were to be saved now |
| 439 | * @param okButton The OK button |
| 440 | * @param deleteButton The delete button |
| 441 | */ |
| 442 | private void updateOkPanel(IRelation<?> newRelation, JButton okButton, JButton deleteButton) { |
| 443 | okButton.setVisible(newRelation.isUseful() || this.getRelationSnapshot() == null); |
| 444 | deleteButton.setVisible(!newRelation.isUseful() && this.getRelationSnapshot() != null); |
| 445 | if (this.getRelationSnapshot() == null && !newRelation.isUseful()) { |
| 446 | okButton.setText(tr("Delete")); |
| 447 | } else { |
| 448 | okButton.setText(tr("OK")); |
| 449 | } |
| 450 | } |
| 451 | |
418 | 452 | /** |
419 | 453 | * builds the panel with the tag editor |
420 | 454 | * @param tagEditorPanel tag editor panel |
… |
… |
public class GenericRelationEditor extends RelationEditor implements CommandQueu
|
1001 | 1035 | @Override |
1002 | 1036 | public void mouseClicked(MouseEvent e) { |
1003 | 1037 | if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) { |
1004 | | new EditAction(new RelationEditorActionAccess()).actionPerformed(null); |
| 1038 | new EditAction(actionAccess).actionPerformed(null); |
1005 | 1039 | } |
1006 | 1040 | } |
1007 | 1041 | } |
diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/actions/ApplyAction.java b/src/org/openstreetmap/josm/gui/dialogs/relation/actions/ApplyAction.java
index 7ddfae63ac..0b65d27cb5 100644
a
|
b
|
public class ApplyAction extends SavingAction {
|
35 | 35 | |
36 | 36 | @Override |
37 | 37 | public void updateEnabledState() { |
38 | | setEnabled(isEditorDirty()); |
| 38 | setEnabled(this.editorAccess.getChangedRelation().isUseful() && isEditorDirty()); |
39 | 39 | } |
40 | 40 | } |
diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/actions/CancelAction.java b/src/org/openstreetmap/josm/gui/dialogs/relation/actions/CancelAction.java
index 6efdaeabdf..a51be40834 100644
a
|
b
|
import java.awt.event.ActionEvent;
|
8 | 8 | import javax.swing.JOptionPane; |
9 | 9 | import javax.swing.RootPaneContainer; |
10 | 10 | |
| 11 | import org.openstreetmap.josm.data.osm.IRelation; |
11 | 12 | import org.openstreetmap.josm.data.osm.Relation; |
12 | 13 | import org.openstreetmap.josm.gui.HelpAwareOptionPane; |
13 | 14 | import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec; |
… |
… |
public class CancelAction extends SavingAction {
|
47 | 48 | if ((!getMemberTableModel().hasSameMembersAs(snapshot) || getTagModel().isDirty()) |
48 | 49 | && !(snapshot == null && getTagModel().getTags().isEmpty())) { |
49 | 50 | //give the user a chance to save the changes |
50 | | int ret = confirmClosingByCancel(); |
| 51 | int ret = confirmClosingByCancel(this.editorAccess.getChangedRelation()); |
51 | 52 | if (ret == 0) { //Yes, save the changes |
52 | 53 | //copied from OKAction.run() |
53 | 54 | Config.getPref().put("relation.editor.generic.lastrole", Utils.strip(tfRole.getText())); |
… |
… |
public class CancelAction extends SavingAction {
|
60 | 61 | hideEditor(); |
61 | 62 | } |
62 | 63 | |
63 | | protected int confirmClosingByCancel() { |
| 64 | protected int confirmClosingByCancel(final IRelation<?> newRelation) { |
64 | 65 | ButtonSpec[] options = { |
65 | 66 | new ButtonSpec( |
66 | 67 | tr("Yes, save the changes and close"), |
… |
… |
public class CancelAction extends SavingAction {
|
82 | 83 | ) |
83 | 84 | }; |
84 | 85 | |
| 86 | // Keep users from saving invalid relations -- a relation MUST have at least a tag with the key "type" |
| 87 | // AND must contain at least one other OSM object. |
| 88 | options[0].setEnabled(newRelation.isUseful()); |
| 89 | |
85 | 90 | return HelpAwareOptionPane.showOptionDialog( |
86 | 91 | MainApplication.getMainFrame(), |
87 | 92 | tr("<html>The relation has been changed.<br><br>Do you want to save your changes?</html>"), |
diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/actions/IRelationEditorActionAccess.java b/src/org/openstreetmap/josm/gui/dialogs/relation/actions/IRelationEditorActionAccess.java
index cddedaf165..b1b79a3128 100644
a
|
b
|
package org.openstreetmap.josm.gui.dialogs.relation.actions;
|
3 | 3 | |
4 | 4 | import javax.swing.Action; |
5 | 5 | |
| 6 | import org.openstreetmap.josm.data.osm.IRelation; |
| 7 | import org.openstreetmap.josm.data.osm.Relation; |
6 | 8 | import org.openstreetmap.josm.gui.dialogs.relation.IRelationEditor; |
7 | 9 | import org.openstreetmap.josm.gui.dialogs.relation.MemberTable; |
8 | 10 | import org.openstreetmap.josm.gui.dialogs.relation.MemberTableModel; |
… |
… |
public interface IRelationEditorActionAccess {
|
65 | 67 | */ |
66 | 68 | TagEditorModel getTagModel(); |
67 | 69 | |
| 70 | /** |
| 71 | * Get the changed relation |
| 72 | * @return The changed relation (note: will not be part of a dataset). This should never be {@code null}. |
| 73 | * @since xxx |
| 74 | */ |
| 75 | default IRelation<?> getChangedRelation() { |
| 76 | final Relation newRelation; |
| 77 | if (getEditor().getRelation() != null) { |
| 78 | newRelation = new Relation(getEditor().getRelation()); |
| 79 | } else { |
| 80 | newRelation = new Relation(); |
| 81 | } |
| 82 | getTagModel().applyToPrimitive(newRelation); |
| 83 | getMemberTableModel().applyToRelation(newRelation); |
| 84 | return newRelation; |
| 85 | } |
| 86 | |
68 | 87 | /** |
69 | 88 | * Get the text field that is used to edit the role. |
70 | 89 | * @return The role text field. |
diff --git a/src/org/openstreetmap/josm/gui/dialogs/relation/actions/SavingAction.java b/src/org/openstreetmap/josm/gui/dialogs/relation/actions/SavingAction.java
index 26813f23c1..35ca1dcfb7 100644
a
|
b
|
abstract class SavingAction extends AbstractRelationEditorAction {
|
54 | 54 | tagEditorModel.applyToPrimitive(newRelation); |
55 | 55 | getMemberTableModel().applyToRelation(newRelation); |
56 | 56 | // If the user wanted to create a new relation, but hasn't added any members or |
57 | | // tags, don't add an empty relation |
58 | | if (newRelation.isEmpty() && !newRelation.hasKeys()) |
| 57 | // tags (specifically the "type" tag), don't add the relation |
| 58 | if (!newRelation.isUseful()) |
59 | 59 | return; |
60 | 60 | UndoRedoHandler.getInstance().add(new AddCommand(getLayer().getDataSet(), newRelation)); |
61 | 61 | |