KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > DefaultListSelectionModel


1 /*
2  * @(#)DefaultListSelectionModel.java 1.75 04/05/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing;
9
10 import java.util.EventListener JavaDoc;
11 import java.util.BitSet JavaDoc;
12 import java.io.Serializable JavaDoc;
13
14 import javax.swing.event.*;
15
16
17 /**
18  * Default data model for list selections.
19  * <p>
20  * <strong>Warning:</strong>
21  * Serialized objects of this class will not be compatible with
22  * future Swing releases. The current serialization support is
23  * appropriate for short term storage or RMI between applications running
24  * the same version of Swing. As of 1.4, support for long term storage
25  * of all JavaBeans<sup><font size="-2">TM</font></sup>
26  * has been added to the <code>java.beans</code> package.
27  * Please see {@link java.beans.XMLEncoder}.
28  *
29  * @version 1.75 05/05/04
30  * @author Philip Milne
31  * @author Hans Muller
32  * @see ListSelectionModel
33  */

34
35 public class DefaultListSelectionModel implements ListSelectionModel JavaDoc, Cloneable JavaDoc, Serializable JavaDoc
36 {
37     private static final int MIN = -1;
38     private static final int MAX = Integer.MAX_VALUE;
39     private int selectionMode = MULTIPLE_INTERVAL_SELECTION;
40     private int minIndex = MAX;
41     private int maxIndex = MIN;
42     private int anchorIndex = -1;
43     private int leadIndex = -1;
44     private int firstAdjustedIndex = MAX;
45     private int lastAdjustedIndex = MIN;
46     private boolean isAdjusting = false;
47
48     private int firstChangedIndex = MAX;
49     private int lastChangedIndex = MIN;
50
51     private BitSet JavaDoc value = new BitSet JavaDoc(32);
52     protected EventListenerList listenerList = new EventListenerList();
53
54     protected boolean leadAnchorNotificationEnabled = true;
55
56     // implements javax.swing.ListSelectionModel
57
public int getMinSelectionIndex() { return isSelectionEmpty() ? -1 : minIndex; }
58
59     // implements javax.swing.ListSelectionModel
60
public int getMaxSelectionIndex() { return maxIndex; }
61
62     // implements javax.swing.ListSelectionModel
63
public boolean getValueIsAdjusting() { return isAdjusting; }
64
65     // implements javax.swing.ListSelectionModel
66
/**
67      * Returns the selection mode.
68      * @return one of the these values:
69      * <ul>
70      * <li>SINGLE_SELECTION
71      * <li>SINGLE_INTERVAL_SELECTION
72      * <li>MULTIPLE_INTERVAL_SELECTION
73      * </ul>
74      * @see #getSelectionMode
75      */

76     public int getSelectionMode() { return selectionMode; }
77
78     // implements javax.swing.ListSelectionModel
79
/**
80      * Sets the selection mode. The default is
81      * MULTIPLE_INTERVAL_SELECTION.
82      * @param selectionMode one of three values:
83      * <ul>
84      * <li>SINGLE_SELECTION
85      * <li>SINGLE_INTERVAL_SELECTION
86      * <li>MULTIPLE_INTERVAL_SELECTION
87      * </ul>
88      * @exception IllegalArgumentException if <code>selectionMode</code>
89      * is not one of the legal values shown above
90      * @see #setSelectionMode
91      */

92     public void setSelectionMode(int selectionMode) {
93     switch (selectionMode) {
94     case SINGLE_SELECTION:
95     case SINGLE_INTERVAL_SELECTION:
96     case MULTIPLE_INTERVAL_SELECTION:
97         this.selectionMode = selectionMode;
98         break;
99     default:
100         throw new IllegalArgumentException JavaDoc("invalid selectionMode");
101     }
102     }
103
104     // implements javax.swing.ListSelectionModel
105
public boolean isSelectedIndex(int index) {
106     return ((index < minIndex) || (index > maxIndex)) ? false : value.get(index);
107     }
108
109     // implements javax.swing.ListSelectionModel
110
public boolean isSelectionEmpty() {
111     return (minIndex > maxIndex);
112     }
113
114     // implements javax.swing.ListSelectionModel
115
public void addListSelectionListener(ListSelectionListener l) {
116     listenerList.add(ListSelectionListener.class, l);
117     }
118
119     // implements javax.swing.ListSelectionModel
120
public void removeListSelectionListener(ListSelectionListener l) {
121     listenerList.remove(ListSelectionListener.class, l);
122     }
123
124     /**
125      * Returns an array of all the list selection listeners
126      * registered on this <code>DefaultListSelectionModel</code>.
127      *
128      * @return all of this model's <code>ListSelectionListener</code>s
129      * or an empty
130      * array if no list selection listeners are currently registered
131      *
132      * @see #addListSelectionListener
133      * @see #removeListSelectionListener
134      *
135      * @since 1.4
136      */

137     public ListSelectionListener[] getListSelectionListeners() {
138         return (ListSelectionListener[])listenerList.getListeners(
139                 ListSelectionListener.class);
140     }
141
142     /**
143      * Notifies listeners that we have ended a series of adjustments.
144      */

145     protected void fireValueChanged(boolean isAdjusting) {
146         if (lastChangedIndex == MIN) {
147         return;
148     }
149     /* Change the values before sending the event to the
150      * listeners in case the event causes a listener to make
151      * another change to the selection.
152      */

153     int oldFirstChangedIndex = firstChangedIndex;
154     int oldLastChangedIndex = lastChangedIndex;
155     firstChangedIndex = MAX;
156     lastChangedIndex = MIN;
157     fireValueChanged(oldFirstChangedIndex, oldLastChangedIndex, isAdjusting);
158     }
159
160
161     /**
162      * Notifies <code>ListSelectionListeners</code> that the value
163      * of the selection, in the closed interval <code>firstIndex</code>,
164      * <code>lastIndex</code>, has changed.
165      */

166     protected void fireValueChanged(int firstIndex, int lastIndex) {
167     fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
168     }
169
170     /**
171      * @param firstIndex the first index in the interval
172      * @param lastIndex the last index in the interval
173      * @param isAdjusting true if this is the final change in a series of
174      * adjustments
175      * @see EventListenerList
176      */

177     protected void fireValueChanged(int firstIndex, int lastIndex, boolean isAdjusting)
178     {
179     Object JavaDoc[] listeners = listenerList.getListenerList();
180     ListSelectionEvent e = null;
181
182     for (int i = listeners.length - 2; i >= 0; i -= 2) {
183         if (listeners[i] == ListSelectionListener.class) {
184         if (e == null) {
185             e = new ListSelectionEvent(this, firstIndex, lastIndex, isAdjusting);
186         }
187         ((ListSelectionListener)listeners[i+1]).valueChanged(e);
188         }
189     }
190     }
191
192     private void fireValueChanged() {
193     if (lastAdjustedIndex == MIN) {
194         return;
195     }
196     /* If getValueAdjusting() is true, (eg. during a drag opereration)
197      * record the bounds of the changes so that, when the drag finishes (and
198      * setValueAdjusting(false) is called) we can post a single event
199      * with bounds covering all of these individual adjustments.
200      */

201     if (getValueIsAdjusting()) {
202             firstChangedIndex = Math.min(firstChangedIndex, firstAdjustedIndex);
203         lastChangedIndex = Math.max(lastChangedIndex, lastAdjustedIndex);
204         }
205     /* Change the values before sending the event to the
206      * listeners in case the event causes a listener to make
207      * another change to the selection.
208      */

209     int oldFirstAdjustedIndex = firstAdjustedIndex;
210     int oldLastAdjustedIndex = lastAdjustedIndex;
211     firstAdjustedIndex = MAX;
212     lastAdjustedIndex = MIN;
213
214     fireValueChanged(oldFirstAdjustedIndex, oldLastAdjustedIndex);
215     }
216
217     /**
218      * Returns an array of all the objects currently registered as
219      * <code><em>Foo</em>Listener</code>s
220      * upon this model.
221      * <code><em>Foo</em>Listener</code>s
222      * are registered using the <code>add<em>Foo</em>Listener</code> method.
223      * <p>
224      * You can specify the <code>listenerType</code> argument
225      * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
226      * For example, you can query a <code>DefaultListSelectionModel</code>
227      * instance <code>m</code>
228      * for its list selection listeners
229      * with the following code:
230      *
231      * <pre>ListSelectionListener[] lsls = (ListSelectionListener[])(m.getListeners(ListSelectionListener.class));</pre>
232      *
233      * If no such listeners exist,
234      * this method returns an empty array.
235      *
236      * @param listenerType the type of listeners requested;
237      * this parameter should specify an interface
238      * that descends from <code>java.util.EventListener</code>
239      * @return an array of all objects registered as
240      * <code><em>Foo</em>Listener</code>s
241      * on this model,
242      * or an empty array if no such
243      * listeners have been added
244      * @exception ClassCastException if <code>listenerType</code> doesn't
245      * specify a class or interface that implements
246      * <code>java.util.EventListener</code>
247      *
248      * @see #getListSelectionListeners
249      *
250      * @since 1.3
251      */

252     public <T extends EventListener JavaDoc> T[] getListeners(Class JavaDoc<T> listenerType) {
253     return listenerList.getListeners(listenerType);
254     }
255
256     // Updates first and last change indices
257
private void markAsDirty(int r) {
258         firstAdjustedIndex = Math.min(firstAdjustedIndex, r);
259     lastAdjustedIndex = Math.max(lastAdjustedIndex, r);
260     }
261
262     // Sets the state at this index and update all relevant state.
263
private void set(int r) {
264     if (value.get(r)) {
265         return;
266     }
267     value.set(r);
268         markAsDirty(r);
269
270         // Update minimum and maximum indices
271
minIndex = Math.min(minIndex, r);
272         maxIndex = Math.max(maxIndex, r);
273     }
274
275     // Clears the state at this index and update all relevant state.
276
private void clear(int r) {
277     if (!value.get(r)) {
278         return;
279     }
280     value.clear(r);
281         markAsDirty(r);
282
283         // Update minimum and maximum indices
284
/*
285            If (r > minIndex) the minimum has not changed.
286            The case (r < minIndex) is not possible because r'th value was set.
287            We only need to check for the case when lowest entry has been cleared,
288            and in this case we need to search for the first value set above it.
289     */

290     if (r == minIndex) {
291         for(minIndex = minIndex + 1; minIndex <= maxIndex; minIndex++) {
292             if (value.get(minIndex)) {
293                 break;
294             }
295         }
296     }
297         /*
298            If (r < maxIndex) the maximum has not changed.
299            The case (r > maxIndex) is not possible because r'th value was set.
300            We only need to check for the case when highest entry has been cleared,
301            and in this case we need to search for the first value set below it.
302     */

303     if (r == maxIndex) {
304         for(maxIndex = maxIndex - 1; minIndex <= maxIndex; maxIndex--) {
305             if (value.get(maxIndex)) {
306                 break;
307             }
308         }
309     }
310     /* Performance note: This method is called from inside a loop in
311        changeSelection() but we will only iterate in the loops
312        above on the basis of one iteration per deselected cell - in total.
313        Ie. the next time this method is called the work of the previous
314        deselection will not be repeated.
315
316        We also don't need to worry about the case when the min and max
317        values are in their unassigned states. This cannot happen because
318        this method's initial check ensures that the selection was not empty
319        and therefore that the minIndex and maxIndex had 'real' values.
320
321        If we have cleared the whole selection, set the minIndex and maxIndex
322        to their cannonical values so that the next set command always works
323        just by using Math.min and Math.max.
324     */

325         if (isSelectionEmpty()) {
326             minIndex = MAX;
327             maxIndex = MIN;
328         }
329     }
330
331     /**
332      * Sets the value of the leadAnchorNotificationEnabled flag.
333      * @see #isLeadAnchorNotificationEnabled()
334      */

335     public void setLeadAnchorNotificationEnabled(boolean flag) {
336         leadAnchorNotificationEnabled = flag;
337     }
338
339     /**
340      * Returns the value of the <code>leadAnchorNotificationEnabled</code> flag.
341      * When <code>leadAnchorNotificationEnabled</code> is true the model
342      * generates notification events with bounds that cover all the changes to
343      * the selection plus the changes to the lead and anchor indices.
344      * Setting the flag to false causes a narrowing of the event's bounds to
345      * include only the elements that have been selected or deselected since
346      * the last change. Either way, the model continues to maintain the lead
347      * and anchor variables internally. The default is true.
348      * <p>
349      * Note: It is possible for the lead or anchor to be changed without a
350      * change to the selection. Notification of these changes is often
351      * important, such as when the new lead or anchor needs to be updated in
352      * the view. Therefore, caution is urged when changing the default value.
353      *
354      * @return the value of the <code>leadAnchorNotificationEnabled</code> flag
355      * @see #setLeadAnchorNotificationEnabled(boolean)
356      */

357     public boolean isLeadAnchorNotificationEnabled() {
358         return leadAnchorNotificationEnabled;
359     }
360
361     private void updateLeadAnchorIndices(int anchorIndex, int leadIndex) {
362         if (leadAnchorNotificationEnabled) {
363             if (this.anchorIndex != anchorIndex) {
364                 if (this.anchorIndex != -1) { // The unassigned state.
365
markAsDirty(this.anchorIndex);
366                 }
367                 markAsDirty(anchorIndex);
368             }
369
370             if (this.leadIndex != leadIndex) {
371                 if (this.leadIndex != -1) { // The unassigned state.
372
markAsDirty(this.leadIndex);
373                 }
374                 markAsDirty(leadIndex);
375             }
376         }
377         this.anchorIndex = anchorIndex;
378         this.leadIndex = leadIndex;
379     }
380
381     private boolean contains(int a, int b, int i) {
382         return (i >= a) && (i <= b);
383     }
384
385     private void changeSelection(int clearMin, int clearMax,
386                                  int setMin, int setMax, boolean clearFirst) {
387         for(int i = Math.min(setMin, clearMin); i <= Math.max(setMax, clearMax); i++) {
388
389             boolean shouldClear = contains(clearMin, clearMax, i);
390             boolean shouldSet = contains(setMin, setMax, i);
391
392             if (shouldSet && shouldClear) {
393                 if (clearFirst) {
394                     shouldClear = false;
395                 }
396                 else {
397                     shouldSet = false;
398                 }
399             }
400
401             if (shouldSet) {
402                 set(i);
403             }
404             if (shouldClear) {
405                 clear(i);
406             }
407         }
408         fireValueChanged();
409     }
410
411    /** Change the selection with the effect of first clearing the values
412     * in the inclusive range [clearMin, clearMax] then setting the values
413     * in the inclusive range [setMin, setMax]. Do this in one pass so
414     * that no values are cleared if they would later be set.
415     */

416     private void changeSelection(int clearMin, int clearMax, int setMin, int setMax) {
417         changeSelection(clearMin, clearMax, setMin, setMax, true);
418     }
419
420     // implements javax.swing.ListSelectionModel
421
public void clearSelection() {
422     removeSelectionIntervalImpl(minIndex, maxIndex, false);
423     }
424
425     // implements javax.swing.ListSelectionModel
426
public void setSelectionInterval(int index0, int index1) {
427         if (index0 == -1 || index1 == -1) {
428             return;
429         }
430
431         if (getSelectionMode() == SINGLE_SELECTION) {
432             index0 = index1;
433         }
434
435         updateLeadAnchorIndices(index0, index1);
436
437         int clearMin = minIndex;
438         int clearMax = maxIndex;
439     int setMin = Math.min(index0, index1);
440     int setMax = Math.max(index0, index1);
441         changeSelection(clearMin, clearMax, setMin, setMax);
442     }
443
444     // implements javax.swing.ListSelectionModel
445
public void addSelectionInterval(int index0, int index1)
446     {
447         if (index0 == -1 || index1 == -1) {
448             return;
449         }
450
451         // If we only allow a single selection, channel through
452
// setSelectionInterval() to enforce the rule.
453
if (getSelectionMode() == SINGLE_SELECTION) {
454             setSelectionInterval(index0, index1);
455             return;
456         }
457
458         updateLeadAnchorIndices(index0, index1);
459
460         int clearMin = MAX;
461         int clearMax = MIN;
462     int setMin = Math.min(index0, index1);
463     int setMax = Math.max(index0, index1);
464
465         // If we only allow a single interval and this would result
466
// in multiple intervals, then set the selection to be just
467
// the new range.
468
if (getSelectionMode() == SINGLE_INTERVAL_SELECTION &&
469                 (setMax < minIndex - 1 || setMin > maxIndex + 1)) {
470
471             setSelectionInterval(index0, index1);
472             return;
473         }
474
475         changeSelection(clearMin, clearMax, setMin, setMax);
476     }
477
478
479     // implements javax.swing.ListSelectionModel
480
public void removeSelectionInterval(int index0, int index1)
481     {
482         removeSelectionIntervalImpl(index0, index1, true);
483     }
484
485     // private implementation allowing the selection interval
486
// to be removed without affecting the lead and anchor
487
private void removeSelectionIntervalImpl(int index0, int index1,
488                                              boolean changeLeadAnchor) {
489
490         if (index0 == -1 || index1 == -1) {
491             return;
492         }
493
494         if (changeLeadAnchor) {
495             updateLeadAnchorIndices(index0, index1);
496         }
497
498     int clearMin = Math.min(index0, index1);
499     int clearMax = Math.max(index0, index1);
500     int setMin = MAX;
501     int setMax = MIN;
502
503         // If the removal would produce to two disjoint selections in a mode
504
// that only allows one, extend the removal to the end of the selection.
505
if (getSelectionMode() != MULTIPLE_INTERVAL_SELECTION &&
506            clearMin > minIndex && clearMax < maxIndex) {
507         clearMax = maxIndex;
508         }
509
510         changeSelection(clearMin, clearMax, setMin, setMax);
511     }
512
513     private void setState(int index, boolean state) {
514         if (state) {
515         set(index);
516     }
517     else {
518         clear(index);
519     }
520     }
521
522     /**
523      * Insert length indices beginning before/after index. If the value
524      * at index is itself selected and the selection mode is not
525      * SINGLE_SELECTION, set all of the newly inserted items as selected.
526      * Otherwise leave them unselected. This method is typically
527      * called to sync the selection model with a corresponding change
528      * in the data model.
529      */

530     public void insertIndexInterval(int index, int length, boolean before)
531     {
532     /* The first new index will appear at insMinIndex and the last
533      * one will appear at insMaxIndex
534      */

535     int insMinIndex = (before) ? index : index + 1;
536     int insMaxIndex = (insMinIndex + length) - 1;
537
538     /* Right shift the entire bitset by length, beginning with
539      * index-1 if before is true, index+1 if it's false (i.e. with
540      * insMinIndex).
541      */

542     for(int i = maxIndex; i >= insMinIndex; i--) {
543         setState(i + length, value.get(i));
544     }
545
546     /* Initialize the newly inserted indices.
547      */

548         boolean setInsertedValues = ((getSelectionMode() == SINGLE_SELECTION) ?
549                                         false : value.get(index));
550     for(int i = insMinIndex; i <= insMaxIndex; i++) {
551         setState(i, setInsertedValues);
552     }
553
554     int leadIndex = this.leadIndex;
555     if (leadIndex > index || (before && leadIndex == index)) {
556         leadIndex = this.leadIndex + length;
557     }
558     int anchorIndex = this.anchorIndex;
559     if (anchorIndex > index || (before && anchorIndex == index)) {
560         anchorIndex = this.anchorIndex + length;
561     }
562     if (leadIndex != this.leadIndex || anchorIndex != this.anchorIndex) {
563         updateLeadAnchorIndices(anchorIndex, leadIndex);
564     }
565
566         fireValueChanged();
567     }
568
569
570     /**
571      * Remove the indices in the interval index0,index1 (inclusive) from
572      * the selection model. This is typically called to sync the selection
573      * model width a corresponding change in the data model. Note
574      * that (as always) index0 need not be <= index1.
575      */

576     public void removeIndexInterval(int index0, int index1)
577     {
578     int rmMinIndex = Math.min(index0, index1);
579     int rmMaxIndex = Math.max(index0, index1);
580     int gapLength = (rmMaxIndex - rmMinIndex) + 1;
581
582     /* Shift the entire bitset to the left to close the index0, index1
583      * gap.
584      */

585     for(int i = rmMinIndex; i <= maxIndex; i++) {
586         setState(i, value.get(i + gapLength));
587     }
588
589     int leadIndex = this.leadIndex;
590         if (leadIndex == 0 && rmMinIndex == 0) {
591             // do nothing
592
} else if (leadIndex > rmMaxIndex) {
593         leadIndex = this.leadIndex - gapLength;
594     } else if (leadIndex >= rmMinIndex) {
595             leadIndex = rmMinIndex - 1;
596         }
597
598     int anchorIndex = this.anchorIndex;
599         if (anchorIndex == 0 && rmMinIndex == 0) {
600             // do nothing
601
} else if (anchorIndex > rmMaxIndex) {
602             anchorIndex = this.anchorIndex - gapLength;
603         } else if (anchorIndex >= rmMinIndex) {
604             anchorIndex = rmMinIndex - 1;
605         }
606
607     if (leadIndex != this.leadIndex || anchorIndex != this.anchorIndex) {
608         updateLeadAnchorIndices(anchorIndex, leadIndex);
609     }
610
611         fireValueChanged();
612     }
613
614
615     // implements javax.swing.ListSelectionModel
616
public void setValueIsAdjusting(boolean isAdjusting) {
617     if (isAdjusting != this.isAdjusting) {
618         this.isAdjusting = isAdjusting;
619         this.fireValueChanged(isAdjusting);
620     }
621     }
622
623
624     /**
625      * Returns a string that displays and identifies this
626      * object's properties.
627      *
628      * @return a <code>String</code> representation of this object
629      */

630     public String JavaDoc toString() {
631     String JavaDoc s = ((getValueIsAdjusting()) ? "~" : "=") + value.toString();
632     return getClass().getName() + " " + Integer.toString(hashCode()) + " " + s;
633     }
634
635     /**
636      * Returns a clone of this selection model with the same selection.
637      * <code>listenerLists</code> are not duplicated.
638      *
639      * @exception CloneNotSupportedException if the selection model does not
640      * both (a) implement the Cloneable interface and (b) define a
641      * <code>clone</code> method.
642      */

643     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
644     DefaultListSelectionModel JavaDoc clone = (DefaultListSelectionModel JavaDoc)super.clone();
645     clone.value = (BitSet JavaDoc)value.clone();
646     clone.listenerList = new EventListenerList();
647     return clone;
648     }
649
650     // implements javax.swing.ListSelectionModel
651
public int getAnchorSelectionIndex() {
652         return anchorIndex;
653     }
654
655     // implements javax.swing.ListSelectionModel
656
public int getLeadSelectionIndex() {
657         return leadIndex;
658     }
659
660     /**
661      * Set the anchor selection index, leaving all selection values unchanged.
662      * If leadAnchorNotificationEnabled is true, send a notification covering
663      * the old and new anchor cells.
664      *
665      * @see #getAnchorSelectionIndex
666      * @see #setLeadSelectionIndex
667      */

668     public void setAnchorSelectionIndex(int anchorIndex) {
669     updateLeadAnchorIndices(anchorIndex, this.leadIndex);
670     fireValueChanged();
671     }
672
673     /**
674      * Set the lead selection index, leaving all selection values unchanged.
675      * If leadAnchorNotificationEnabled is true, send a notification covering
676      * the old and new lead cells.
677      *
678      * @param leadIndex the new lead selection index
679      *
680      * @see #setAnchorSelectionIndex
681      * @see #setLeadSelectionIndex
682      * @see #getLeadSelectionIndex
683      *
684      * @since 1.5
685      */

686     public void moveLeadSelectionIndex(int leadIndex) {
687         // disallow a -1 lead unless the anchor is already -1
688
if (leadIndex == -1) {
689             if (this.anchorIndex != -1) {
690                 return;
691             }
692
693 /* PENDING(shannonh) - The following check is nice, to be consistent with
694                        setLeadSelectionIndex. However, it is not absolutely
695                        necessary: One could work around it by setting the anchor
696                        to something valid, modifying the lead, and then moving
697                        the anchor back to -1. For this reason, there's no sense
698                        in adding it at this time, as that would require
699                        updating the spec and officially committing to it.
700
701         // otherwise, don't do anything if the anchor is -1
702         } else if (this.anchorIndex == -1) {
703             return;
704 */

705
706         }
707
708         updateLeadAnchorIndices(this.anchorIndex, leadIndex);
709         fireValueChanged();
710     }
711
712     /**
713      * Sets the lead selection index, ensuring that values between the
714      * anchor and the new lead are either all selected or all deselected.
715      * If the value at the anchor index is selected, first clear all the
716      * values in the range [anchor, oldLeadIndex], then select all the values
717      * values in the range [anchor, newLeadIndex], where oldLeadIndex is the old
718      * leadIndex and newLeadIndex is the new one.
719      * <p>
720      * If the value at the anchor index is not selected, do the same thing in
721      * reverse selecting values in the old range and deslecting values in the
722      * new one.
723      * <p>
724      * Generate a single event for this change and notify all listeners.
725      * For the purposes of generating minimal bounds in this event, do the
726      * operation in a single pass; that way the first and last index inside the
727      * ListSelectionEvent that is broadcast will refer to cells that actually
728      * changed value because of this method. If, instead, this operation were
729      * done in two steps the effect on the selection state would be the same
730      * but two events would be generated and the bounds around the changed
731      * values would be wider, including cells that had been first cleared only
732      * to later be set.
733      * <p>
734      * This method can be used in the <code>mouseDragged</code> method
735      * of a UI class to extend a selection.
736      *
737      * @see #getLeadSelectionIndex
738      * @see #setAnchorSelectionIndex
739      */

740     public void setLeadSelectionIndex(int leadIndex) {
741         int anchorIndex = this.anchorIndex;
742
743         // only allow a -1 lead if the anchor is already -1
744
if (leadIndex == -1) {
745             if (anchorIndex == -1) {
746                 updateLeadAnchorIndices(anchorIndex, leadIndex);
747                 fireValueChanged();
748             }
749
750             return;
751         // otherwise, don't do anything if the anchor is -1
752
} else if (anchorIndex == -1) {
753             return;
754         }
755
756     if (this.leadIndex == -1) {
757         this.leadIndex = leadIndex;
758     }
759
760     boolean shouldSelect = value.get(this.anchorIndex);
761
762         if (getSelectionMode() == SINGLE_SELECTION) {
763             anchorIndex = leadIndex;
764             shouldSelect = true;
765         }
766
767         int oldMin = Math.min(this.anchorIndex, this.leadIndex);
768         int oldMax = Math.max(this.anchorIndex, this.leadIndex);
769     int newMin = Math.min(anchorIndex, leadIndex);
770     int newMax = Math.max(anchorIndex, leadIndex);
771
772     updateLeadAnchorIndices(anchorIndex, leadIndex);
773
774         if (shouldSelect) {
775             changeSelection(oldMin, oldMax, newMin, newMax);
776         }
777         else {
778             changeSelection(newMin, newMax, oldMin, oldMax, false);
779         }
780     }
781 }
782
783
Popular Tags