source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMerger.java@ 12663

Last change on this file since 12663 was 12663, checked in by Don-vip, 7 years ago

see #15182 - move NameFormatter* from gui to data.osm

  • Property svn:eol-style set to native
File size: 17.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.pair.properties;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.text.DecimalFormat;
8import java.util.Arrays;
9import java.util.List;
10
11import javax.swing.AbstractAction;
12import javax.swing.Action;
13import javax.swing.BorderFactory;
14import javax.swing.JButton;
15import javax.swing.JComponent;
16import javax.swing.JLabel;
17import javax.swing.JPanel;
18import javax.swing.event.ChangeEvent;
19import javax.swing.event.ChangeListener;
20
21import org.openstreetmap.josm.data.conflict.Conflict;
22import org.openstreetmap.josm.data.coor.LatLon;
23import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
24import org.openstreetmap.josm.data.osm.OsmPrimitive;
25import org.openstreetmap.josm.gui.conflict.ConflictColors;
26import org.openstreetmap.josm.gui.conflict.pair.AbstractMergePanel;
27import org.openstreetmap.josm.gui.conflict.pair.IConflictResolver;
28import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
29import org.openstreetmap.josm.gui.history.VersionInfoPanel;
30import org.openstreetmap.josm.tools.GBC;
31import org.openstreetmap.josm.tools.ImageProvider;
32import org.openstreetmap.josm.tools.Utils;
33
34/**
35 * This class represents a UI component for resolving conflicts in some properties of {@link OsmPrimitive}.
36 * @since 1654
37 */
38public class PropertiesMerger extends AbstractMergePanel implements ChangeListener, IConflictResolver {
39 private static final DecimalFormat COORD_FORMATTER = new DecimalFormat("###0.0000000");
40
41 private final JLabel lblMyCoordinates = buildValueLabel("label.mycoordinates");
42 private final JLabel lblMergedCoordinates = buildValueLabel("label.mergedcoordinates");
43 private final JLabel lblTheirCoordinates = buildValueLabel("label.theircoordinates");
44
45 private final JLabel lblMyDeletedState = buildValueLabel("label.mydeletedstate");
46 private final JLabel lblMergedDeletedState = buildValueLabel("label.mergeddeletedstate");
47 private final JLabel lblTheirDeletedState = buildValueLabel("label.theirdeletedstate");
48
49 private final JLabel lblMyReferrers = buildValueLabel("label.myreferrers");
50 private final JLabel lblTheirReferrers = buildValueLabel("label.theirreferrers");
51
52 private final transient PropertiesMergeModel model = new PropertiesMergeModel();
53 private final VersionInfoPanel mineVersionInfo = new VersionInfoPanel();
54 private final VersionInfoPanel theirVersionInfo = new VersionInfoPanel();
55
56 /**
57 * Constructs a new {@code PropertiesMerger}.
58 */
59 public PropertiesMerger() {
60 model.addChangeListener(this);
61 buildRows();
62 }
63
64 @Override
65 protected List<? extends MergeRow> getRows() {
66 return Arrays.asList(
67 new AbstractMergePanel.TitleRow(),
68 new VersionInfoRow(),
69 new MergeCoordinatesRow(),
70 new UndecideCoordinatesRow(),
71 new MergeDeletedStateRow(),
72 new UndecideDeletedStateRow(),
73 new ReferrersRow(),
74 new EmptyFillRow());
75 }
76
77 protected static JLabel buildValueLabel(String name) {
78 JLabel lbl = new JLabel();
79 lbl.setName(name);
80 lbl.setHorizontalAlignment(JLabel.CENTER);
81 lbl.setOpaque(true);
82 lbl.setBorder(BorderFactory.createLoweredBevelBorder());
83 return lbl;
84 }
85
86 protected static String coordToString(LatLon coord) {
87 if (coord == null)
88 return tr("(none)");
89 StringBuilder sb = new StringBuilder();
90 sb.append('(')
91 .append(COORD_FORMATTER.format(coord.lat()))
92 .append(',')
93 .append(COORD_FORMATTER.format(coord.lon()))
94 .append(')');
95 return sb.toString();
96 }
97
98 protected static String deletedStateToString(Boolean deleted) {
99 if (deleted == null)
100 return tr("(none)");
101 if (deleted)
102 return tr("deleted");
103 else
104 return tr("not deleted");
105 }
106
107 protected static String referrersToString(List<OsmPrimitive> referrers) {
108 if (referrers.isEmpty())
109 return tr("(none)");
110 StringBuilder str = new StringBuilder("<html>");
111 for (OsmPrimitive r: referrers) {
112 str.append(Utils.escapeReservedCharactersHTML(r.getDisplayName(DefaultNameFormatter.getInstance()))).append("<br>");
113 }
114 str.append("</html>");
115 return str.toString();
116 }
117
118 protected void updateCoordinates() {
119 lblMyCoordinates.setText(coordToString(model.getMyCoords()));
120 lblMergedCoordinates.setText(coordToString(model.getMergedCoords()));
121 lblTheirCoordinates.setText(coordToString(model.getTheirCoords()));
122 if (!model.hasCoordConflict()) {
123 lblMyCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
124 lblMergedCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
125 lblTheirCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
126 } else {
127 if (!model.isDecidedCoord()) {
128 lblMyCoordinates.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
129 lblMergedCoordinates.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
130 lblTheirCoordinates.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
131 } else {
132 lblMyCoordinates.setBackground(
133 model.isCoordMergeDecision(MergeDecisionType.KEEP_MINE)
134 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
135 );
136 lblMergedCoordinates.setBackground(ConflictColors.BGCOLOR_DECIDED.get());
137 lblTheirCoordinates.setBackground(
138 model.isCoordMergeDecision(MergeDecisionType.KEEP_THEIR)
139 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
140 );
141 }
142 }
143 }
144
145 protected void updateDeletedState() {
146 lblMyDeletedState.setText(deletedStateToString(model.getMyDeletedState()));
147 lblMergedDeletedState.setText(deletedStateToString(model.getMergedDeletedState()));
148 lblTheirDeletedState.setText(deletedStateToString(model.getTheirDeletedState()));
149
150 if (!model.hasDeletedStateConflict()) {
151 lblMyDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
152 lblMergedDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
153 lblTheirDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
154 } else {
155 if (!model.isDecidedDeletedState()) {
156 lblMyDeletedState.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
157 lblMergedDeletedState.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
158 lblTheirDeletedState.setBackground(ConflictColors.BGCOLOR_UNDECIDED.get());
159 } else {
160 lblMyDeletedState.setBackground(
161 model.isDeletedStateDecision(MergeDecisionType.KEEP_MINE)
162 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
163 );
164 lblMergedDeletedState.setBackground(ConflictColors.BGCOLOR_DECIDED.get());
165 lblTheirDeletedState.setBackground(
166 model.isDeletedStateDecision(MergeDecisionType.KEEP_THEIR)
167 ? ConflictColors.BGCOLOR_DECIDED.get() : ConflictColors.BGCOLOR_NO_CONFLICT.get()
168 );
169 }
170 }
171 }
172
173 protected void updateReferrers() {
174 lblMyReferrers.setText(referrersToString(model.getMyReferrers()));
175 lblMyReferrers.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
176 lblTheirReferrers.setText(referrersToString(model.getTheirReferrers()));
177 lblTheirReferrers.setBackground(ConflictColors.BGCOLOR_NO_CONFLICT.get());
178 }
179
180 @Override
181 public void stateChanged(ChangeEvent e) {
182 updateCoordinates();
183 updateDeletedState();
184 updateReferrers();
185 }
186
187 /**
188 * Returns properties merge model.
189 * @return properties merge model
190 */
191 public PropertiesMergeModel getModel() {
192 return model;
193 }
194
195 private final class MergeDeletedStateRow extends AbstractMergePanel.MergeRow {
196 @Override
197 protected JComponent rowTitle() {
198 return new JLabel(tr("Deleted State:"));
199 }
200
201 @Override
202 protected JComponent mineField() {
203 return lblMyDeletedState;
204 }
205
206 @Override
207 protected JComponent mineButton() {
208 KeepMyDeletedStateAction actKeepMyDeletedState = new KeepMyDeletedStateAction();
209 model.addChangeListener(actKeepMyDeletedState);
210 JButton btnKeepMyDeletedState = new JButton(actKeepMyDeletedState);
211 btnKeepMyDeletedState.setName("button.keepmydeletedstate");
212 return btnKeepMyDeletedState;
213 }
214
215 @Override
216 protected JComponent merged() {
217 return lblMergedDeletedState;
218 }
219
220 @Override
221 protected JComponent theirsButton() {
222 KeepTheirDeletedStateAction actKeepTheirDeletedState = new KeepTheirDeletedStateAction();
223 model.addChangeListener(actKeepTheirDeletedState);
224 JButton btnKeepTheirDeletedState = new JButton(actKeepTheirDeletedState);
225 btnKeepTheirDeletedState.setName("button.keeptheirdeletedstate");
226 return btnKeepTheirDeletedState;
227 }
228
229 @Override
230 protected JComponent theirsField() {
231 return lblTheirDeletedState;
232 }
233 }
234
235 private final class MergeCoordinatesRow extends AbstractMergePanel.MergeRow {
236 @Override
237 protected JComponent rowTitle() {
238 return new JLabel(tr("Coordinates:"));
239 }
240
241 @Override
242 protected JComponent mineField() {
243 return lblMyCoordinates;
244 }
245
246 @Override
247 protected JComponent mineButton() {
248 KeepMyCoordinatesAction actKeepMyCoordinates = new KeepMyCoordinatesAction();
249 model.addChangeListener(actKeepMyCoordinates);
250 JButton btnKeepMyCoordinates = new JButton(actKeepMyCoordinates);
251 btnKeepMyCoordinates.setName("button.keepmycoordinates");
252 return btnKeepMyCoordinates;
253 }
254
255 @Override
256 protected JComponent merged() {
257 return lblMergedCoordinates;
258 }
259
260 @Override
261 protected JComponent theirsButton() {
262 KeepTheirCoordinatesAction actKeepTheirCoordinates = new KeepTheirCoordinatesAction();
263 model.addChangeListener(actKeepTheirCoordinates);
264 JButton btnKeepTheirCoordinates = new JButton(actKeepTheirCoordinates);
265 btnKeepTheirCoordinates.setName("button.keeptheircoordinates");
266 return btnKeepTheirCoordinates;
267 }
268
269 @Override
270 protected JComponent theirsField() {
271 return lblTheirCoordinates;
272 }
273 }
274
275 private final class UndecideCoordinatesRow extends AbstractUndecideRow {
276 @Override
277 protected UndecideCoordinateConflictAction createAction() {
278 UndecideCoordinateConflictAction action = new UndecideCoordinateConflictAction();
279 model.addChangeListener(action);
280 return action;
281 }
282
283 @Override
284 protected String getButtonName() {
285 return "button.undecidecoordinates";
286 }
287 }
288
289 private final class UndecideDeletedStateRow extends AbstractUndecideRow {
290 @Override
291 protected UndecideDeletedStateConflictAction createAction() {
292 UndecideDeletedStateConflictAction action = new UndecideDeletedStateConflictAction();
293 model.addChangeListener(action);
294 return action;
295 }
296
297 @Override
298 protected String getButtonName() {
299 return "button.undecidedeletedstate";
300 }
301 }
302
303 private final class VersionInfoRow extends AbstractMergePanel.MergeRowWithoutButton {
304 @Override
305 protected JComponent mineField() {
306 return mineVersionInfo;
307 }
308
309 @Override
310 protected JComponent theirsField() {
311 return theirVersionInfo;
312 }
313 }
314
315 private final class ReferrersRow extends AbstractMergePanel.MergeRow {
316 @Override
317 protected JComponent rowTitle() {
318 return new JLabel(tr("Referenced by:"));
319 }
320
321 @Override
322 protected JComponent mineField() {
323 return lblMyReferrers;
324 }
325
326 @Override
327 protected JComponent theirsField() {
328 return lblTheirReferrers;
329 }
330 }
331
332 private static final class EmptyFillRow extends AbstractMergePanel.MergeRow {
333 @Override
334 protected JComponent merged() {
335 return new JPanel();
336 }
337
338 @Override
339 protected void addConstraints(GBC constraints, int columnIndex) {
340 super.addConstraints(constraints, columnIndex);
341 // fill to bottom
342 constraints.weighty = 1;
343 }
344 }
345
346 class KeepMyCoordinatesAction extends AbstractAction implements ChangeListener {
347 KeepMyCoordinatesAction() {
348 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine"));
349 putValue(Action.SHORT_DESCRIPTION, tr("Keep my coordinates"));
350 }
351
352 @Override
353 public void actionPerformed(ActionEvent e) {
354 model.decideCoordsConflict(MergeDecisionType.KEEP_MINE);
355 }
356
357 @Override
358 public void stateChanged(ChangeEvent e) {
359 setEnabled(model.hasCoordConflict() && !model.isDecidedCoord() && model.getMyCoords() != null);
360 }
361 }
362
363 class KeepTheirCoordinatesAction extends AbstractAction implements ChangeListener {
364 KeepTheirCoordinatesAction() {
365 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir"));
366 putValue(Action.SHORT_DESCRIPTION, tr("Keep their coordinates"));
367 }
368
369 @Override
370 public void actionPerformed(ActionEvent e) {
371 model.decideCoordsConflict(MergeDecisionType.KEEP_THEIR);
372 }
373
374 @Override
375 public void stateChanged(ChangeEvent e) {
376 setEnabled(model.hasCoordConflict() && !model.isDecidedCoord() && model.getTheirCoords() != null);
377 }
378 }
379
380 class UndecideCoordinateConflictAction extends AbstractAction implements ChangeListener {
381 UndecideCoordinateConflictAction() {
382 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide"));
383 putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between different coordinates"));
384 }
385
386 @Override
387 public void actionPerformed(ActionEvent e) {
388 model.decideCoordsConflict(MergeDecisionType.UNDECIDED);
389 }
390
391 @Override
392 public void stateChanged(ChangeEvent e) {
393 setEnabled(model.hasCoordConflict() && model.isDecidedCoord());
394 }
395 }
396
397 class KeepMyDeletedStateAction extends AbstractAction implements ChangeListener {
398 KeepMyDeletedStateAction() {
399 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine"));
400 putValue(Action.SHORT_DESCRIPTION, tr("Keep my deleted state"));
401 }
402
403 @Override
404 public void actionPerformed(ActionEvent e) {
405 model.decideDeletedStateConflict(MergeDecisionType.KEEP_MINE);
406 }
407
408 @Override
409 public void stateChanged(ChangeEvent e) {
410 setEnabled(model.hasDeletedStateConflict() && !model.isDecidedDeletedState());
411 }
412 }
413
414 class KeepTheirDeletedStateAction extends AbstractAction implements ChangeListener {
415 KeepTheirDeletedStateAction() {
416 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir"));
417 putValue(Action.SHORT_DESCRIPTION, tr("Keep their deleted state"));
418 }
419
420 @Override
421 public void actionPerformed(ActionEvent e) {
422 model.decideDeletedStateConflict(MergeDecisionType.KEEP_THEIR);
423 }
424
425 @Override
426 public void stateChanged(ChangeEvent e) {
427 setEnabled(model.hasDeletedStateConflict() && !model.isDecidedDeletedState());
428 }
429 }
430
431 class UndecideDeletedStateConflictAction extends AbstractAction implements ChangeListener {
432 UndecideDeletedStateConflictAction() {
433 putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide"));
434 putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between deleted state"));
435 }
436
437 @Override
438 public void actionPerformed(ActionEvent e) {
439 model.decideDeletedStateConflict(MergeDecisionType.UNDECIDED);
440 }
441
442 @Override
443 public void stateChanged(ChangeEvent e) {
444 setEnabled(model.hasDeletedStateConflict() && model.isDecidedDeletedState());
445 }
446 }
447
448 @Override
449 public void deletePrimitive(boolean deleted) {
450 if (deleted) {
451 if (model.getMergedCoords() == null) {
452 model.decideCoordsConflict(MergeDecisionType.KEEP_MINE);
453 }
454 } else {
455 model.decideCoordsConflict(MergeDecisionType.UNDECIDED);
456 }
457 }
458
459 @Override
460 public void populate(Conflict<? extends OsmPrimitive> conflict) {
461 model.populate(conflict);
462 mineVersionInfo.update(conflict.getMy(), true);
463 theirVersionInfo.update(conflict.getTheir(), false);
464 }
465
466 @Override
467 public void decideRemaining(MergeDecisionType decision) {
468 if (!model.isDecidedCoord()) {
469 model.decideDeletedStateConflict(decision);
470 }
471 if (!model.isDecidedCoord()) {
472 model.decideCoordsConflict(decision);
473 }
474 }
475}
Note: See TracBrowser for help on using the repository browser.