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, 3 months 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.