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

Last change on this file since 16267 was 16267, checked in by simon04, 4 years ago

Use non-optional ImageResource without null check

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