/* class DefaultListSelectionModel * * Copyright (C) 2001-2003 R M Pitman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package charvax.swing; import java.util.ArrayList; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.TreeSet; import charvax.swing.event.ListSelectionEvent; import charvax.swing.event.ListSelectionListener; /** * Default data model for list selections. */ public class DefaultListSelectionModel implements ListSelectionModel { public DefaultListSelectionModel() { } /** Add a listener to the list that is notified each time a change to * the selection occurs. */ public void addListSelectionListener(ListSelectionListener l_) { _listeners.add(l_); } /** Remove a listener from the list. */ public void removeListSelectionListener(ListSelectionListener l_) { _listeners.remove(l_); } /** Set the selection mode. The following modes are allowed: * <ul> * <li> SINGLE_SELECTION. Only one list index can be selected at a time. * <li> SINGLE_INTERVAL_SELECTION. One contiguous index interval can be set * at at time. * </ul> */ public void setSelectionMode(int mode_) { _selectionMode = mode_; } /** Returns the current selection mode. */ public int getSelectionMode() { return _selectionMode; } /** Returns true if the specified index is selected. */ public boolean isSelectedIndex(int index) { return _selection.contains(new Integer(index)); } /** Returns the first selected index, or -1 if the selection is empty. */ public int getMinSelectionIndex() { try { Integer first = (Integer) _selection.first(); return first.intValue(); } catch (NoSuchElementException e) { return -1; } } /** Returns the last selected index, or -1 if the selection is empty. */ public int getMaxSelectionIndex() { try { Integer last = (Integer) _selection.last(); return last.intValue(); } catch (NoSuchElementException e) { return -1; } } /** Returns true if no indices are selected. */ public boolean isSelectionEmpty() { return _selection.isEmpty(); } /** Change the selection to be the empty set. If this represents a change * to the selection then notify each ListSelectionListener. */ public void clearSelection() { if ( ! isSelectionEmpty()) { int first = getMinSelectionIndex(); int last = getMaxSelectionIndex(); fireValueChanged(first, last); _selection.clear(); } } /** Change the selection to be the set union between the current * selection and the indices between index0 and index1 inclusive. If * this represents a change to the current selection, then notify each * ListSelectionListener. Note that index0 does not have to be less * than or equal to index1. */ public void addSelectionInterval(int index0, int index1) { TreeSet<Integer> range = getRange(index0, index1); /* Find the differences */ TreeSet<Integer> newSelection = new TreeSet<Integer>(_selection); newSelection.addAll(range); handleSelectionChange(newSelection); } /** Change the selection to be the set difference between the current * selection and the indices between index0 and index1 inclusive. If * this represents a change to the current selection, then notify each * ListSelectionListener. Note that index0 does not have to be less * than or equal to index1. */ public void removeSelectionInterval(int index0, int index1) { TreeSet<Integer> range = getRange(index0, index1); /* Find the differences */ TreeSet<Integer> newSelection = new TreeSet<Integer>(_selection); newSelection.removeAll(range); handleSelectionChange(newSelection); } /** Change the selection to be between index0 and index1 inclusive. * If this represents a change to the selection, then notify each * ListSelectionListener. Note that index0 doesn't have to be less than or * equal to index1. */ public void setSelectionInterval(int index0, int index1) { TreeSet<Integer> newSelection = getRange(index0, index1); handleSelectionChange(newSelection); } /** Insert length indices beginning before/after index, without * notifying the ListSelectionListeners. This * is typically called to sync the selection model with a * corresponding change in the data model. */ public void insertIndexInterval(int index, int length, boolean before) { if (before) { for (int i=index-length; i>=index; i++) _selection.add(new Integer(i)); } else { for (int i=index; i<=index+length; i++) _selection.add(new Integer(i)); } } /** Remove the indices in the interval index0,index1 (inclusive) * from the selection model, without notifying the ListSelectionListeners. * This is typically called to sync the * selection model with a corresponding change in the data model. */ public void removeIndexInterval(int index0, int index1) { for (int i=index0; i<=index1; i++) _selection.remove(new Integer(i)); } /** Notify the listeners that the selection has changed. * @param firstindex_ The first index in the interval * @param lastindex_ The last index in the interval. */ protected void fireValueChanged(int firstindex_, int lastindex_) { ListSelectionEvent event = new ListSelectionEvent(this, firstindex_, lastindex_, false); Iterator<ListSelectionListener> iter = _listeners.iterator(); while (iter.hasNext()) { ListSelectionListener l = (ListSelectionListener) iter.next(); l.valueChanged(event); } } /** Returns a TreeSet that contains the indices between index0 and index1 * inclusive. */ private TreeSet<Integer> getRange(int index0, int index1) { int start = 0; int end = 0; if (index0 <= index1) { start = index0; end = index1; } else { start = index1; end = index0; } TreeSet<Integer> range = new TreeSet<Integer>(); for (int i=start; i<=end; i++) { range.add(new Integer(i)); } return range; } private void handleSelectionChange(TreeSet<Integer> newSelection_) { /* Find the differences between the old selection and the new * selection. */ TreeSet<Integer> copyOld = new TreeSet<Integer>(_selection); TreeSet<Integer> differences = new TreeSet<Integer>(newSelection_); differences.removeAll(_selection); copyOld.removeAll(newSelection_); differences.addAll(copyOld); /* We must set the new selection before calling "valueChanged" * so that the "valueChanged" method sees the correct new selection. */ _selection = newSelection_; if ( ! differences.isEmpty()) { Integer first = (Integer) differences.first(); Integer last = (Integer) differences.last(); fireValueChanged(first.intValue(), last.intValue()); } } //==================================================================== // INSTANCE VARIABLES /** The list of listeners. */ protected ArrayList<ListSelectionListener> _listeners = new ArrayList<ListSelectionListener>(); /** The set of selected indices. */ protected TreeSet<Integer> _selection = new TreeSet<Integer>(); private int _selectionMode = ListSelectionModel.SINGLE_SELECTION; }