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

Last change on this file since 5266 was 5266, checked in by bastiK, 12 years ago

fixed majority of javadoc warnings by replacing "{@see" by "{@link"

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