source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/SavingAction.java@ 14027

Last change on this file since 14027 was 14027, checked in by michael2402, 6 years ago

See #16388: New mechanism for plugins to register relation editor actions.

File size: 8.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.util.ArrayList;
8import java.util.List;
9
10import javax.swing.JOptionPane;
11import javax.swing.SwingUtilities;
12
13import org.openstreetmap.josm.Main;
14import org.openstreetmap.josm.command.AddCommand;
15import org.openstreetmap.josm.command.ChangeCommand;
16import org.openstreetmap.josm.command.conflict.ConflictAddCommand;
17import org.openstreetmap.josm.data.conflict.Conflict;
18import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
19import org.openstreetmap.josm.data.osm.Relation;
20import org.openstreetmap.josm.data.osm.RelationMember;
21import org.openstreetmap.josm.gui.HelpAwareOptionPane;
22import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
23import org.openstreetmap.josm.gui.MainApplication;
24import org.openstreetmap.josm.gui.dialogs.relation.RelationDialogManager;
25import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
26import org.openstreetmap.josm.gui.tagging.TagEditorModel;
27import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
28import org.openstreetmap.josm.tools.ImageProvider;
29import org.openstreetmap.josm.tools.Utils;
30
31/**
32 * Abstract superclass of relation saving actions (OK, Apply, Cancel).
33 * @since 9496
34 */
35abstract class SavingAction extends AbstractRelationEditorAction {
36 private static final long serialVersionUID = 1L;
37
38 protected final AutoCompletingTextField tfRole;
39
40 protected SavingAction(IRelationEditorActionAccess editorAccess, IRelationEditorUpdateOn... updateOn) {
41 super(editorAccess, updateOn);
42 this.tfRole = editorAccess.getTextFieldRole();
43 }
44
45 /**
46 * apply updates to a new relation
47 * @param tagEditorModel tag editor model
48 */
49 protected void applyNewRelation(TagEditorModel tagEditorModel) {
50 final Relation newRelation = new Relation();
51 tagEditorModel.applyToPrimitive(newRelation);
52 getMemberTableModel().applyToRelation(newRelation);
53 List<RelationMember> newMembers = new ArrayList<>();
54 for (RelationMember rm: newRelation.getMembers()) {
55 if (!rm.getMember().isDeleted()) {
56 newMembers.add(rm);
57 }
58 }
59 if (newRelation.getMembersCount() != newMembers.size()) {
60 newRelation.setMembers(newMembers);
61 String msg = tr("One or more members of this new relation have been deleted while the relation editor\n" +
62 "was open. They have been removed from the relation members list.");
63 JOptionPane.showMessageDialog(Main.parent, msg, tr("Warning"), JOptionPane.WARNING_MESSAGE);
64 }
65 // If the user wanted to create a new relation, but hasn't added any members or
66 // tags, don't add an empty relation
67 if (newRelation.getMembersCount() == 0 && !newRelation.hasKeys())
68 return;
69 MainApplication.undoRedo.add(new AddCommand(getLayer().getDataSet(), newRelation));
70
71 // make sure everybody is notified about the changes
72 //
73 getEditor().setRelation(newRelation);
74 if (getEditor() instanceof RelationEditor) {
75 RelationDialogManager.getRelationDialogManager().updateContext(
76 getLayer(), getEditor().getRelation(), (RelationEditor) getEditor());
77 }
78 // Relation list gets update in EDT so selecting my be postponed to following EDT run
79 SwingUtilities.invokeLater(() -> MainApplication.getMap().relationListDialog.selectRelation(newRelation));
80 }
81
82 /**
83 * Apply the updates for an existing relation which has been changed outside of the relation editor.
84 * @param tagEditorModel tag editor model
85 */
86 protected void applyExistingConflictingRelation(TagEditorModel tagEditorModel) {
87 Relation editedRelation = new Relation(editorAccess.getEditor().getRelation());
88 tagEditorModel.applyToPrimitive(editedRelation);
89 editorAccess.getMemberTableModel().applyToRelation(editedRelation);
90 Conflict<Relation> conflict = new Conflict<>(editorAccess.getEditor().getRelation(), editedRelation);
91 MainApplication.undoRedo.add(new ConflictAddCommand(getLayer().getDataSet(), conflict));
92 }
93
94 /**
95 * Apply the updates for an existing relation which has not been changed outside of the relation editor.
96 * @param tagEditorModel tag editor model
97 */
98 protected void applyExistingNonConflictingRelation(TagEditorModel tagEditorModel) {
99 Relation originRelation = editorAccess.getEditor().getRelation();
100 Relation editedRelation = new Relation(originRelation);
101 tagEditorModel.applyToPrimitive(editedRelation);
102 getMemberTableModel().applyToRelation(editedRelation);
103 if (!editedRelation.hasEqualSemanticAttributes(originRelation, false)) {
104 MainApplication.undoRedo.add(new ChangeCommand(originRelation, editedRelation));
105 }
106 }
107
108 protected boolean confirmClosingBecauseOfDirtyState() {
109 ButtonSpec[] options = new ButtonSpec[] {
110 new ButtonSpec(
111 tr("Yes, create a conflict and close"),
112 new ImageProvider("ok"),
113 tr("Click to create a conflict and close this relation editor"),
114 null /* no specific help topic */
115 ),
116 new ButtonSpec(
117 tr("No, continue editing"),
118 new ImageProvider("cancel"),
119 tr("Click to return to the relation editor and to resume relation editing"),
120 null /* no specific help topic */
121 )
122 };
123
124 int ret = HelpAwareOptionPane.showOptionDialog(
125 Main.parent,
126 tr("<html>This relation has been changed outside of the editor.<br>"
127 + "You cannot apply your changes and continue editing.<br>"
128 + "<br>"
129 + "Do you want to create a conflict and close the editor?</html>"),
130 tr("Conflict in data"),
131 JOptionPane.WARNING_MESSAGE,
132 null,
133 options,
134 options[0], // OK is default
135 "/Dialog/RelationEditor#RelationChangedOutsideOfEditor"
136 );
137 if (ret == 0) {
138 MainApplication.getMap().conflictDialog.unfurlDialog();
139 }
140 return ret == 0;
141 }
142
143 protected void warnDoubleConflict() {
144 JOptionPane.showMessageDialog(
145 Main.parent,
146 tr("<html>Layer ''{0}'' already has a conflict for object<br>"
147 + "''{1}''.<br>"
148 + "Please resolve this conflict first, then try again.</html>",
149 Utils.escapeReservedCharactersHTML(getLayer().getName()),
150 Utils.escapeReservedCharactersHTML(getEditor().getRelation().getDisplayName(DefaultNameFormatter.getInstance()))
151 ),
152 tr("Double conflict"),
153 JOptionPane.WARNING_MESSAGE
154 );
155 }
156
157 @Override
158 protected void updateEnabledState() {
159 // Do nothing
160 }
161
162 protected boolean applyChanges() {
163 if (editorAccess.getEditor().getRelation() == null) {
164 applyNewRelation(getTagModel());
165 } else if (isEditorDirty()) {
166 if (editorAccess.getEditor().isDirtyRelation()) {
167 if (confirmClosingBecauseOfDirtyState()) {
168 if (getLayer().getConflicts().hasConflictForMy(editorAccess.getEditor().getRelation())) {
169 warnDoubleConflict();
170 return false;
171 }
172 applyExistingConflictingRelation(getTagModel());
173 hideEditor();
174 } else
175 return false;
176 } else {
177 applyExistingNonConflictingRelation(getTagModel());
178 }
179 }
180 editorAccess.getEditor().setRelation(editorAccess.getEditor().getRelation());
181 return true;
182 }
183
184 protected void hideEditor() {
185 if (editorAccess.getEditor() instanceof Component) {
186 ((Component) editorAccess.getEditor()).setVisible(false);
187 }
188 }
189
190 protected boolean isEditorDirty() {
191 Relation snapshot = editorAccess.getEditor().getRelationSnapshot();
192 return (snapshot != null && !getMemberTableModel().hasSameMembersAs(snapshot)) || getTagModel().isDirty();
193 }
194}
Note: See TracBrowser for help on using the repository browser.