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

Last change on this file since 12718 was 12718, checked in by Don-vip, 3 months ago

see #13036 - see #15229 - see #15182 - make Commands depends only on a DataSet, not a Layer. This removes a lot of GUI dependencies

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