source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/pair/ListMergeModel.java@ 1954

Last change on this file since 1954 was 1954, checked in by Gubaer, 15 years ago

Moved package org.openstreetmap.josm.gui.conflict to org.openstreetmap.josm.gui.conflict.pair

File size: 28.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.pair;
3
4import static org.openstreetmap.josm.gui.conflict.pair.ComparePairType.MY_WITH_MERGED;
5import static org.openstreetmap.josm.gui.conflict.pair.ComparePairType.MY_WITH_THEIR;
6import static org.openstreetmap.josm.gui.conflict.pair.ComparePairType.THEIR_WITH_MERGED;
7import static org.openstreetmap.josm.gui.conflict.pair.ListRole.MERGED_ENTRIES;
8import static org.openstreetmap.josm.gui.conflict.pair.ListRole.MY_ENTRIES;
9import static org.openstreetmap.josm.gui.conflict.pair.ListRole.THEIR_ENTRIES;
10import static org.openstreetmap.josm.tools.I18n.tr;
11
12import java.beans.PropertyChangeEvent;
13import java.beans.PropertyChangeListener;
14import java.util.ArrayList;
15import java.util.HashMap;
16import java.util.Observable;
17import java.util.logging.Logger;
18
19import javax.swing.AbstractListModel;
20import javax.swing.ComboBoxModel;
21import javax.swing.DefaultListSelectionModel;
22import javax.swing.table.DefaultTableModel;
23import javax.swing.table.TableModel;
24
25/**
26 * ListMergeModel is a model for interactively comparing and merging two list of entries
27 * of type T. It maintains three lists of entries of type T:
28 * <ol>
29 * <li>the list of <em>my</em> entries</li>
30 * <li>the list of <em>their</em> entries</li>
31 * <li>the list of <em>merged</em> entries</li>
32 * </ol>
33 *
34 * A ListMergeModel is a factory for three {@see TableModel}s and three {@see ListSelectionModel}s:
35 * <ol>
36 * <li>the table model and the list selection for for a {@see JTable} which shows my entries.
37 * See {@see #getMyTableModel()}</li> and {@see ListMergeModel#getMySelectionModel()}</li>
38 * <li>dito for their entries and merged entries</li>
39 * </ol>
40 *
41 * A ListMergeModel can be ''frozen''. If it's frozen, it doesn't accept additional merge
42 * decisions. {@see PropertyChangeListener}s can register for property value changes of
43 * {@see #PROP_FROZEN}.
44 *
45 * ListMergeModel is an abstract class. Three methods have to be implemented by subclasses:
46 * <ul>
47 * <li>{@see ListMergeModel#cloneEntryForMergedList(Object)} - clones an entry of type T</li>
48 * <li>{@see ListMergeModel#isEqualEntry(Object, Object)} - checks whether two entries are equals </li>
49 * <li>{@see ListMergeModel#setValueAt(DefaultTableModel, Object, int, int)} - handles values edited in
50 * a JTable, dispatched from {@see TableModel#setValueAt(Object, int, int)} </li>
51 * </ul>
52 * A ListMergeModel is used in combination with a {@see ListMerger}.
53 *
54 * @param <T> the type of the list entries
55 * @see ListMerger
56 */
57public abstract class ListMergeModel<T> extends Observable {
58 private static final Logger logger = Logger.getLogger(ListMergeModel.class.getName());
59
60 public static final String FROZEN_PROP = ListMergeModel.class.getName() + ".frozen";
61
62 protected HashMap<ListRole, ArrayList<T>> entries;
63
64 protected DefaultTableModel myEntriesTableModel;
65 protected DefaultTableModel theirEntriesTableModel;
66 protected DefaultTableModel mergedEntriesTableModel;
67
68 protected EntriesSelectionModel myEntriesSelectionModel;
69 protected EntriesSelectionModel theirEntriesSelectionModel;
70 protected EntriesSelectionModel mergedEntriesSelectionModel;
71
72 private final ArrayList<PropertyChangeListener> listeners;
73 private boolean isFrozen = false;
74 private final ComparePairListModel comparePairListModel;
75
76
77
78 /**
79 * Creates a clone of an entry of type T suitable to be included in the
80 * list of merged entries
81 *
82 * @param entry the entry
83 * @return the cloned entry
84 */
85 protected abstract T cloneEntryForMergedList(T entry);
86
87 /**
88 * checks whether two entries are equal. This is not necessarily the same as
89 * e1.equals(e2).
90 *
91 * @param e1 the first entry
92 * @param e2 the second entry
93 * @return true, if the entries are equal, false otherwise.
94 */
95 public abstract boolean isEqualEntry(T e1, T e2);
96
97 /**
98 * Handles method dispatches from {@see TableModel#setValueAt(Object, int, int)}.
99 *
100 * @param model the table model
101 * @param value the value to be set
102 * @param row the row index
103 * @param col the column index
104 *
105 * @see TableModel#setValueAt(Object, int, int)
106 */
107 protected abstract void setValueAt(DefaultTableModel model, Object value, int row, int col);
108
109
110
111 protected void buildMyEntriesTableModel() {
112 myEntriesTableModel = new EntriesTableModel(MY_ENTRIES);
113 }
114
115 protected void buildTheirEntriesTableModel() {
116 theirEntriesTableModel = new EntriesTableModel(THEIR_ENTRIES);
117 }
118
119 protected void buildMergedEntriesTableModel() {
120 mergedEntriesTableModel = new EntriesTableModel(MERGED_ENTRIES);
121 }
122
123 protected ArrayList<T> getMergedEntries() {
124 return entries.get(MERGED_ENTRIES);
125 }
126 protected ArrayList<T> getMyEntries() {
127 return entries.get(MY_ENTRIES);
128 }
129 protected ArrayList<T> getTheirEntries() {
130 return entries.get(THEIR_ENTRIES);
131 }
132
133 public int getMyEntriesSize() {
134 return getMyEntries().size();
135 }
136
137 public int getMergedEntriesSize() {
138 return getMergedEntries().size();
139 }
140
141 public int getTheirEntriesSize() {
142 return getTheirEntries().size();
143 }
144
145 public ListMergeModel() {
146 entries = new HashMap<ListRole, ArrayList<T>>();
147 for (ListRole role : ListRole.values()) {
148 entries.put(role, new ArrayList<T>());
149 }
150
151 buildMyEntriesTableModel();
152 buildTheirEntriesTableModel();
153 buildMergedEntriesTableModel();
154
155 myEntriesSelectionModel = new EntriesSelectionModel(entries.get(MY_ENTRIES));
156 theirEntriesSelectionModel = new EntriesSelectionModel(entries.get(THEIR_ENTRIES));
157 mergedEntriesSelectionModel = new EntriesSelectionModel(entries.get(MERGED_ENTRIES));
158
159 listeners = new ArrayList<PropertyChangeListener>();
160 comparePairListModel = new ComparePairListModel();
161
162 setFrozen(true);
163 }
164
165 public void addPropertyChangeListener(PropertyChangeListener listener) {
166 synchronized(listeners) {
167 if (listener != null && ! listeners.contains(listener)) {
168 listeners.add(listener);
169 }
170 }
171 }
172
173 public void removePropertyChangeListener(PropertyChangeListener listener) {
174 synchronized(listeners) {
175 if (listener != null && listeners.contains(listener)) {
176 listeners.remove(listener);
177 }
178 }
179 }
180
181 protected void fireFrozenChanged(boolean oldValue, boolean newValue) {
182 synchronized(listeners) {
183 PropertyChangeEvent evt = new PropertyChangeEvent(this, FROZEN_PROP, oldValue, newValue);
184 for (PropertyChangeListener listener: listeners) {
185 listener.propertyChange(evt);
186 }
187 }
188 }
189
190 public void setFrozen(boolean isFrozen) {
191 boolean oldValue = this.isFrozen;
192 this.isFrozen = isFrozen;
193 fireFrozenChanged(oldValue, this.isFrozen);
194 }
195
196 public boolean isFrozen() {
197 return isFrozen;
198 }
199
200 public TableModel getMyTableModel() {
201 return myEntriesTableModel;
202 }
203
204 public TableModel getTheirTableModel() {
205 return theirEntriesTableModel;
206 }
207
208 public TableModel getMergedTableModel() {
209 return mergedEntriesTableModel;
210 }
211
212 public EntriesSelectionModel getMySelectionModel() {
213 return myEntriesSelectionModel;
214 }
215
216 public EntriesSelectionModel getTheirSelectionModel() {
217 return theirEntriesSelectionModel;
218 }
219
220 public EntriesSelectionModel getMergedSelectionModel() {
221 return mergedEntriesSelectionModel;
222 }
223
224 protected void fireModelDataChanged() {
225 myEntriesTableModel.fireTableDataChanged();
226 theirEntriesTableModel.fireTableDataChanged();
227 mergedEntriesTableModel.fireTableDataChanged();
228 setChanged();
229 notifyObservers();
230 }
231
232 protected void copyToTop(ListRole role, int []rows) {
233 if (rows == null || rows.length == 0)
234 return;
235 for (int i = rows.length - 1; i >= 0; i--) {
236 int row = rows[i];
237 T n = entries.get(role).get(row);
238 entries.get(MERGED_ENTRIES).add(0, cloneEntryForMergedList(n));
239 }
240 fireModelDataChanged();
241 mergedEntriesSelectionModel.setSelectionInterval(0, rows.length -1);
242 }
243
244 /**
245 * Copies the nodes given by indices in rows from the list of my nodes to the
246 * list of merged nodes. Inserts the nodes at the top of the list of merged
247 * nodes.
248 *
249 * @param rows the indices
250 */
251 public void copyMyToTop(int [] rows) {
252 copyToTop(MY_ENTRIES, rows);
253 }
254
255 /**
256 * Copies the nodes given by indices in rows from the list of their nodes to the
257 * list of merged nodes. Inserts the nodes at the top of the list of merged
258 * nodes.
259 *
260 * @param rows the indices
261 */
262 public void copyTheirToTop(int [] rows) {
263 copyToTop(THEIR_ENTRIES, rows);
264 }
265
266 /**
267 * Copies the nodes given by indices in rows from the list of nodes in source to the
268 * list of merged nodes. Inserts the nodes at the end of the list of merged
269 * nodes.
270 *
271 * @param source the list of nodes to copy from
272 * @param rows the indices
273 */
274
275 public void copyToEnd(ListRole source, int [] rows) {
276 if (rows == null || rows.length == 0)
277 return;
278 ArrayList<T> mergedEntries = getMergedEntries();
279 for (int row : rows) {
280 T n = entries.get(source).get(row);
281 mergedEntries.add(cloneEntryForMergedList(n));
282 }
283 fireModelDataChanged();
284 mergedEntriesSelectionModel.setSelectionInterval(mergedEntries.size()-rows.length, mergedEntries.size() -1);
285
286 }
287
288 /**
289 * Copies the nodes given by indices in rows from the list of my nodes to the
290 * list of merged nodes. Inserts the nodes at the end of the list of merged
291 * nodes.
292 *
293 * @param rows the indices
294 */
295 public void copyMyToEnd(int [] rows) {
296 copyToEnd(MY_ENTRIES, rows);
297 }
298
299 /**
300 * Copies the nodes given by indices in rows from the list of their nodes to the
301 * list of merged nodes. Inserts the nodes at the end of the list of merged
302 * nodes.
303 *
304 * @param rows the indices
305 */
306 public void copyTheirToEnd(int [] rows) {
307 copyToEnd(THEIR_ENTRIES, rows);
308 }
309
310 /**
311 * Copies the nodes given by indices in rows from the list of nodes <code>source</code> to the
312 * list of merged nodes. Inserts the nodes before row given by current.
313 *
314 * @param source the list of nodes to copy from
315 * @param rows the indices
316 * @param current the row index before which the nodes are inserted
317 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
318 *
319 */
320 protected void copyBeforeCurrent(ListRole source, int [] rows, int current) {
321 if (rows == null || rows.length == 0)
322 return;
323 ArrayList<T> mergedEntries = getMergedEntries();
324 if (current < 0 || current >= mergedEntries.size())
325 throw new IllegalArgumentException(tr("parameter current out of range: got {0}", current));
326 for (int i=rows.length -1; i>=0; i--) {
327 int row = rows[i];
328 T n = entries.get(source).get(row);
329 mergedEntries.add(current, cloneEntryForMergedList(n));
330 }
331 fireModelDataChanged();
332 mergedEntriesSelectionModel.setSelectionInterval(current, current + rows.length-1);
333 }
334
335 /**
336 * Copies the nodes given by indices in rows from the list of my nodes to the
337 * list of merged nodes. Inserts the nodes before row given by current.
338 *
339 * @param rows the indices
340 * @param current the row index before which the nodes are inserted
341 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
342 *
343 */
344 public void copyMyBeforeCurrent(int [] rows, int current) {
345 copyBeforeCurrent(MY_ENTRIES,rows,current);
346 }
347
348 /**
349 * Copies the nodes given by indices in rows from the list of their nodes to the
350 * list of merged nodes. Inserts the nodes before row given by current.
351 *
352 * @param rows the indices
353 * @param current the row index before which the nodes are inserted
354 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
355 *
356 */
357 public void copyTheirBeforeCurrent(int [] rows, int current) {
358 copyBeforeCurrent(THEIR_ENTRIES,rows,current);
359 }
360
361 /**
362 * Copies the nodes given by indices in rows from the list of nodes <code>source</code> to the
363 * list of merged nodes. Inserts the nodes after the row given by current.
364 *
365 * @param source the list of nodes to copy from
366 * @param rows the indices
367 * @param current the row index after which the nodes are inserted
368 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
369 *
370 */
371 protected void copyAfterCurrent(ListRole source, int [] rows, int current) {
372 if (rows == null || rows.length == 0)
373 return;
374 ArrayList<T> mergedEntries = getMergedEntries();
375
376 if (current < 0 || current >= mergedEntries.size())
377 throw new IllegalArgumentException(tr("parameter current out of range: got {0}", current));
378 if (current == mergedEntries.size() -1) {
379 copyToEnd(source, rows);
380 } else {
381 for (int i=rows.length -1; i>=0; i--) {
382 int row = rows[i];
383 T n = entries.get(source).get(row);
384 mergedEntries.add(current+1, cloneEntryForMergedList(n));
385 }
386 }
387 fireModelDataChanged();
388 mergedEntriesSelectionModel.setSelectionInterval(current+1, current + rows.length-1);
389 notifyObservers();
390 }
391
392 /**
393 * Copies the nodes given by indices in rows from the list of my nodes to the
394 * list of merged nodes. Inserts the nodes after the row given by current.
395 *
396 * @param rows the indices
397 * @param current the row index after which the nodes are inserted
398 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
399 *
400 */
401 public void copyMyAfterCurrent(int [] rows, int current) {
402 copyAfterCurrent(MY_ENTRIES, rows, current);
403 }
404
405 /**
406 * Copies the nodes given by indices in rows from the list of my nodes to the
407 * list of merged nodes. Inserts the nodes after the row given by current.
408 *
409 * @param rows the indices
410 * @param current the row index after which the nodes are inserted
411 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
412 *
413 */
414 public void copyTheirAfterCurrent(int [] rows, int current) {
415 copyAfterCurrent(THEIR_ENTRIES, rows, current);
416 }
417
418 /**
419 * Moves the nodes given by indices in rows up by one position in the list
420 * of merged nodes.
421 *
422 * @param rows the indices
423 *
424 */
425 public void moveUpMerged(int [] rows) {
426 if (rows == null || rows.length == 0)
427 return;
428 if (rows[0] == 0)
429 // can't move up
430 return;
431 ArrayList<T> mergedEntries = getMergedEntries();
432 for (int row: rows) {
433 T n = mergedEntries.get(row);
434 mergedEntries.remove(row);
435 mergedEntries.add(row -1, n);
436 }
437 fireModelDataChanged();
438 notifyObservers();
439 mergedEntriesSelectionModel.clearSelection();
440 for (int row: rows) {
441 mergedEntriesSelectionModel.addSelectionInterval(row-1, row-1);
442 }
443 }
444
445 /**
446 * Moves the nodes given by indices in rows down by one position in the list
447 * of merged nodes.
448 *
449 * @param rows the indices
450 */
451 public void moveDownMerged(int [] rows) {
452 if (rows == null || rows.length == 0)
453 return;
454 ArrayList<T> mergedEntries = getMergedEntries();
455 if (rows[rows.length -1] == mergedEntries.size() -1)
456 // can't move down
457 return;
458 for (int i = rows.length-1; i>=0;i--) {
459 int row = rows[i];
460 T n = mergedEntries.get(row);
461 mergedEntries.remove(row);
462 mergedEntries.add(row +1, n);
463 }
464 fireModelDataChanged();
465 notifyObservers();
466 mergedEntriesSelectionModel.clearSelection();
467 for (int row: rows) {
468 mergedEntriesSelectionModel.addSelectionInterval(row+1, row+1);
469 }
470 }
471
472 /**
473 * Removes the nodes given by indices in rows from the list
474 * of merged nodes.
475 *
476 * @param rows the indices
477 */
478 public void removeMerged(int [] rows) {
479 if (rows == null || rows.length == 0)
480 return;
481
482 ArrayList<T> mergedEntries = getMergedEntries();
483
484 for (int i = rows.length-1; i>=0;i--) {
485 mergedEntries.remove(rows[i]);
486 }
487 fireModelDataChanged();
488 notifyObservers();
489 mergedEntriesSelectionModel.clearSelection();
490 }
491
492
493 /**
494 * Replies true if the list of my entries and the list of their
495 * entries are equal
496 *
497 * @return true, if the lists are equal; false otherwise
498 */
499 protected boolean myAndTheirEntriesEqual() {
500
501 if (getMyEntries().size() != getTheirEntries().size())
502 return false;
503 for (int i=0; i < getMyEntries().size(); i++) {
504 if (! isEqualEntry(getMyEntries().get(i), getTheirEntries().get(i)))
505 return false;
506 }
507 return true;
508 }
509
510
511 /**
512 * This an adapter between a {@see JTable} and one of the three entry lists
513 * in the role {@see ListRole} managed by the {@see ListMergeModel}.
514 *
515 * From the point of view of the {@see JTable} it is a {@see TableModel}.
516 *
517 * @param <T>
518 * @see ListMergeModel#getMyTableModel()
519 * @see ListMergeModel#getTheirTableModel()
520 * @see ListMergeModel#getMergedTableModel()
521 */
522 public class EntriesTableModel extends DefaultTableModel {
523 private final ListRole role;
524
525 /**
526 *
527 * @param role the role
528 */
529 public EntriesTableModel(ListRole role) {
530 this.role = role;
531 }
532
533 @Override
534 public int getRowCount() {
535 int count = Math.max(getMyEntries().size(), getMergedEntries().size());
536 count = Math.max(count, getTheirEntries().size());
537 return count;
538 }
539
540 @Override
541 public Object getValueAt(int row, int column) {
542 if (row < entries.get(role).size())
543 return entries.get(role).get(row);
544 return null;
545 }
546
547 @Override
548 public boolean isCellEditable(int row, int column) {
549 return false;
550 }
551
552 @Override
553 public void setValueAt(Object value, int row, int col) {
554 ListMergeModel.this.setValueAt(this, value,row,col);
555 }
556
557 public ListMergeModel getListMergeModel() {
558 return ListMergeModel.this;
559 }
560
561 /**
562 * replies true if the {@see ListRole} of this {@see EntriesTableModel}
563 * participates in the current {@see ComparePairType}
564 *
565 * @return true, if the if the {@see ListRole} of this {@see EntriesTableModel}
566 * participates in the current {@see ComparePairType}
567 *
568 * @see ComparePairListModel#getSelectedComparePair()
569 */
570 public boolean isParticipatingInCurrentComparePair() {
571 return getComparePairListModel()
572 .getSelectedComparePair()
573 .isParticipatingIn(role);
574 }
575
576 /**
577 * replies true if the entry at <code>row</code> is equal to the entry at the
578 * same position in the opposite list of the current {@see ComparePairType}.
579 *
580 * @param row the row number
581 * @return true if the entry at <code>row</code> is equal to the entry at the
582 * same position in the opposite list of the current {@see ComparePairType}
583 * @exception IllegalStateException thrown, if this model is not participating in the
584 * current {@see ComparePairType}
585 * @see ComparePairType#getOppositeRole(ListRole)
586 * @see #getRole()
587 * @see #getOppositeEntries()
588 */
589 public boolean isSamePositionInOppositeList(int row) {
590 if (!isParticipatingInCurrentComparePair())
591 throw new IllegalStateException(tr("list in role {0} is currently not participating in a compare pair", role.toString()));
592 if (row >= getEntries().size()) return false;
593 if (row >= getOppositeEntries().size()) return false;
594
595 T e1 = getEntries().get(row);
596 T e2 = getOppositeEntries().get(row);
597 return isEqualEntry(e1, e2);
598 }
599
600 /**
601 * replies true if the entry at the current position is present in the opposite list
602 * of the current {@see ComparePairType}.
603 *
604 * @param row the current row
605 * @return true if the entry at the current position is present in the opposite list
606 * of the current {@see ComparePairType}.
607 * @exception IllegalStateException thrown, if this model is not participating in the
608 * current {@see ComparePairType}
609 * @see ComparePairType#getOppositeRole(ListRole)
610 * @see #getRole()
611 * @see #getOppositeEntries()
612 */
613 public boolean isIncludedInOppositeList(int row) {
614 if (!isParticipatingInCurrentComparePair())
615 throw new IllegalStateException(tr("list in role {0} is currently not participating in a compare pair", role.toString()));
616
617 if (row >= getEntries().size()) return false;
618 T e1 = getEntries().get(row);
619 for (T e2: getOppositeEntries()) {
620 if (isEqualEntry(e1, e2)) return true;
621 }
622 return false;
623 }
624
625 protected ArrayList<T> getEntries() {
626 return entries.get(role);
627 }
628
629 /**
630 * replies the opposite list of entries with respect to the current {@see ComparePairType}
631 *
632 * @return the opposite list of entries
633 */
634 protected ArrayList<T> getOppositeEntries() {
635 ListRole opposite = getComparePairListModel().getSelectedComparePair().getOppositeRole(role);
636 return entries.get(opposite);
637 }
638
639 public ListRole getRole() {
640 return role;
641 }
642 }
643
644 /**
645 * This is the selection model to be used in a {@see JTable} which displays
646 * an entry list managed by {@see ListMergeModel}.
647 *
648 * The model ensures that only rows displaying an entry in the entry list
649 * can be selected. "Empty" rows can't be selected.
650 *
651 * @see ListMergeModel#getMySelectionModel()
652 * @see ListMergeModel#getMergedSelectionModel()
653 * @see ListMergeModel#getTheirSelectionModel()
654 *
655 */
656 protected class EntriesSelectionModel extends DefaultListSelectionModel {
657 private final ArrayList<T> entries;
658
659 public EntriesSelectionModel(ArrayList<T> nodes) {
660 this.entries = nodes;
661 }
662
663 @Override
664 public void addSelectionInterval(int index0, int index1) {
665 if (entries.isEmpty()) return;
666 if (index0 > entries.size() - 1) return;
667 index0 = Math.min(entries.size()-1, index0);
668 index1 = Math.min(entries.size()-1, index1);
669 super.addSelectionInterval(index0, index1);
670 }
671
672 @Override
673 public void insertIndexInterval(int index, int length, boolean before) {
674 if (entries.isEmpty()) return;
675 if (before) {
676 int newindex = Math.min(entries.size()-1, index);
677 if (newindex < index - length) return;
678 length = length - (index - newindex);
679 super.insertIndexInterval(newindex, length, before);
680 } else {
681 if (index > entries.size() -1) return;
682 length = Math.min(entries.size()-1 - index, length);
683 super.insertIndexInterval(index, length, before);
684 }
685 }
686
687 @Override
688 public void moveLeadSelectionIndex(int leadIndex) {
689 if (entries.isEmpty()) return;
690 leadIndex = Math.max(0, leadIndex);
691 leadIndex = Math.min(entries.size() - 1, leadIndex);
692 super.moveLeadSelectionIndex(leadIndex);
693 }
694
695 @Override
696 public void removeIndexInterval(int index0, int index1) {
697 if (entries.isEmpty()) return;
698 index0 = Math.max(0, index0);
699 index0 = Math.min(entries.size() - 1, index0);
700
701 index1 = Math.max(0, index1);
702 index1 = Math.min(entries.size() - 1, index1);
703 super.removeIndexInterval(index0, index1);
704 }
705
706 @Override
707 public void removeSelectionInterval(int index0, int index1) {
708 if (entries.isEmpty()) return;
709 index0 = Math.max(0, index0);
710 index0 = Math.min(entries.size() - 1, index0);
711
712 index1 = Math.max(0, index1);
713 index1 = Math.min(entries.size() - 1, index1);
714 super.removeSelectionInterval(index0, index1);
715 }
716
717 @Override
718 public void setAnchorSelectionIndex(int anchorIndex) {
719 if (entries.isEmpty()) return;
720 anchorIndex = Math.min(entries.size() - 1, anchorIndex);
721 super.setAnchorSelectionIndex(anchorIndex);
722 }
723
724 @Override
725 public void setLeadSelectionIndex(int leadIndex) {
726 if (entries.isEmpty()) return;
727 leadIndex = Math.min(entries.size() - 1, leadIndex);
728 super.setLeadSelectionIndex(leadIndex);
729 }
730
731 @Override
732 public void setSelectionInterval(int index0, int index1) {
733 if (entries.isEmpty()) return;
734 index0 = Math.max(0, index0);
735 index0 = Math.min(entries.size() - 1, index0);
736
737 index1 = Math.max(0, index1);
738 index1 = Math.min(entries.size() - 1, index1);
739
740 super.setSelectionInterval(index0, index1);
741 }
742 }
743
744 public ComparePairListModel getComparePairListModel() {
745 return this.comparePairListModel;
746 }
747
748 public class ComparePairListModel extends AbstractListModel implements ComboBoxModel {
749
750 private int selectedIdx;
751 private final ArrayList<ComparePairType> compareModes;
752
753 public ComparePairListModel() {
754 this.compareModes = new ArrayList<ComparePairType>();
755 compareModes.add(MY_WITH_THEIR);
756 compareModes.add(MY_WITH_MERGED);
757 compareModes.add(THEIR_WITH_MERGED);
758 selectedIdx = 0;
759 }
760
761 public Object getElementAt(int index) {
762 if (index < compareModes.size())
763 return compareModes.get(index);
764 throw new IllegalArgumentException(tr("unexpected value of parameter \"index\". Got {0}", index));
765 }
766
767 public int getSize() {
768 return compareModes.size();
769 }
770
771 public Object getSelectedItem() {
772 return compareModes.get(selectedIdx);
773 }
774
775 public void setSelectedItem(Object anItem) {
776 int i = compareModes.indexOf(anItem);
777 if (i < 0)
778 throw new IllegalStateException(tr("item {0} not found in list", anItem));
779 selectedIdx = i;
780 fireModelDataChanged();
781 }
782
783 public ComparePairType getSelectedComparePair() {
784 return compareModes.get(selectedIdx);
785 }
786 }
787}
Note: See TracBrowser for help on using the repository browser.