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

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

fix #15572 - use ImageProvider attach API for all JOSM actions to ensure proper icon size everywhere

  • Property svn:eol-style set to native
File size: 33.2 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;
43import org.openstreetmap.josm.tools.ImageResource;
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 ImageResource icon = new ImageProvider("dialogs/conflict", iconName).getResource();
425 if (icon == null) {
426 putValue(Action.NAME, actionName);
427 } else {
428 icon.attachImageIcon(this, true);
429 }
430 putValue(Action.SHORT_DESCRIPTION, shortDescription);
431 setEnabled(false);
432 }
433 }
434
435 /**
436 * Action for copying selected nodes in the list of my nodes to the list of merged
437 * nodes. Inserts the nodes at the beginning of the list of merged nodes.
438 */
439 class CopyStartLeftAction extends CopyAction {
440
441 CopyStartLeftAction() {
442 super(/* ICON(dialogs/conflict/)*/ "copystartleft", tr("> top"),
443 tr("Copy my selected nodes to the start of the merged node list"));
444 }
445
446 @Override
447 public void actionPerformed(ActionEvent e) {
448 model.copyMyToTop(myEntriesTable.getSelectedRows());
449 }
450
451 @Override
452 public void valueChanged(ListSelectionEvent e) {
453 setEnabled(!myEntriesTable.getSelectionModel().isSelectionEmpty());
454 }
455 }
456
457 /**
458 * Action for copying selected nodes in the list of my nodes to the list of merged
459 * nodes. Inserts the nodes at the end of the list of merged nodes.
460 */
461 class CopyEndLeftAction extends CopyAction {
462
463 CopyEndLeftAction() {
464 super(/* ICON(dialogs/conflict/)*/ "copyendleft", tr("> bottom"),
465 tr("Copy my selected elements to the end of the list of merged elements."));
466 }
467
468 @Override
469 public void actionPerformed(ActionEvent e) {
470 model.copyMyToEnd(myEntriesTable.getSelectedRows());
471 }
472
473 @Override
474 public void valueChanged(ListSelectionEvent e) {
475 setEnabled(!myEntriesTable.getSelectionModel().isSelectionEmpty());
476 }
477 }
478
479 /**
480 * Action for copying selected nodes in the list of my nodes to the list of merged
481 * nodes. Inserts the nodes before the first selected row in the list of merged nodes.
482 */
483 class CopyBeforeCurrentLeftAction extends CopyAction {
484
485 CopyBeforeCurrentLeftAction() {
486 super(/* ICON(dialogs/conflict/)*/ "copybeforecurrentleft", tr("> before"),
487 tr("Copy my selected elements before the first selected element in the list of merged elements."));
488 }
489
490 @Override
491 public void actionPerformed(ActionEvent e) {
492 int[] mergedRows = mergedEntriesTable.getSelectedRows();
493 if (mergedRows.length == 0)
494 return;
495 int[] myRows = myEntriesTable.getSelectedRows();
496 int current = mergedRows[0];
497 model.copyMyBeforeCurrent(myRows, current);
498 }
499
500 @Override
501 public void valueChanged(ListSelectionEvent e) {
502 setEnabled(
503 !myEntriesTable.getSelectionModel().isSelectionEmpty()
504 && !mergedEntriesTable.getSelectionModel().isSelectionEmpty()
505 );
506 }
507 }
508
509 /**
510 * Action for copying selected nodes in the list of my nodes to the list of merged
511 * nodes. Inserts the nodes after the first selected row in the list of merged nodes.
512 */
513 class CopyAfterCurrentLeftAction extends CopyAction {
514
515 CopyAfterCurrentLeftAction() {
516 super(/* ICON(dialogs/conflict/)*/ "copyaftercurrentleft", tr("> after"),
517 tr("Copy my selected elements after the first selected element in the list of merged elements."));
518 }
519
520 @Override
521 public void actionPerformed(ActionEvent e) {
522 int[] mergedRows = mergedEntriesTable.getSelectedRows();
523 if (mergedRows.length == 0)
524 return;
525 int[] myRows = myEntriesTable.getSelectedRows();
526 int current = mergedRows[0];
527 model.copyMyAfterCurrent(myRows, current);
528 }
529
530 @Override
531 public void valueChanged(ListSelectionEvent e) {
532 setEnabled(
533 !myEntriesTable.getSelectionModel().isSelectionEmpty()
534 && !mergedEntriesTable.getSelectionModel().isSelectionEmpty()
535 );
536 }
537 }
538
539 class CopyStartRightAction extends CopyAction {
540
541 CopyStartRightAction() {
542 super(/* ICON(dialogs/conflict/)*/ "copystartright", tr("< top"),
543 tr("Copy their selected element to the start of the list of merged elements."));
544 }
545
546 @Override
547 public void actionPerformed(ActionEvent e) {
548 model.copyTheirToTop(theirEntriesTable.getSelectedRows());
549 }
550
551 @Override
552 public void valueChanged(ListSelectionEvent e) {
553 setEnabled(!theirEntriesTable.getSelectionModel().isSelectionEmpty());
554 }
555 }
556
557 class CopyEndRightAction extends CopyAction {
558
559 CopyEndRightAction() {
560 super(/* ICON(dialogs/conflict/)*/ "copyendright", tr("< bottom"),
561 tr("Copy their selected elements to the end of the list of merged elements."));
562 }
563
564 @Override
565 public void actionPerformed(ActionEvent arg0) {
566 model.copyTheirToEnd(theirEntriesTable.getSelectedRows());
567 }
568
569 @Override
570 public void valueChanged(ListSelectionEvent e) {
571 setEnabled(!theirEntriesTable.getSelectionModel().isSelectionEmpty());
572 }
573 }
574
575 class CopyBeforeCurrentRightAction extends CopyAction {
576
577 CopyBeforeCurrentRightAction() {
578 super(/* ICON(dialogs/conflict/)*/ "copybeforecurrentright", tr("< before"),
579 tr("Copy their selected elements before the first selected element in the list of merged elements."));
580 }
581
582 @Override
583 public void actionPerformed(ActionEvent e) {
584 int[] mergedRows = mergedEntriesTable.getSelectedRows();
585 if (mergedRows.length == 0)
586 return;
587 int[] myRows = theirEntriesTable.getSelectedRows();
588 int current = mergedRows[0];
589 model.copyTheirBeforeCurrent(myRows, current);
590 }
591
592 @Override
593 public void valueChanged(ListSelectionEvent e) {
594 setEnabled(
595 !theirEntriesTable.getSelectionModel().isSelectionEmpty()
596 && !mergedEntriesTable.getSelectionModel().isSelectionEmpty()
597 );
598 }
599 }
600
601 class CopyAfterCurrentRightAction extends CopyAction {
602
603 CopyAfterCurrentRightAction() {
604 super(/* ICON(dialogs/conflict/)*/ "copyaftercurrentright", tr("< after"),
605 tr("Copy their selected element after the first selected element in the list of merged elements"));
606 }
607
608 @Override
609 public void actionPerformed(ActionEvent e) {
610 int[] mergedRows = mergedEntriesTable.getSelectedRows();
611 if (mergedRows.length == 0)
612 return;
613 int[] myRows = theirEntriesTable.getSelectedRows();
614 int current = mergedRows[0];
615 model.copyTheirAfterCurrent(myRows, current);
616 }
617
618 @Override
619 public void valueChanged(ListSelectionEvent e) {
620 setEnabled(
621 !theirEntriesTable.getSelectionModel().isSelectionEmpty()
622 && !mergedEntriesTable.getSelectionModel().isSelectionEmpty()
623 );
624 }
625 }
626
627 class CopyAllLeft extends AbstractAction implements ChangeListener, PropertyChangeListener {
628
629 CopyAllLeft() {
630 new ImageProvider("dialogs/conflict", "useallleft").getResource().attachImageIcon(this, true);
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 new ImageProvider("dialogs/conflict", "useallright").getResource().attachImageIcon(this, true);
659 putValue(Action.SHORT_DESCRIPTION, tr("Copy all their elements to the target"));
660 }
661
662 @Override
663 public void actionPerformed(ActionEvent arg0) {
664 model.copyAll(ListRole.THEIR_ENTRIES);
665 model.setFrozen(true);
666 }
667
668 private void updateEnabledState() {
669 setEnabled(model.getMergedEntries().isEmpty() && !model.isFrozen());
670 }
671
672 @Override
673 public void stateChanged(ChangeEvent e) {
674 updateEnabledState();
675 }
676
677 @Override
678 public void propertyChange(PropertyChangeEvent evt) {
679 updateEnabledState();
680 }
681 }
682
683 class MoveUpMergedAction extends AbstractAction implements ListSelectionListener {
684
685 MoveUpMergedAction() {
686 ImageResource icon = new ImageProvider("dialogs/conflict", "moveup").getResource();
687 if (icon == null) {
688 putValue(Action.NAME, tr("Up"));
689 } else {
690 icon.attachImageIcon(this, true);
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 ImageResource icon = new ImageProvider("dialogs/conflict", "movedown").getResource();
720 if (icon == null) {
721 putValue(Action.NAME, tr("Down"));
722 } else {
723 icon.attachImageIcon(this, true);
724 }
725 putValue(Action.SHORT_DESCRIPTION, tr("Move down the selected entries by one position."));
726 setEnabled(false);
727 }
728
729 @Override
730 public void actionPerformed(ActionEvent arg0) {
731 int[] rows = mergedEntriesTable.getSelectedRows();
732 model.moveDownMerged(rows);
733 }
734
735 @Override
736 public void valueChanged(ListSelectionEvent e) {
737 int[] rows = mergedEntriesTable.getSelectedRows();
738 setEnabled(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 ImageResource icon = new ImageProvider("dialogs/conflict", "remove").getResource();
753 if (icon == null) {
754 putValue(Action.NAME, tr("Remove"));
755 } else {
756 icon.attachImageIcon(this, true);
757 }
758 putValue(Action.SHORT_DESCRIPTION, tr("Remove the selected entries from the list of merged elements."));
759 setEnabled(false);
760 }
761
762 @Override
763 public void actionPerformed(ActionEvent arg0) {
764 int[] rows = mergedEntriesTable.getSelectedRows();
765 model.removeMerged(rows);
766 }
767
768 @Override
769 public void valueChanged(ListSelectionEvent e) {
770 int[] rows = mergedEntriesTable.getSelectedRows();
771 setEnabled(rows.length > 0);
772 }
773 }
774
775 private interface FreezeActionProperties {
776 String PROP_SELECTED = FreezeActionProperties.class.getName() + ".selected";
777 }
778
779 /**
780 * Action for freezing the current state of the list merger
781 *
782 */
783 private final class FreezeAction extends AbstractAction implements ItemListener, FreezeActionProperties {
784
785 private FreezeAction() {
786 putValue(Action.NAME, tr("Freeze"));
787 putValue(Action.SHORT_DESCRIPTION, tr("Freeze the current list of merged elements."));
788 putValue(PROP_SELECTED, Boolean.FALSE);
789 setEnabled(true);
790 }
791
792 @Override
793 public void actionPerformed(ActionEvent arg0) {
794 // do nothing
795 }
796
797 /**
798 * Java 1.5 doesn't known Action.SELECT_KEY. Wires a toggle button to this action
799 * such that the action gets notified about item state changes and the button gets
800 * notified about selection state changes of the action.
801 *
802 * @param btn a toggle button
803 */
804 public void adapt(final JToggleButton btn) {
805 btn.addItemListener(this);
806 addPropertyChangeListener(evt -> {
807 if (evt.getPropertyName().equals(PROP_SELECTED)) {
808 btn.setSelected((Boolean) evt.getNewValue());
809 }
810 });
811 }
812
813 @Override
814 public void itemStateChanged(ItemEvent e) {
815 int state = e.getStateChange();
816 if (state == ItemEvent.SELECTED) {
817 putValue(Action.NAME, tr("Unfreeze"));
818 putValue(Action.SHORT_DESCRIPTION, tr("Unfreeze the list of merged elements and start merging."));
819 model.setFrozen(true);
820 } else if (state == ItemEvent.DESELECTED) {
821 putValue(Action.NAME, tr("Freeze"));
822 putValue(Action.SHORT_DESCRIPTION, tr("Freeze the current list of merged elements."));
823 model.setFrozen(false);
824 }
825 boolean isSelected = (Boolean) getValue(PROP_SELECTED);
826 if (isSelected != (e.getStateChange() == ItemEvent.SELECTED)) {
827 putValue(PROP_SELECTED, e.getStateChange() == ItemEvent.SELECTED);
828 }
829
830 }
831 }
832
833 protected void handlePropertyChangeFrozen(boolean newValue) {
834 myEntriesTable.getSelectionModel().clearSelection();
835 myEntriesTable.setEnabled(!newValue);
836 theirEntriesTable.getSelectionModel().clearSelection();
837 theirEntriesTable.setEnabled(!newValue);
838 mergedEntriesTable.getSelectionModel().clearSelection();
839 mergedEntriesTable.setEnabled(!newValue);
840 freezeAction.putValue(FreezeActionProperties.PROP_SELECTED, newValue);
841 if (newValue) {
842 lblFrozenState.setText(
843 tr("<html>Click <strong>{0}</strong> to start merging my and their entries.</html>",
844 freezeAction.getValue(Action.NAME))
845 );
846 } else {
847 lblFrozenState.setText(
848 tr("<html>Click <strong>{0}</strong> to finish merging my and their entries.</html>",
849 freezeAction.getValue(Action.NAME))
850 );
851 }
852 }
853
854 @Override
855 public void propertyChange(PropertyChangeEvent evt) {
856 if (evt.getPropertyName().equals(AbstractListMergeModel.FROZEN_PROP)) {
857 handlePropertyChangeFrozen((Boolean) evt.getNewValue());
858 }
859 }
860
861 /**
862 * Returns the model.
863 * @return the model
864 */
865 public AbstractListMergeModel<T, C> getModel() {
866 return model;
867 }
868
869 @Override
870 public void stateChanged(ChangeEvent e) {
871 lblMyVersion.setText(
872 trn("My version ({0} entry)", "My version ({0} entries)", model.getMyEntriesSize(), model.getMyEntriesSize())
873 );
874 lblMergedVersion.setText(
875 trn("Merged version ({0} entry)", "Merged version ({0} entries)", model.getMergedEntriesSize(), model.getMergedEntriesSize())
876 );
877 lblTheirVersion.setText(
878 trn("Their version ({0} entry)", "Their version ({0} entries)", model.getTheirEntriesSize(), model.getTheirEntriesSize())
879 );
880 }
881
882 /**
883 * Adds all registered listeners by this merger
884 * @see #unregisterListeners()
885 * @since 10454
886 */
887 public void registerListeners() {
888 myEntriesTable.registerListeners();
889 mergedEntriesTable.registerListeners();
890 theirEntriesTable.registerListeners();
891 }
892
893 /**
894 * Removes all registered listeners by this merger
895 * @since 10454
896 */
897 public void unregisterListeners() {
898 myEntriesTable.unregisterListeners();
899 mergedEntriesTable.unregisterListeners();
900 theirEntriesTable.unregisterListeners();
901 }
902
903 protected final <P extends OsmPrimitive> OsmDataLayer findLayerFor(P primitive) {
904 if (primitive != null) {
905 Iterable<OsmDataLayer> layers = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class);
906 // Find layer with same dataset
907 for (OsmDataLayer layer : layers) {
908 if (layer.data == primitive.getDataSet()) {
909 return layer;
910 }
911 }
912 // Conflict after merging layers: a dataset could be no more in any layer, try to find another layer with same primitive
913 for (OsmDataLayer layer : layers) {
914 final Collection<? extends OsmPrimitive> collection;
915 if (primitive instanceof Way) {
916 collection = layer.data.getWays();
917 } else if (primitive instanceof Relation) {
918 collection = layer.data.getRelations();
919 } else {
920 collection = layer.data.allPrimitives();
921 }
922 for (OsmPrimitive p : collection) {
923 if (p.getPrimitiveId().equals(primitive.getPrimitiveId())) {
924 return layer;
925 }
926 }
927 }
928 }
929 return null;
930 }
931
932 @Override
933 public void decideRemaining(MergeDecisionType decision) {
934 if (!model.isFrozen()) {
935 model.copyAll(MergeDecisionType.KEEP_MINE.equals(decision) ? ListRole.MY_ENTRIES : ListRole.THEIR_ENTRIES);
936 model.setFrozen(true);
937 }
938 }
939}
Note: See TracBrowser for help on using the repository browser.