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

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

fix #7855 - conflict dialog: CCE selecting a way in members tab

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