source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/pair/AbstractListMerger.java@ 12636

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

see #15182 - deprecate Main.getLayerManager(). Replacement: gui.MainApplication.getLayerManager()

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