source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/pair/ListMerger.java@ 9990

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

sonar - Inner classes which do not reference their owning classes should be static

  • Property svn:eol-style set to native
File size: 32.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.pair;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.FlowLayout;
8import java.awt.GridBagConstraints;
9import java.awt.GridBagLayout;
10import java.awt.Insets;
11import java.awt.event.ActionEvent;
12import java.awt.event.ItemEvent;
13import java.awt.event.ItemListener;
14import java.beans.PropertyChangeEvent;
15import java.beans.PropertyChangeListener;
16import java.util.Collection;
17import java.util.Observable;
18import java.util.Observer;
19
20import javax.swing.AbstractAction;
21import javax.swing.Action;
22import javax.swing.ImageIcon;
23import javax.swing.JButton;
24import javax.swing.JCheckBox;
25import javax.swing.JLabel;
26import javax.swing.JPanel;
27import javax.swing.JScrollPane;
28import javax.swing.JTable;
29import javax.swing.JToggleButton;
30import javax.swing.event.ListSelectionEvent;
31import javax.swing.event.ListSelectionListener;
32
33import org.openstreetmap.josm.Main;
34import org.openstreetmap.josm.data.osm.OsmPrimitive;
35import org.openstreetmap.josm.data.osm.PrimitiveId;
36import org.openstreetmap.josm.data.osm.Relation;
37import org.openstreetmap.josm.data.osm.Way;
38import org.openstreetmap.josm.gui.layer.OsmDataLayer;
39import org.openstreetmap.josm.gui.util.AdjustmentSynchronizer;
40import org.openstreetmap.josm.gui.widgets.JosmComboBox;
41import org.openstreetmap.josm.gui.widgets.OsmPrimitivesTable;
42import org.openstreetmap.josm.tools.ImageProvider;
43
44/**
45 * A UI component for resolving conflicts in two lists of entries of type T.
46 *
47 * @param <T> the type of the entries
48 * @see ListMergeModel
49 * @since 1631
50 */
51public abstract class ListMerger<T extends PrimitiveId> extends JPanel implements PropertyChangeListener, Observer {
52 protected OsmPrimitivesTable myEntriesTable;
53 protected OsmPrimitivesTable mergedEntriesTable;
54 protected OsmPrimitivesTable theirEntriesTable;
55
56 protected transient ListMergeModel<T> model;
57
58 private CopyStartLeftAction copyStartLeftAction;
59 private CopyBeforeCurrentLeftAction copyBeforeCurrentLeftAction;
60 private CopyAfterCurrentLeftAction copyAfterCurrentLeftAction;
61 private CopyEndLeftAction copyEndLeftAction;
62 private CopyAllLeft copyAllLeft;
63
64 private CopyStartRightAction copyStartRightAction;
65 private CopyBeforeCurrentRightAction copyBeforeCurrentRightAction;
66 private CopyAfterCurrentRightAction copyAfterCurrentRightAction;
67 private CopyEndRightAction copyEndRightAction;
68 private CopyAllRight copyAllRight;
69
70 private MoveUpMergedAction moveUpMergedAction;
71 private MoveDownMergedAction moveDownMergedAction;
72 private RemoveMergedAction removeMergedAction;
73 private FreezeAction freezeAction;
74
75 private transient AdjustmentSynchronizer adjustmentSynchronizer;
76
77 private JLabel lblMyVersion;
78 private JLabel lblMergedVersion;
79 private JLabel lblTheirVersion;
80
81 private JLabel lblFrozenState;
82
83 protected abstract JScrollPane buildMyElementsTable();
84
85 protected abstract JScrollPane buildMergedElementsTable();
86
87 protected abstract JScrollPane buildTheirElementsTable();
88
89 protected JScrollPane embeddInScrollPane(JTable table) {
90 JScrollPane pane = new JScrollPane(table);
91 if (adjustmentSynchronizer == null) {
92 adjustmentSynchronizer = new AdjustmentSynchronizer();
93 }
94 return pane;
95 }
96
97 protected void wireActionsToSelectionModels() {
98 myEntriesTable.getSelectionModel().addListSelectionListener(copyStartLeftAction);
99
100 myEntriesTable.getSelectionModel().addListSelectionListener(copyBeforeCurrentLeftAction);
101 mergedEntriesTable.getSelectionModel().addListSelectionListener(copyBeforeCurrentLeftAction);
102
103 myEntriesTable.getSelectionModel().addListSelectionListener(copyAfterCurrentLeftAction);
104 mergedEntriesTable.getSelectionModel().addListSelectionListener(copyAfterCurrentLeftAction);
105
106 myEntriesTable.getSelectionModel().addListSelectionListener(copyEndLeftAction);
107
108 theirEntriesTable.getSelectionModel().addListSelectionListener(copyStartRightAction);
109
110 theirEntriesTable.getSelectionModel().addListSelectionListener(copyBeforeCurrentRightAction);
111 mergedEntriesTable.getSelectionModel().addListSelectionListener(copyBeforeCurrentRightAction);
112
113 theirEntriesTable.getSelectionModel().addListSelectionListener(copyAfterCurrentRightAction);
114 mergedEntriesTable.getSelectionModel().addListSelectionListener(copyAfterCurrentRightAction);
115
116 theirEntriesTable.getSelectionModel().addListSelectionListener(copyEndRightAction);
117
118 mergedEntriesTable.getSelectionModel().addListSelectionListener(moveUpMergedAction);
119 mergedEntriesTable.getSelectionModel().addListSelectionListener(moveDownMergedAction);
120 mergedEntriesTable.getSelectionModel().addListSelectionListener(removeMergedAction);
121
122 model.addObserver(copyAllLeft);
123 model.addObserver(copyAllRight);
124 model.addPropertyChangeListener(copyAllLeft);
125 model.addPropertyChangeListener(copyAllRight);
126 }
127
128 protected JPanel buildLeftButtonPanel() {
129 JPanel pnl = new JPanel(new GridBagLayout());
130 GridBagConstraints gc = new GridBagConstraints();
131
132 gc.gridx = 0;
133 gc.gridy = 0;
134 copyStartLeftAction = new CopyStartLeftAction();
135 JButton btn = new JButton(copyStartLeftAction);
136 btn.setName("button.copystartleft");
137 pnl.add(btn, gc);
138
139 gc.gridx = 0;
140 gc.gridy = 1;
141 copyBeforeCurrentLeftAction = new CopyBeforeCurrentLeftAction();
142 btn = new JButton(copyBeforeCurrentLeftAction);
143 btn.setName("button.copybeforecurrentleft");
144 pnl.add(btn, gc);
145
146 gc.gridx = 0;
147 gc.gridy = 2;
148 copyAfterCurrentLeftAction = new CopyAfterCurrentLeftAction();
149 btn = new JButton(copyAfterCurrentLeftAction);
150 btn.setName("button.copyaftercurrentleft");
151 pnl.add(btn, gc);
152
153 gc.gridx = 0;
154 gc.gridy = 3;
155 copyEndLeftAction = new CopyEndLeftAction();
156 btn = new JButton(copyEndLeftAction);
157 btn.setName("button.copyendleft");
158 pnl.add(btn, gc);
159
160 gc.gridx = 0;
161 gc.gridy = 4;
162 copyAllLeft = new CopyAllLeft();
163 btn = new JButton(copyAllLeft);
164 btn.setName("button.copyallleft");
165 pnl.add(btn, gc);
166
167 return pnl;
168 }
169
170 protected JPanel buildRightButtonPanel() {
171 JPanel pnl = new JPanel(new GridBagLayout());
172 GridBagConstraints gc = new GridBagConstraints();
173
174 gc.gridx = 0;
175 gc.gridy = 0;
176 copyStartRightAction = new CopyStartRightAction();
177 pnl.add(new JButton(copyStartRightAction), gc);
178
179 gc.gridx = 0;
180 gc.gridy = 1;
181 copyBeforeCurrentRightAction = new CopyBeforeCurrentRightAction();
182 pnl.add(new JButton(copyBeforeCurrentRightAction), gc);
183
184 gc.gridx = 0;
185 gc.gridy = 2;
186 copyAfterCurrentRightAction = new CopyAfterCurrentRightAction();
187 pnl.add(new JButton(copyAfterCurrentRightAction), gc);
188
189 gc.gridx = 0;
190 gc.gridy = 3;
191 copyEndRightAction = new CopyEndRightAction();
192 pnl.add(new JButton(copyEndRightAction), gc);
193
194 gc.gridx = 0;
195 gc.gridy = 4;
196 copyAllRight = new CopyAllRight();
197 pnl.add(new JButton(copyAllRight), gc);
198
199 return pnl;
200 }
201
202 protected JPanel buildMergedListControlButtons() {
203 JPanel pnl = new JPanel(new GridBagLayout());
204 GridBagConstraints gc = new GridBagConstraints();
205
206 gc.gridx = 0;
207 gc.gridy = 0;
208 gc.gridwidth = 1;
209 gc.gridheight = 1;
210 gc.fill = GridBagConstraints.HORIZONTAL;
211 gc.anchor = GridBagConstraints.CENTER;
212 gc.weightx = 0.3;
213 gc.weighty = 0.0;
214 moveUpMergedAction = new MoveUpMergedAction();
215 pnl.add(new JButton(moveUpMergedAction), gc);
216
217 gc.gridx = 1;
218 gc.gridy = 0;
219 moveDownMergedAction = new MoveDownMergedAction();
220 pnl.add(new JButton(moveDownMergedAction), gc);
221
222 gc.gridx = 2;
223 gc.gridy = 0;
224 removeMergedAction = new RemoveMergedAction();
225 pnl.add(new JButton(removeMergedAction), gc);
226
227 return pnl;
228 }
229
230 protected JPanel buildAdjustmentLockControlPanel(JCheckBox cb) {
231 JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
232 panel.add(new JLabel(tr("lock scrolling")));
233 panel.add(cb);
234 return panel;
235 }
236
237 protected JPanel buildComparePairSelectionPanel() {
238 JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
239 p.add(new JLabel(tr("Compare ")));
240 JosmComboBox<ComparePairType> cbComparePair = new JosmComboBox<>(model.getComparePairListModel());
241 cbComparePair.setRenderer(new ComparePairListCellRenderer());
242 p.add(cbComparePair);
243 return p;
244 }
245
246 protected JPanel buildFrozeStateControlPanel() {
247 JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
248 lblFrozenState = new JLabel();
249 p.add(lblFrozenState);
250 freezeAction = new FreezeAction();
251 JToggleButton btn = new JToggleButton(freezeAction);
252 freezeAction.adapt(btn);
253 btn.setName("button.freeze");
254 p.add(btn);
255
256 return p;
257 }
258
259 protected final void build() {
260 setLayout(new GridBagLayout());
261 GridBagConstraints gc = new GridBagConstraints();
262
263 // ------------------
264 gc.gridx = 0;
265 gc.gridy = 0;
266 gc.gridwidth = 1;
267 gc.gridheight = 1;
268 gc.fill = GridBagConstraints.NONE;
269 gc.anchor = GridBagConstraints.CENTER;
270 gc.weightx = 0.0;
271 gc.weighty = 0.0;
272 gc.insets = new Insets(10, 0, 0, 0);
273 lblMyVersion = new JLabel(tr("My version"));
274 lblMyVersion.setToolTipText(tr("List of elements in my dataset, i.e. the local dataset"));
275 add(lblMyVersion, gc);
276
277 gc.gridx = 2;
278 gc.gridy = 0;
279 lblMergedVersion = new JLabel(tr("Merged version"));
280 lblMergedVersion.setToolTipText(
281 tr("List of merged elements. They will replace the list of my elements when the merge decisions are applied."));
282 add(lblMergedVersion, gc);
283
284 gc.gridx = 4;
285 gc.gridy = 0;
286 lblTheirVersion = new JLabel(tr("Their version"));
287 lblTheirVersion.setToolTipText(tr("List of elements in their dataset, i.e. the server dataset"));
288 add(lblTheirVersion, gc);
289
290 // ------------------------------
291 gc.gridx = 0;
292 gc.gridy = 1;
293 gc.gridwidth = 1;
294 gc.gridheight = 1;
295 gc.fill = GridBagConstraints.HORIZONTAL;
296 gc.anchor = GridBagConstraints.FIRST_LINE_START;
297 gc.weightx = 0.33;
298 gc.weighty = 0.0;
299 gc.insets = new Insets(0, 0, 0, 0);
300 JCheckBox cbLockMyScrolling = new JCheckBox();
301 cbLockMyScrolling.setName("checkbox.lockmyscrolling");
302 add(buildAdjustmentLockControlPanel(cbLockMyScrolling), gc);
303
304 gc.gridx = 2;
305 gc.gridy = 1;
306 JCheckBox cbLockMergedScrolling = new JCheckBox();
307 cbLockMergedScrolling.setName("checkbox.lockmergedscrolling");
308 add(buildAdjustmentLockControlPanel(cbLockMergedScrolling), gc);
309
310 gc.gridx = 4;
311 gc.gridy = 1;
312 JCheckBox cbLockTheirScrolling = new JCheckBox();
313 cbLockTheirScrolling.setName("checkbox.locktheirscrolling");
314 add(buildAdjustmentLockControlPanel(cbLockTheirScrolling), gc);
315
316 // --------------------------------
317 gc.gridx = 0;
318 gc.gridy = 2;
319 gc.gridwidth = 1;
320 gc.gridheight = 1;
321 gc.fill = GridBagConstraints.BOTH;
322 gc.anchor = GridBagConstraints.FIRST_LINE_START;
323 gc.weightx = 0.33;
324 gc.weighty = 1.0;
325 gc.insets = new Insets(0, 0, 0, 0);
326 JScrollPane pane = buildMyElementsTable();
327 lblMyVersion.setLabelFor(pane);
328 adjustmentSynchronizer.adapt(cbLockMyScrolling, pane.getVerticalScrollBar());
329 add(pane, gc);
330
331 gc.gridx = 1;
332 gc.gridy = 2;
333 gc.fill = GridBagConstraints.NONE;
334 gc.anchor = GridBagConstraints.CENTER;
335 gc.weightx = 0.0;
336 gc.weighty = 0.0;
337 add(buildLeftButtonPanel(), gc);
338
339 gc.gridx = 2;
340 gc.gridy = 2;
341 gc.fill = GridBagConstraints.BOTH;
342 gc.anchor = GridBagConstraints.FIRST_LINE_START;
343 gc.weightx = 0.33;
344 gc.weighty = 0.0;
345 pane = buildMergedElementsTable();
346 lblMergedVersion.setLabelFor(pane);
347 adjustmentSynchronizer.adapt(cbLockMergedScrolling, pane.getVerticalScrollBar());
348 add(pane, gc);
349
350 gc.gridx = 3;
351 gc.gridy = 2;
352 gc.fill = GridBagConstraints.NONE;
353 gc.anchor = GridBagConstraints.CENTER;
354 gc.weightx = 0.0;
355 gc.weighty = 0.0;
356 add(buildRightButtonPanel(), gc);
357
358 gc.gridx = 4;
359 gc.gridy = 2;
360 gc.fill = GridBagConstraints.BOTH;
361 gc.anchor = GridBagConstraints.FIRST_LINE_START;
362 gc.weightx = 0.33;
363 gc.weighty = 0.0;
364 pane = buildTheirElementsTable();
365 lblTheirVersion.setLabelFor(pane);
366 adjustmentSynchronizer.adapt(cbLockTheirScrolling, pane.getVerticalScrollBar());
367 add(pane, gc);
368
369 // ----------------------------------
370 gc.gridx = 2;
371 gc.gridy = 3;
372 gc.gridwidth = 1;
373 gc.gridheight = 1;
374 gc.fill = GridBagConstraints.BOTH;
375 gc.anchor = GridBagConstraints.CENTER;
376 gc.weightx = 0.0;
377 gc.weighty = 0.0;
378 add(buildMergedListControlButtons(), gc);
379
380 // -----------------------------------
381 gc.gridx = 0;
382 gc.gridy = 4;
383 gc.gridwidth = 2;
384 gc.gridheight = 1;
385 gc.fill = GridBagConstraints.HORIZONTAL;
386 gc.anchor = GridBagConstraints.LINE_START;
387 gc.weightx = 0.0;
388 gc.weighty = 0.0;
389 add(buildComparePairSelectionPanel(), gc);
390
391 gc.gridx = 2;
392 gc.gridy = 4;
393 gc.gridwidth = 3;
394 gc.gridheight = 1;
395 gc.fill = GridBagConstraints.HORIZONTAL;
396 gc.anchor = GridBagConstraints.LINE_START;
397 gc.weightx = 0.0;
398 gc.weighty = 0.0;
399 add(buildFrozeStateControlPanel(), gc);
400
401 wireActionsToSelectionModels();
402 }
403
404 /**
405 * Constructs a new {@code ListMerger}.
406 * @param model list merger model
407 */
408 public ListMerger(ListMergeModel<T> model) {
409 this.model = model;
410 model.addObserver(this);
411 build();
412 model.addPropertyChangeListener(this);
413 }
414
415 /**
416 * Base class of all other Copy* inner classes.
417 */
418 abstract static class CopyAction extends AbstractAction implements ListSelectionListener {
419
420 protected CopyAction(String icon_name, String action_name, String short_description) {
421 ImageIcon icon = ImageProvider.get("dialogs/conflict", icon_name);
422 putValue(Action.SMALL_ICON, icon);
423 if (icon == null) {
424 putValue(Action.NAME, action_name);
425 }
426 putValue(Action.SHORT_DESCRIPTION, short_description);
427 setEnabled(false);
428 }
429 }
430
431 /**
432 * Action for copying selected nodes in the list of my nodes to the list of merged
433 * nodes. Inserts the nodes at the beginning of the list of merged nodes.
434 */
435 class CopyStartLeftAction extends CopyAction {
436
437 CopyStartLeftAction() {
438 super(/* ICON(dialogs/conflict/)*/ "copystartleft", tr("> top"),
439 tr("Copy my selected nodes to the start of the merged node list"));
440 }
441
442 @Override
443 public void actionPerformed(ActionEvent e) {
444 model.copyMyToTop(myEntriesTable.getSelectedRows());
445 }
446
447 @Override
448 public void valueChanged(ListSelectionEvent e) {
449 setEnabled(!myEntriesTable.getSelectionModel().isSelectionEmpty());
450 }
451 }
452
453 /**
454 * Action for copying selected nodes in the list of my nodes to the list of merged
455 * nodes. Inserts the nodes at the end of the list of merged nodes.
456 */
457 class CopyEndLeftAction extends CopyAction {
458
459 CopyEndLeftAction() {
460 super(/* ICON(dialogs/conflict/)*/ "copyendleft", tr("> bottom"),
461 tr("Copy my selected elements to the end of the list of merged elements."));
462 }
463
464 @Override
465 public void actionPerformed(ActionEvent e) {
466 model.copyMyToEnd(myEntriesTable.getSelectedRows());
467 }
468
469 @Override
470 public void valueChanged(ListSelectionEvent e) {
471 setEnabled(!myEntriesTable.getSelectionModel().isSelectionEmpty());
472 }
473 }
474
475 /**
476 * Action for copying selected nodes in the list of my nodes to the list of merged
477 * nodes. Inserts the nodes before the first selected row in the list of merged nodes.
478 */
479 class CopyBeforeCurrentLeftAction extends CopyAction {
480
481 CopyBeforeCurrentLeftAction() {
482 super(/* ICON(dialogs/conflict/)*/ "copybeforecurrentleft", tr("> before"),
483 tr("Copy my selected elements before the first selected element in the list of merged elements."));
484 }
485
486 @Override
487 public void actionPerformed(ActionEvent e) {
488 int[] mergedRows = mergedEntriesTable.getSelectedRows();
489 if (mergedRows == null || mergedRows.length == 0)
490 return;
491 int[] myRows = myEntriesTable.getSelectedRows();
492 int current = mergedRows[0];
493 model.copyMyBeforeCurrent(myRows, current);
494 }
495
496 @Override
497 public void valueChanged(ListSelectionEvent e) {
498 setEnabled(
499 !myEntriesTable.getSelectionModel().isSelectionEmpty()
500 && !mergedEntriesTable.getSelectionModel().isSelectionEmpty()
501 );
502 }
503 }
504
505 /**
506 * Action for copying selected nodes in the list of my nodes to the list of merged
507 * nodes. Inserts the nodes after the first selected row in the list of merged nodes.
508 */
509 class CopyAfterCurrentLeftAction extends CopyAction {
510
511 CopyAfterCurrentLeftAction() {
512 super(/* ICON(dialogs/conflict/)*/ "copyaftercurrentleft", tr("> after"),
513 tr("Copy my selected elements after the first selected element in the list of merged elements."));
514 }
515
516 @Override
517 public void actionPerformed(ActionEvent e) {
518 int[] mergedRows = mergedEntriesTable.getSelectedRows();
519 if (mergedRows == null || mergedRows.length == 0)
520 return;
521 int[] myRows = myEntriesTable.getSelectedRows();
522 int current = mergedRows[0];
523 model.copyMyAfterCurrent(myRows, current);
524 }
525
526 @Override
527 public void valueChanged(ListSelectionEvent e) {
528 setEnabled(
529 !myEntriesTable.getSelectionModel().isSelectionEmpty()
530 && !mergedEntriesTable.getSelectionModel().isSelectionEmpty()
531 );
532 }
533 }
534
535 class CopyStartRightAction extends CopyAction {
536
537 CopyStartRightAction() {
538 super(/* ICON(dialogs/conflict/)*/ "copystartright", tr("< top"),
539 tr("Copy their selected element to the start of the list of merged elements."));
540 }
541
542 @Override
543 public void actionPerformed(ActionEvent e) {
544 model.copyTheirToTop(theirEntriesTable.getSelectedRows());
545 }
546
547 @Override
548 public void valueChanged(ListSelectionEvent e) {
549 setEnabled(!theirEntriesTable.getSelectionModel().isSelectionEmpty());
550 }
551 }
552
553 class CopyEndRightAction extends CopyAction {
554
555 CopyEndRightAction() {
556 super(/* ICON(dialogs/conflict/)*/ "copyendright", tr("< bottom"),
557 tr("Copy their selected elements to the end of the list of merged elements."));
558 }
559
560 @Override
561 public void actionPerformed(ActionEvent arg0) {
562 model.copyTheirToEnd(theirEntriesTable.getSelectedRows());
563 }
564
565 @Override
566 public void valueChanged(ListSelectionEvent e) {
567 setEnabled(!theirEntriesTable.getSelectionModel().isSelectionEmpty());
568 }
569 }
570
571 class CopyBeforeCurrentRightAction extends CopyAction {
572
573 CopyBeforeCurrentRightAction() {
574 super(/* ICON(dialogs/conflict/)*/ "copybeforecurrentright", tr("< before"),
575 tr("Copy their selected elements before the first selected element in the list of merged elements."));
576 }
577
578 @Override
579 public void actionPerformed(ActionEvent e) {
580 int[] mergedRows = mergedEntriesTable.getSelectedRows();
581 if (mergedRows == null || mergedRows.length == 0)
582 return;
583 int[] myRows = theirEntriesTable.getSelectedRows();
584 int current = mergedRows[0];
585 model.copyTheirBeforeCurrent(myRows, current);
586 }
587
588 @Override
589 public void valueChanged(ListSelectionEvent e) {
590 setEnabled(
591 !theirEntriesTable.getSelectionModel().isSelectionEmpty()
592 && !mergedEntriesTable.getSelectionModel().isSelectionEmpty()
593 );
594 }
595 }
596
597 class CopyAfterCurrentRightAction extends CopyAction {
598
599 CopyAfterCurrentRightAction() {
600 super(/* ICON(dialogs/conflict/)*/ "copyaftercurrentright", tr("< after"),
601 tr("Copy their selected element after the first selected element in the list of merged elements"));
602 }
603
604 @Override
605 public void actionPerformed(ActionEvent e) {
606 int[] mergedRows = mergedEntriesTable.getSelectedRows();
607 if (mergedRows == null || mergedRows.length == 0)
608 return;
609 int[] myRows = theirEntriesTable.getSelectedRows();
610 int current = mergedRows[0];
611 model.copyTheirAfterCurrent(myRows, current);
612 }
613
614 @Override
615 public void valueChanged(ListSelectionEvent e) {
616 setEnabled(
617 !theirEntriesTable.getSelectionModel().isSelectionEmpty()
618 && !mergedEntriesTable.getSelectionModel().isSelectionEmpty()
619 );
620 }
621 }
622
623 class CopyAllLeft extends AbstractAction implements Observer, PropertyChangeListener {
624
625 CopyAllLeft() {
626 ImageIcon icon = ImageProvider.get("dialogs/conflict", "useallleft");
627 putValue(Action.SMALL_ICON, icon);
628 putValue(Action.SHORT_DESCRIPTION, tr("Copy all my elements to the target"));
629 }
630
631 @Override
632 public void actionPerformed(ActionEvent arg0) {
633 model.copyAll(ListRole.MY_ENTRIES);
634 model.setFrozen(true);
635 }
636
637 private void updateEnabledState() {
638 setEnabled(model.getMergedEntries().isEmpty() && !model.isFrozen());
639 }
640
641 @Override
642 public void update(Observable o, Object arg) {
643 updateEnabledState();
644 }
645
646 @Override
647 public void propertyChange(PropertyChangeEvent evt) {
648 updateEnabledState();
649 }
650 }
651
652 class CopyAllRight extends AbstractAction implements Observer, PropertyChangeListener {
653
654 CopyAllRight() {
655 ImageIcon icon = ImageProvider.get("dialogs/conflict", "useallright");
656 putValue(Action.SMALL_ICON, icon);
657 putValue(Action.SHORT_DESCRIPTION, tr("Copy all their elements to the target"));
658 }
659
660 @Override
661 public void actionPerformed(ActionEvent arg0) {
662 model.copyAll(ListRole.THEIR_ENTRIES);
663 model.setFrozen(true);
664 }
665
666 private void updateEnabledState() {
667 setEnabled(model.getMergedEntries().isEmpty() && !model.isFrozen());
668 }
669
670 @Override
671 public void update(Observable o, Object arg) {
672 updateEnabledState();
673 }
674
675 @Override
676 public void propertyChange(PropertyChangeEvent evt) {
677 updateEnabledState();
678 }
679 }
680
681 class MoveUpMergedAction extends AbstractAction implements ListSelectionListener {
682
683 MoveUpMergedAction() {
684 ImageIcon icon = ImageProvider.get("dialogs/conflict", "moveup");
685 putValue(Action.SMALL_ICON, icon);
686 if (icon == null) {
687 putValue(Action.NAME, tr("Up"));
688 }
689 putValue(Action.SHORT_DESCRIPTION, tr("Move up the selected entries by one position."));
690 setEnabled(false);
691 }
692
693 @Override
694 public void actionPerformed(ActionEvent arg0) {
695 int[] rows = mergedEntriesTable.getSelectedRows();
696 model.moveUpMerged(rows);
697 }
698
699 @Override
700 public void valueChanged(ListSelectionEvent e) {
701 int[] rows = mergedEntriesTable.getSelectedRows();
702 setEnabled(
703 rows != null
704 && rows.length > 0
705 && rows[0] != 0
706 );
707 }
708 }
709
710 /**
711 * Action for moving the currently selected entries in the list of merged entries
712 * one position down
713 *
714 */
715 class MoveDownMergedAction extends AbstractAction implements ListSelectionListener {
716
717 MoveDownMergedAction() {
718 ImageIcon icon = ImageProvider.get("dialogs/conflict", "movedown");
719 putValue(Action.SMALL_ICON, icon);
720 if (icon == null) {
721 putValue(Action.NAME, tr("Down"));
722 }
723 putValue(Action.SHORT_DESCRIPTION, tr("Move down the selected entries by one position."));
724 setEnabled(false);
725 }
726
727 @Override
728 public void actionPerformed(ActionEvent arg0) {
729 int[] rows = mergedEntriesTable.getSelectedRows();
730 model.moveDownMerged(rows);
731 }
732
733 @Override
734 public void valueChanged(ListSelectionEvent e) {
735 int[] rows = mergedEntriesTable.getSelectedRows();
736 setEnabled(
737 rows != null
738 && rows.length > 0
739 && rows[rows.length -1] != mergedEntriesTable.getRowCount() -1
740 );
741 }
742 }
743
744 /**
745 * Action for removing the selected entries in the list of merged entries
746 * from the list of merged entries.
747 *
748 */
749 class RemoveMergedAction extends AbstractAction implements ListSelectionListener {
750
751 RemoveMergedAction() {
752 ImageIcon icon = ImageProvider.get("dialogs/conflict", "remove");
753 putValue(Action.SMALL_ICON, icon);
754 if (icon == null) {
755 putValue(Action.NAME, tr("Remove"));
756 }
757 putValue(Action.SHORT_DESCRIPTION, tr("Remove the selected entries from the list of merged elements."));
758 setEnabled(false);
759 }
760
761 @Override
762 public void actionPerformed(ActionEvent arg0) {
763 int[] rows = mergedEntriesTable.getSelectedRows();
764 model.removeMerged(rows);
765 }
766
767 @Override
768 public void valueChanged(ListSelectionEvent e) {
769 int[] rows = mergedEntriesTable.getSelectedRows();
770 setEnabled(
771 rows != null
772 && rows.length > 0
773 );
774 }
775 }
776
777 private interface FreezeActionProperties {
778 String PROP_SELECTED = FreezeActionProperties.class.getName() + ".selected";
779 }
780
781 /**
782 * Action for freezing the current state of the list merger
783 *
784 */
785 private final class FreezeAction extends AbstractAction implements ItemListener, FreezeActionProperties {
786
787 private FreezeAction() {
788 putValue(Action.NAME, tr("Freeze"));
789 putValue(Action.SHORT_DESCRIPTION, tr("Freeze the current list of merged elements."));
790 putValue(PROP_SELECTED, Boolean.FALSE);
791 setEnabled(true);
792 }
793
794 @Override
795 public void actionPerformed(ActionEvent arg0) {
796 // do nothing
797 }
798
799 /**
800 * Java 1.5 doesn't known Action.SELECT_KEY. Wires a toggle button to this action
801 * such that the action gets notified about item state changes and the button gets
802 * notified about selection state changes of the action.
803 *
804 * @param btn a toggle button
805 */
806 public void adapt(final JToggleButton btn) {
807 btn.addItemListener(this);
808 addPropertyChangeListener(
809 new PropertyChangeListener() {
810 @Override
811 public void propertyChange(PropertyChangeEvent evt) {
812 if (evt.getPropertyName().equals(PROP_SELECTED)) {
813 btn.setSelected((Boolean) evt.getNewValue());
814 }
815 }
816 }
817 );
818 }
819
820 @Override
821 public void itemStateChanged(ItemEvent e) {
822 int state = e.getStateChange();
823 if (state == ItemEvent.SELECTED) {
824 putValue(Action.NAME, tr("Unfreeze"));
825 putValue(Action.SHORT_DESCRIPTION, tr("Unfreeze the list of merged elements and start merging."));
826 model.setFrozen(true);
827 } else if (state == ItemEvent.DESELECTED) {
828 putValue(Action.NAME, tr("Freeze"));
829 putValue(Action.SHORT_DESCRIPTION, tr("Freeze the current list of merged elements."));
830 model.setFrozen(false);
831 }
832 boolean isSelected = (Boolean) getValue(PROP_SELECTED);
833 if (isSelected != (e.getStateChange() == ItemEvent.SELECTED)) {
834 putValue(PROP_SELECTED, e.getStateChange() == ItemEvent.SELECTED);
835 }
836
837 }
838 }
839
840 protected void handlePropertyChangeFrozen(boolean oldValue, boolean newValue) {
841 myEntriesTable.getSelectionModel().clearSelection();
842 myEntriesTable.setEnabled(!newValue);
843 theirEntriesTable.getSelectionModel().clearSelection();
844 theirEntriesTable.setEnabled(!newValue);
845 mergedEntriesTable.getSelectionModel().clearSelection();
846 mergedEntriesTable.setEnabled(!newValue);
847 freezeAction.putValue(FreezeActionProperties.PROP_SELECTED, newValue);
848 if (newValue) {
849 lblFrozenState.setText(
850 tr("<html>Click <strong>{0}</strong> to start merging my and their entries.</html>",
851 freezeAction.getValue(Action.NAME))
852 );
853 } else {
854 lblFrozenState.setText(
855 tr("<html>Click <strong>{0}</strong> to finish merging my and their entries.</html>",
856 freezeAction.getValue(Action.NAME))
857 );
858 }
859 }
860
861 @Override
862 public void propertyChange(PropertyChangeEvent evt) {
863 if (evt.getPropertyName().equals(ListMergeModel.FROZEN_PROP)) {
864 handlePropertyChangeFrozen((Boolean) evt.getOldValue(), (Boolean) evt.getNewValue());
865 }
866 }
867
868 public ListMergeModel<T> getModel() {
869 return model;
870 }
871
872 @Override
873 public void update(Observable o, Object arg) {
874 lblMyVersion.setText(
875 trn("My version ({0} entry)", "My version ({0} entries)", model.getMyEntriesSize(), model.getMyEntriesSize())
876 );
877 lblMergedVersion.setText(
878 trn("Merged version ({0} entry)", "Merged version ({0} entries)", model.getMergedEntriesSize(), model.getMergedEntriesSize())
879 );
880 lblTheirVersion.setText(
881 trn("Their version ({0} entry)", "Their version ({0} entries)", model.getTheirEntriesSize(), model.getTheirEntriesSize())
882 );
883 }
884
885 public void unlinkAsListener() {
886 myEntriesTable.unlinkAsListener();
887 mergedEntriesTable.unlinkAsListener();
888 theirEntriesTable.unlinkAsListener();
889 }
890
891 protected final <P extends OsmPrimitive> OsmDataLayer findLayerFor(P primitive) {
892 if (primitive != null) {
893 Iterable<OsmDataLayer> layers = Main.map.mapView.getLayersOfType(OsmDataLayer.class);
894 // Find layer with same dataset
895 for (OsmDataLayer layer : layers) {
896 if (layer.data == primitive.getDataSet()) {
897 return layer;
898 }
899 }
900 // Conflict after merging layers: a dataset could be no more in any layer, try to find another layer with same primitive
901 for (OsmDataLayer layer : layers) {
902 final Collection<? extends OsmPrimitive> collection;
903 if (primitive instanceof Way) {
904 collection = layer.data.getWays();
905 } else if (primitive instanceof Relation) {
906 collection = layer.data.getRelations();
907 } else {
908 collection = layer.data.allPrimitives();
909 }
910 for (OsmPrimitive p : collection) {
911 if (p.getPrimitiveId().equals(primitive.getPrimitiveId())) {
912 return layer;
913 }
914 }
915 }
916 }
917 return null;
918 }
919
920 public void decideRemaining(MergeDecisionType decision) {
921 if (!model.isFrozen()) {
922 model.copyAll(MergeDecisionType.KEEP_MINE.equals(decision) ? ListRole.MY_ENTRIES : ListRole.THEIR_ENTRIES);
923 model.setFrozen(true);
924 }
925 }
926}
Note: See TracBrowser for help on using the repository browser.