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

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

Accessibility - global use of JLabel.setLabelFor() + various fixes (javadoc, code style)

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