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

Last change on this file since 9468 was 9468, checked in by simon04, 8 years ago

fix #8804 - Add version info to conflict dialog

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