source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/PropertiesMembershipChoiceDialog.java@ 16362

Last change on this file since 16362 was 16362, checked in by GerdP, 4 years ago

see #19111, #19105

  • don't ignore/reverse user input given in popup reg. tags/memberships
  • report correct number of affected relations (#19105)
  • when unglued (old) node is moved, place it either at the current mouse position (in cursor is in mapview) or 5 pixels above the original position. The old code sometimes placed it somewhere near the edge of the mapview window. The actual position depends on the input device that is used to select the node and to close the popup dialog (keyboard or mouse)
  • various code simplifications
  • Property svn:eol-style set to native
File size: 5.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.GridBagLayout;
7import java.util.Collection;
8import java.util.Objects;
9import java.util.Optional;
10
11import javax.swing.AbstractButton;
12import javax.swing.ButtonGroup;
13import javax.swing.JLabel;
14import javax.swing.JPanel;
15import javax.swing.JToggleButton;
16
17import org.openstreetmap.josm.data.osm.Node;
18import org.openstreetmap.josm.data.osm.Relation;
19import org.openstreetmap.josm.gui.ExtendedDialog;
20import org.openstreetmap.josm.gui.MainApplication;
21import org.openstreetmap.josm.tools.GBC;
22import org.openstreetmap.josm.tools.ImageProvider;
23import org.openstreetmap.josm.tools.UserCancelException;
24
25/**
26 * A dialog allowing the user decide whether the tags/memberships of the existing node should afterwards be at
27 * the existing node, the new nodes, or all of them.
28 * @since 14320 (extracted from UnglueAction)
29 */
30public final class PropertiesMembershipChoiceDialog extends ExtendedDialog {
31
32 private final transient ExistingBothNewChoice tags;
33 private final transient ExistingBothNewChoice memberships;
34
35 /**
36 * Represents the user choice: the existing node, the new nodes, or all of them
37 */
38 public enum ExistingBothNew {
39 OLD, BOTH, NEW;
40 }
41
42 /**
43 * Provides toggle buttons to allow the user choose the existing node, the new nodes, or all of them.
44 */
45 private static class ExistingBothNewChoice {
46 /** The "Existing node" button */
47 final AbstractButton oldNode = new JToggleButton(tr("Existing node"), ImageProvider.get("dialogs/conflict/tagkeeptheir"));
48 /** The "Both nodes" button */
49 final AbstractButton bothNodes = new JToggleButton(tr("Both nodes"), ImageProvider.get("dialogs/conflict/tagundecide"));
50 /** The "New node" button */
51 final AbstractButton newNode = new JToggleButton(tr("New node"), ImageProvider.get("dialogs/conflict/tagkeepmine"));
52
53 ExistingBothNewChoice(final boolean preselectNew) {
54 final ButtonGroup tagsGroup = new ButtonGroup();
55 tagsGroup.add(oldNode);
56 tagsGroup.add(bothNodes);
57 tagsGroup.add(newNode);
58 tagsGroup.setSelected((preselectNew ? newNode : oldNode).getModel(), true);
59 }
60
61 void add(JPanel content, int gridy) {
62 content.add(oldNode, GBC.std(1, gridy));
63 content.add(bothNodes, GBC.std(2, gridy));
64 content.add(newNode, GBC.std(3, gridy));
65 }
66
67 ExistingBothNew getSelected() {
68 if (oldNode.isSelected()) {
69 return ExistingBothNew.OLD;
70 } else if (bothNodes.isSelected()) {
71 return ExistingBothNew.BOTH;
72 } else if (newNode.isSelected()) {
73 return ExistingBothNew.NEW;
74 } else {
75 throw new IllegalStateException();
76 }
77 }
78 }
79
80 private PropertiesMembershipChoiceDialog(boolean preselectNew, boolean queryTags, boolean queryMemberships) {
81 super(MainApplication.getMainFrame(), tr("Tags/Memberships"), tr("Unglue"), tr("Cancel"));
82 setButtonIcons("unglueways", "cancel");
83
84 final JPanel content = new JPanel(new GridBagLayout());
85
86 if (queryTags) {
87 content.add(new JLabel(tr("Where should the tags of the node be put?")), GBC.std(1, 1).span(3).insets(0, 20, 0, 0));
88 tags = new ExistingBothNewChoice(preselectNew);
89 tags.add(content, 2);
90 } else {
91 tags = null;
92 }
93
94 if (queryMemberships) {
95 content.add(new JLabel(tr("Where should the memberships of this node be put?")), GBC.std(1, 3).span(3).insets(0, 20, 0, 0));
96 memberships = new ExistingBothNewChoice(preselectNew);
97 memberships.add(content, 4);
98 } else {
99 memberships = null;
100 }
101
102 setContent(content);
103 setResizable(false);
104 }
105
106 /**
107 * Returns the tags choice.
108 * @return the tags choice
109 */
110 public Optional<ExistingBothNew> getTags() {
111 return Optional.ofNullable(tags).map(ExistingBothNewChoice::getSelected);
112 }
113
114 /**
115 * Returns the memberships choice.
116 * @return the memberships choice
117 */
118 public Optional<ExistingBothNew> getMemberships() {
119 return Optional.ofNullable(memberships).map(ExistingBothNewChoice::getSelected);
120 }
121
122 /**
123 * Creates and shows a new {@code PropertiesMembershipChoiceDialog} if necessary. Otherwise does nothing.
124 * @param selectedNodes selected nodes
125 * @param preselectNew if {@code true}, pre-select "new node" as default choice
126 * @return A new {@code PropertiesMembershipChoiceDialog} that has been shown to user, or {@code null}
127 * @throws UserCancelException if user cancels choice
128 */
129 public static PropertiesMembershipChoiceDialog showIfNecessary(Collection<Node> selectedNodes, boolean preselectNew)
130 throws UserCancelException {
131 final boolean queryTags = isTagged(selectedNodes);
132 final boolean queryMemberships = isUsedInRelations(selectedNodes);
133 if (queryTags || queryMemberships) {
134 final PropertiesMembershipChoiceDialog dialog = new PropertiesMembershipChoiceDialog(preselectNew, queryTags, queryMemberships);
135 dialog.showDialog();
136 if (dialog.getValue() != 1) {
137 throw new UserCancelException();
138 }
139 return dialog;
140 }
141 return null;
142 }
143
144 private static boolean isTagged(final Collection<Node> existingNodes) {
145 return existingNodes.stream().anyMatch(Node::hasKeys);
146 }
147
148 private static boolean isUsedInRelations(final Collection<Node> existingNodes) {
149 return existingNodes.stream().anyMatch(
150 selectedNode -> selectedNode.referrers(Relation.class).anyMatch(Objects::nonNull));
151 }
152}
Note: See TracBrowser for help on using the repository browser.