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

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

see #11924 - Java 9 - replace calls to deprecated classes java.util.Observable / java.util.Observer by a new class ChangeNotifier + swing's ChangeListener

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