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

Revision 4191, 28.6 KB checked in by stoecker, 11 months ago (diff)

remove old debug stuff

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