KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > barracuda > core > comp > DefaultListSelectionModel


1 /*
2  * Copyright (C) 2003 Christian Cryder [christianc@granitepeaks.com]
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * $Id: DefaultListSelectionModel.java,v 1.9 2004/02/01 05:16:27 christianc Exp $
19  */

20 package org.enhydra.barracuda.core.comp;
21
22 import java.io.*;
23 import java.util.*;
24
25 import org.enhydra.barracuda.core.comp.model.*;
26
27 /**
28  * Default data model implementation for list selections.
29  */

30 public class DefaultListSelectionModel implements ListSelectionModel, Cloneable JavaDoc, Serializable {
31
32     private static final int MIN = -1;
33     private static final int MAX = Integer.MAX_VALUE;
34     private int selectionMode = SINGLE_SELECTION;
35     private int minIndex = MAX;
36     private int maxIndex = MIN;
37     private int firstAdjustedIndex = MAX;
38     private int lastAdjustedIndex = MIN;
39     private BitSet value = new BitSet(32);
40     protected List listeners = new ArrayList();
41
42     //--------------- ListSelectionModel -------------------------
43
/**
44      * Add a listener to the template that's notified each time a change
45      * to the data model occurs.
46      *
47      * @param ml the TemplateModelListener
48      */

49     public void addModelListener(ModelListener ml) {
50         listeners.add(ml);
51     }
52
53     /**
54      * Remove a listener
55      *
56      * @param ml the TemplateModelListener
57      */

58     public void removeModelListener(ModelListener ml) {
59         listeners.remove(ml);
60     }
61     
62     /**
63      * Forwards the given notification event to all
64      * <code>TemplateModelListeners</code> that registered
65      * themselves as listeners for this template model.
66      */

67     public void fireModelChanged() {
68         Iterator it = listeners.iterator();
69         ModelListener ml = null;
70         while (it.hasNext()) {
71             ml = (ModelListener) it.next();
72             ml.modelChanged(this);
73         }
74     }
75
76     /**
77      * Sets the selection mode. The default is
78      * SINGLE_SELECTION.
79      * @param selectionMode one of three values:
80      * <ul>
81      * <li>SINGLE_SELECTION</li>
82      * <li>SINGLE_INTERVAL_SELECTION</li>
83      * <li>MULTIPLE_INTERVAL_SELECTION</li>
84      * </ul>
85      * @exception IllegalArgumentException if <code>selectionMode</code>
86      * is not one of the legal values shown above
87      * @see #setSelectionMode
88      */

89     public void setSelectionMode(int selectionMode) {
90         switch (selectionMode) {
91         case SINGLE_SELECTION:
92         case SINGLE_INTERVAL_SELECTION:
93         case MULTIPLE_INTERVAL_SELECTION:
94             this.selectionMode = selectionMode;
95             break;
96         default:
97             throw new IllegalArgumentException JavaDoc("invalid selectionMode");
98         }
99     }
100
101     /**
102      * Returns the selection mode.
103      * @return one of the these values:
104      * <ul>
105      * <li>SINGLE_SELECTION</li>
106      * <li>SINGLE_INTERVAL_SELECTION</li>
107      * <li>MULTIPLE_INTERVAL_SELECTION</li>
108      * </ul>
109      * @see #getSelectionMode
110      */

111     public int getSelectionMode() {
112         return selectionMode;
113     }
114
115     /**
116      * Change the selection to be between index0 and index1 inclusive.
117      * If this represents a change to the current selection, then
118      * notify each ListSelectionListener. Note that index0 doesn't have
119      * to be less than or equal to index1.
120      *
121      * @param index0 one end of the interval.
122      * @param index1 other end of the interval
123      */

124     public void setSelectionInterval(int index0, int index1) {
125         if (index0==-1 || index1==-1) return;
126
127         if (getSelectionMode()==SINGLE_SELECTION) index0 = index1;
128
129         int clearMin = minIndex;
130         int clearMax = maxIndex;
131         int setMin = Math.min(index0, index1);
132         int setMax = Math.max(index0, index1);
133         changeSelection(clearMin, clearMax, setMin, setMax);
134     }
135
136     /**
137      * Change the selection to be the set union of the current selection
138      * and the indices between index0 and index1 inclusive. If this represents
139      * a change to the current selection, then notify each
140      * ListSelectionListener. Note that index0 doesn't have to be less
141      * than or equal to index1.
142      *
143      * @param index0 one end of the interval.
144      * @param index1 other end of the interval
145      */

146     public void addSelectionInterval(int index0, int index1) {
147         if (index0==-1 || index1==-1) return;
148
149         if (getSelectionMode()!=MULTIPLE_INTERVAL_SELECTION) {
150             setSelectionInterval(index0, index1);
151             return;
152         }
153
154         int clearMin = MAX;
155         int clearMax = MIN;
156         int setMin = Math.min(index0, index1);
157         int setMax = Math.max(index0, index1);
158         changeSelection(clearMin, clearMax, setMin, setMax);
159     }
160
161     /**
162      * Change the selection to be the set difference of the current selection
163      * and the indices between index0 and index1 inclusive. If this represents
164      * a change to the current selection, then notify each
165      * ListSelectionListener. Note that index0 doesn't have to be less
166      * than or equal to index1.
167      *
168      * @param index0 one end of the interval.
169      * @param index1 other end of the interval
170      */

171     public void removeSelectionInterval(int index0, int index1) {
172         if (index0==-1 || index1==-1) return;
173
174         int clearMin = Math.min(index0, index1);
175         int clearMax = Math.max(index0, index1);
176         int setMin = MAX;
177         int setMax = MIN;
178
179         // If the removal would produce to two disjoint selections in a mode
180
// that only allows one, extend the removal to the end of the selection.
181
if (getSelectionMode()!=MULTIPLE_INTERVAL_SELECTION &&
182             clearMin>minIndex && clearMax<maxIndex) {
183             clearMax = maxIndex;
184         }
185
186         changeSelection(clearMin, clearMax, setMin, setMax);
187     }
188
189     /**
190      * Returns the first selected index or -1 if the selection is empty.
191      */

192     public int getMinSelectionIndex() {
193         return isSelectionEmpty() ? -1 : minIndex;
194     }
195
196     /**
197      * Returns the last selected index or -1 if the selection is empty.
198      */

199     public int getMaxSelectionIndex() {
200         return maxIndex;
201     }
202
203     /**
204      * Returns true if the specified index is selected.
205      */

206     public boolean isSelectedIndex(int index) {
207         return ((index < minIndex) || (index > maxIndex)) ? false : value.get(index);
208     }
209
210     /**
211      * Returns true if no indices are selected.
212      */

213     public boolean isSelectionEmpty() {
214         return (minIndex > maxIndex);
215     }
216
217     /**
218      * Change the selection to the empty set. If this represents
219      * a change to the current selection then notify each ListSelectionListener.
220      */

221     public void clearSelection() {
222         removeSelectionInterval(minIndex, maxIndex);
223     }
224
225     // Updates first and last change indices
226
private void markAsDirty(int r) {
227         firstAdjustedIndex = Math.min(firstAdjustedIndex, r);
228         lastAdjustedIndex = Math.max(lastAdjustedIndex, r);
229     }
230
231     // Sets the state at this index and update all relevant state.
232
private void set(int r) {
233         if (value.get(r)) return;
234         value.set(r);
235         markAsDirty(r);
236
237         // Update minimum and maximum indices
238
minIndex = Math.min(minIndex, r);
239         maxIndex = Math.max(maxIndex, r);
240     }
241
242     // Clears the state at this index and update all relevant state.
243
private void clear(int r) {
244         if (!value.get(r)) return;
245         value.clear(r);
246         markAsDirty(r);
247
248         // Update minimum and maximum indices
249
//
250
// If (r > minIndex) the minimum has not changed.
251
// The case (r < minIndex) is not possible because r'th value was set.
252
// We only need to check for the case when lowest entry has been cleared,
253
// and in this case we need to search for the first value set above it.
254
if (r==minIndex) {
255             for (minIndex=minIndex+1; minIndex<=maxIndex; minIndex++) {
256                 if (value.get(minIndex)) {
257                     break;
258                 }
259             }
260         }
261         
262         //If (r < maxIndex) the maximum has not changed.
263
//The case (r > maxIndex) is not possible because r'th value was set.
264
//We only need to check for the case when highest entry has been cleared,
265
//and in this case we need to search for the first value set below it.
266
if (r==maxIndex) {
267             for (maxIndex=maxIndex-1; minIndex<=maxIndex; maxIndex--) {
268                 if (value.get(maxIndex)) {
269                     break;
270                 }
271             }
272         }
273         
274         //Performance note: This method is called from inside a loop in
275
//changeSelection() but we will only iterate in the loops
276
//above on the basis of one iteration per deselected cell - in total.
277
//Ie. the next time this method is called the work of the previous
278
//deselection will not be repeated.
279
//
280
//We also don't need to worry about the case when the min and max
281
//values are in their unassigned states. This cannot happen because
282
//this method's initial check ensures that the selection was not empty
283
//and therefore that the minIndex and maxIndex had 'real' values.
284
//
285
//If we have cleared the whole selection, set the minIndex and maxIndex
286
//to their cannonical values so that the next set command always works
287
//just by using Math.min and Math.max.
288
if (isSelectionEmpty()) {
289             minIndex = MAX;
290             maxIndex = MIN;
291         }
292     }
293
294     private boolean contains(int a, int b, int i) {
295         return (i>=a) && (i<=b);
296     }
297
298     private void changeSelection(int clearMin, int clearMax, int setMin, int setMax, boolean clearFirst) {
299         for (int i=Math.min(setMin, clearMin); i<=Math.max(setMax, clearMax); i++) {
300             boolean shouldClear = contains(clearMin, clearMax, i);
301             boolean shouldSet = contains(setMin, setMax, i);
302
303             if (shouldSet && shouldClear) {
304                 if (clearFirst) shouldClear = false;
305                 else shouldSet = false;
306             }
307
308             if (shouldSet) set(i);
309             if (shouldClear) clear(i);
310         }
311         fireModelChanged();
312     }
313
314     /**
315      * Change the selection with the effect of first clearing the values
316      * in the inclusive range [clearMin, clearMax] then setting the values
317      * in the inclusive range [setMin, setMax]. Do this in one pass so
318      * that no values are cleared if they would later be set.
319      */

320     private void changeSelection(int clearMin, int clearMax, int setMin, int setMax) {
321         changeSelection(clearMin, clearMax, setMin, setMax, true);
322     }
323
324     private void setState(int index, boolean state) {
325         if (state) set(index);
326         else clear(index);
327     }
328
329     /**
330      * Returns a string that displays and identifies this
331      * object's properties.
332      *
333      * @return a <code>String</code> representation of this object
334      */

335     public String JavaDoc toString() {
336         String JavaDoc s = "="+value.toString();
337         return getClass().getName()+" "+Integer.toString(hashCode())+" "+s;
338     }
339
340     /**
341      * Returns a clone of this selection model with the same selection.
342      * <code>listenerLists</code> are not duplicated.
343      *
344      * @exception CloneNotSupportedException if the selection model does not
345      * both (a) implement the Cloneable interface and (b) define a
346      * <code>clone</code> method.
347      */

348     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
349         DefaultListSelectionModel clone = (DefaultListSelectionModel)super.clone();
350         clone.value = (BitSet)value.clone();
351         clone.listeners = new ArrayList();
352         return clone;
353     }
354
355 }
356
Popular Tags