History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/DefaultListSelectionModel.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/DefaultListSelectionModel.java+849−0
@@ -0,0 +1,849 @@
1+/*
2+ * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package javax.swing;
27+
28+import java.util.EventListener;
29+import java.util.BitSet;
30+import java.io.Serializable;
31+import java.beans.Transient;
32+
33+import javax.swing.event.*;
34+
35+
36+/**
37+ * Default data model for list selections.
38+ * <p>
39+ * <strong>Warning:</strong>
40+ * Serialized objects of this class will not be compatible with
41+ * future Swing releases. The current serialization support is
42+ * appropriate for short term storage or RMI between applications running
43+ * the same version of Swing. As of 1.4, support for long term storage
44+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
45+ * has been added to the <code>java.beans</code> package.
46+ * Please see {@link java.beans.XMLEncoder}.
47+ *
48+ * @author Philip Milne
49+ * @author Hans Muller
50+ * @see ListSelectionModel
51+ */
52+
53+public class DefaultListSelectionModel implements ListSelectionModel, Cloneable, Serializable
54+{
55+ private static final int MIN = -1;
56+ private static final int MAX = Integer.MAX_VALUE;
57+ private int selectionMode = MULTIPLE_INTERVAL_SELECTION;
58+ private int minIndex = MAX;
59+ private int maxIndex = MIN;
60+ private int anchorIndex = -1;
61+ private int leadIndex = -1;
62+ private int firstAdjustedIndex = MAX;
63+ private int lastAdjustedIndex = MIN;
64+ private boolean isAdjusting = false;
65+
66+ private int firstChangedIndex = MAX;
67+ private int lastChangedIndex = MIN;
68+
69+ private BitSet value = new BitSet(32);
70+ protected EventListenerList listenerList = new EventListenerList();
71+
72+ protected boolean leadAnchorNotificationEnabled = true;
73+
74+ /** {@inheritDoc} */
75+ public int getMinSelectionIndex() { return isSelectionEmpty() ? -1 : minIndex; }
76+
77+ /** {@inheritDoc} */
78+ public int getMaxSelectionIndex() { return maxIndex; }
79+
80+ /** {@inheritDoc} */
81+ public boolean getValueIsAdjusting() { return isAdjusting; }
82+
83+ /** {@inheritDoc} */
84+ public int getSelectionMode() { return selectionMode; }
85+
86+ /**
87+ * {@inheritDoc}
88+ * @throws IllegalArgumentException {@inheritDoc}
89+ */
90+ public void setSelectionMode(int selectionMode) {
91+ switch (selectionMode) {
92+ case SINGLE_SELECTION:
93+ case SINGLE_INTERVAL_SELECTION:
94+ case MULTIPLE_INTERVAL_SELECTION:
95+ this.selectionMode = selectionMode;
96+ break;
97+ default:
98+ throw new IllegalArgumentException("invalid selectionMode");
99+ }
100+ }
101+
102+ /** {@inheritDoc} */
103+ public boolean isSelectedIndex(int index) {
104+ return ((index < minIndex) || (index > maxIndex)) ? false : value.get(index);
105+ }
106+
107+ /** {@inheritDoc} */
108+ public boolean isSelectionEmpty() {
109+ return (minIndex > maxIndex);
110+ }
111+
112+ /** {@inheritDoc} */
113+ public void addListSelectionListener(ListSelectionListener l) {
114+ listenerList.add(ListSelectionListener.class, l);
115+ }
116+
117+ /** {@inheritDoc} */
118+ public void removeListSelectionListener(ListSelectionListener l) {
119+ listenerList.remove(ListSelectionListener.class, l);
120+ }
121+
122+ /**
123+ * Returns an array of all the list selection listeners
124+ * registered on this <code>DefaultListSelectionModel</code>.
125+ *
126+ * @return all of this model's <code>ListSelectionListener</code>s
127+ * or an empty
128+ * array if no list selection listeners are currently registered
129+ *
130+ * @see #addListSelectionListener
131+ * @see #removeListSelectionListener
132+ *
133+ * @since 1.4
134+ */
135+ public ListSelectionListener[] getListSelectionListeners() {
136+ return listenerList.getListeners(ListSelectionListener.class);
137+ }
138+
139+ /**
140+ * Notifies listeners that we have ended a series of adjustments.
141+ */
142+ protected void fireValueChanged(boolean isAdjusting) {
143+ if (lastChangedIndex == MIN) {
144+ return;
145+ }
146+ /* Change the values before sending the event to the
147+ * listeners in case the event causes a listener to make
148+ * another change to the selection.
149+ */
150+ int oldFirstChangedIndex = firstChangedIndex;
151+ int oldLastChangedIndex = lastChangedIndex;
152+ firstChangedIndex = MAX;
153+ lastChangedIndex = MIN;
154+ fireValueChanged(oldFirstChangedIndex, oldLastChangedIndex, isAdjusting);
155+ }
156+
157+
158+ /**
159+ * Notifies <code>ListSelectionListeners</code> that the value
160+ * of the selection, in the closed interval <code>firstIndex</code>,
161+ * <code>lastIndex</code>, has changed.
162+ */
163+ protected void fireValueChanged(int firstIndex, int lastIndex) {
164+ fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
165+ }
166+
167+ /**
168+ * @param firstIndex the first index in the interval
169+ * @param lastIndex the last index in the interval
170+ * @param isAdjusting true if this is the final change in a series of
171+ * adjustments
172+ * @see EventListenerList
173+ */
174+ protected void fireValueChanged(int firstIndex, int lastIndex, boolean isAdjusting)
175+ {
176+ Object[] listeners = listenerList.getListenerList();
177+ ListSelectionEvent e = null;
178+
179+ for (int i = listeners.length - 2; i >= 0; i -= 2) {
180+ if (listeners[i] == ListSelectionListener.class) {
181+ if (e == null) {
182+ e = new ListSelectionEvent(this, firstIndex, lastIndex, isAdjusting);
183+ }
184+ ((ListSelectionListener)listeners[i+1]).valueChanged(e);
185+ }
186+ }
187+ }
188+
189+ private void fireValueChanged() {
190+ if (lastAdjustedIndex == MIN) {
191+ return;
192+ }
193+ /* If getValueAdjusting() is true, (eg. during a drag opereration)
194+ * record the bounds of the changes so that, when the drag finishes (and
195+ * setValueAdjusting(false) is called) we can post a single event
196+ * with bounds covering all of these individual adjustments.
197+ */
198+ if (getValueIsAdjusting()) {
199+ firstChangedIndex = Math.min(firstChangedIndex, firstAdjustedIndex);
200+ lastChangedIndex = Math.max(lastChangedIndex, lastAdjustedIndex);
201+ }
202+ /* Change the values before sending the event to the
203+ * listeners in case the event causes a listener to make
204+ * another change to the selection.
205+ */
206+ int oldFirstAdjustedIndex = firstAdjustedIndex;
207+ int oldLastAdjustedIndex = lastAdjustedIndex;
208+ firstAdjustedIndex = MAX;
209+ lastAdjustedIndex = MIN;
210+
211+ fireValueChanged(oldFirstAdjustedIndex, oldLastAdjustedIndex);
212+ }
213+
214+ /**
215+ * Returns an array of all the objects currently registered as
216+ * <code><em>Foo</em>Listener</code>s
217+ * upon this model.
218+ * <code><em>Foo</em>Listener</code>s
219+ * are registered using the <code>add<em>Foo</em>Listener</code> method.
220+ * <p>
221+ * You can specify the <code>listenerType</code> argument
222+ * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
223+ * For example, you can query a <code>DefaultListSelectionModel</code>
224+ * instance <code>m</code>
225+ * for its list selection listeners
226+ * with the following code:
227+ *
228+ * <pre>ListSelectionListener[] lsls = (ListSelectionListener[])(m.getListeners(ListSelectionListener.class));</pre>
229+ *
230+ * If no such listeners exist,
231+ * this method returns an empty array.
232+ *
233+ * @param listenerType the type of listeners requested;
234+ * this parameter should specify an interface
235+ * that descends from <code>java.util.EventListener</code>
236+ * @return an array of all objects registered as
237+ * <code><em>Foo</em>Listener</code>s
238+ * on this model,
239+ * or an empty array if no such
240+ * listeners have been added
241+ * @exception ClassCastException if <code>listenerType</code> doesn't
242+ * specify a class or interface that implements
243+ * <code>java.util.EventListener</code>
244+ *
245+ * @see #getListSelectionListeners
246+ *
247+ * @since 1.3
248+ */
249+ public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
250+ return listenerList.getListeners(listenerType);
251+ }
252+
253+ // Updates first and last change indices
254+ private void markAsDirty(int r) {
255+ firstAdjustedIndex = Math.min(firstAdjustedIndex, r);
256+ lastAdjustedIndex = Math.max(lastAdjustedIndex, r);
257+ }
258+
259+ // Sets the state at this index and update all relevant state.
260+ private void set(int r) {
261+ if (value.get(r)) {
262+ return;
263+ }
264+ value.set(r);
265+ markAsDirty(r);
266+
267+ // Update minimum and maximum indices
268+ minIndex = Math.min(minIndex, r);
269+ maxIndex = Math.max(maxIndex, r);
270+ }
271+
272+ // Clears the state at this index and update all relevant state.
273+ private void clear(int r) {
274+ if (!value.get(r)) {
275+ return;
276+ }
277+ value.clear(r);
278+ markAsDirty(r);
279+
280+ // Update minimum and maximum indices
281+ /*
282+ If (r > minIndex) the minimum has not changed.
283+ The case (r < minIndex) is not possible because r'th value was set.
284+ We only need to check for the case when lowest entry has been cleared,
285+ and in this case we need to search for the first value set above it.
286+ */
287+ if (r == minIndex) {
288+ for(minIndex = minIndex + 1; minIndex <= maxIndex; minIndex++) {
289+ if (value.get(minIndex)) {
290+ break;
291+ }
292+ }
293+ }
294+ /*
295+ If (r < maxIndex) the maximum has not changed.
296+ The case (r > maxIndex) is not possible because r'th value was set.
297+ We only need to check for the case when highest entry has been cleared,
298+ and in this case we need to search for the first value set below it.
299+ */
300+ if (r == maxIndex) {
301+ for(maxIndex = maxIndex - 1; minIndex <= maxIndex; maxIndex--) {
302+ if (value.get(maxIndex)) {
303+ break;
304+ }
305+ }
306+ }
307+ /* Performance note: This method is called from inside a loop in
308+ changeSelection() but we will only iterate in the loops
309+ above on the basis of one iteration per deselected cell - in total.
310+ Ie. the next time this method is called the work of the previous
311+ deselection will not be repeated.
312+
313+ We also don't need to worry about the case when the min and max
314+ values are in their unassigned states. This cannot happen because
315+ this method's initial check ensures that the selection was not empty
316+ and therefore that the minIndex and maxIndex had 'real' values.
317+
318+ If we have cleared the whole selection, set the minIndex and maxIndex
319+ to their cannonical values so that the next set command always works
320+ just by using Math.min and Math.max.
321+ */
322+ if (isSelectionEmpty()) {
323+ minIndex = MAX;
324+ maxIndex = MIN;
325+ }
326+ }
327+
328+ /**
329+ * Sets the value of the leadAnchorNotificationEnabled flag.
330+ * @see #isLeadAnchorNotificationEnabled()
331+ */
332+ public void setLeadAnchorNotificationEnabled(boolean flag) {
333+ leadAnchorNotificationEnabled = flag;
334+ }
335+
336+ /**
337+ * Returns the value of the <code>leadAnchorNotificationEnabled</code> flag.
338+ * When <code>leadAnchorNotificationEnabled</code> is true the model
339+ * generates notification events with bounds that cover all the changes to
340+ * the selection plus the changes to the lead and anchor indices.
341+ * Setting the flag to false causes a narrowing of the event's bounds to
342+ * include only the elements that have been selected or deselected since
343+ * the last change. Either way, the model continues to maintain the lead
344+ * and anchor variables internally. The default is true.
345+ * <p>
346+ * Note: It is possible for the lead or anchor to be changed without a
347+ * change to the selection. Notification of these changes is often
348+ * important, such as when the new lead or anchor needs to be updated in
349+ * the view. Therefore, caution is urged when changing the default value.
350+ *
351+ * @return the value of the <code>leadAnchorNotificationEnabled</code> flag
352+ * @see #setLeadAnchorNotificationEnabled(boolean)
353+ */
354+ public boolean isLeadAnchorNotificationEnabled() {
355+ return leadAnchorNotificationEnabled;
356+ }
357+
358+ private void updateLeadAnchorIndices(int anchorIndex, int leadIndex) {
359+ if (leadAnchorNotificationEnabled) {
360+ if (this.anchorIndex != anchorIndex) {
361+ if (this.anchorIndex != -1) { // The unassigned state.
362+ markAsDirty(this.anchorIndex);
363+ }
364+ markAsDirty(anchorIndex);
365+ }
366+
367+ if (this.leadIndex != leadIndex) {
368+ if (this.leadIndex != -1) { // The unassigned state.
369+ markAsDirty(this.leadIndex);
370+ }
371+ markAsDirty(leadIndex);
372+ }
373+ }
374+ this.anchorIndex = anchorIndex;
375+ this.leadIndex = leadIndex;
376+ }
377+
378+ private boolean contains(int a, int b, int i) {
379+ return (i >= a) && (i <= b);
380+ }
381+
382+ private void changeSelection(int clearMin, int clearMax,
383+ int setMin, int setMax, boolean clearFirst) {
384+ for(int i = Math.min(setMin, clearMin); i <= Math.max(setMax, clearMax); i++) {
385+
386+ boolean shouldClear = contains(clearMin, clearMax, i);
387+ boolean shouldSet = contains(setMin, setMax, i);
388+
389+ if (shouldSet && shouldClear) {
390+ if (clearFirst) {
391+ shouldClear = false;
392+ }
393+ else {
394+ shouldSet = false;
395+ }
396+ }
397+
398+ if (shouldSet) {
399+ set(i);
400+ }
401+ if (shouldClear) {
402+ clear(i);
403+ }
404+ }
405+ fireValueChanged();
406+ }
407+
408+ /**
409+ * Change the selection with the effect of first clearing the values
410+ * in the inclusive range [clearMin, clearMax] then setting the values
411+ * in the inclusive range [setMin, setMax]. Do this in one pass so
412+ * that no values are cleared if they would later be set.
413+ */
414+ private void changeSelection(int clearMin, int clearMax, int setMin, int setMax) {
415+ changeSelection(clearMin, clearMax, setMin, setMax, true);
416+ }
417+
418+ /** {@inheritDoc} */
419+ public void clearSelection() {
420+ removeSelectionIntervalImpl(minIndex, maxIndex, false);
421+ }
422+
423+ /**
424+ * Changes the selection to be between {@code index0} and {@code index1}
425+ * inclusive. {@code index0} doesn't have to be less than or equal to
426+ * {@code index1}.
427+ * <p>
428+ * In {@code SINGLE_SELECTION} selection mode, only the second index
429+ * is used.
430+ * <p>
431+ * If this represents a change to the current selection, then each
432+ * {@code ListSelectionListener} is notified of the change.
433+ * <p>
434+ * If either index is {@code -1}, this method does nothing and returns
435+ * without exception. Otherwise, if either index is less than {@code -1},
436+ * an {@code IndexOutOfBoundsException} is thrown.
437+ *
438+ * @param index0 one end of the interval.
439+ * @param index1 other end of the interval
440+ * @throws IndexOutOfBoundsException if either index is less than {@code -1}
441+ * (and neither index is {@code -1})
442+ * @see #addListSelectionListener
443+ */
444+ public void setSelectionInterval(int index0, int index1) {
445+ if (index0 == -1 || index1 == -1) {
446+ return;
447+ }
448+
449+ if (getSelectionMode() == SINGLE_SELECTION) {
450+ index0 = index1;
451+ }
452+
453+ updateLeadAnchorIndices(index0, index1);
454+
455+ int clearMin = minIndex;
456+ int clearMax = maxIndex;
457+ int setMin = Math.min(index0, index1);
458+ int setMax = Math.max(index0, index1);
459+ changeSelection(clearMin, clearMax, setMin, setMax);
460+ }
461+
462+ /**
463+ * Changes the selection to be the set union of the current selection
464+ * and the indices between {@code index0} and {@code index1} inclusive.
465+ * <p>
466+ * In {@code SINGLE_SELECTION} selection mode, this is equivalent
467+ * to calling {@code setSelectionInterval}, and only the second index
468+ * is used. In {@code SINGLE_INTERVAL_SELECTION} selection mode, this
469+ * method behaves like {@code setSelectionInterval}, unless the given
470+ * interval is immediately adjacent to or overlaps the existing selection,
471+ * and can therefore be used to grow it.
472+ * <p>
473+ * If this represents a change to the current selection, then each
474+ * {@code ListSelectionListener} is notified of the change. Note that
475+ * {@code index0} doesn't have to be less than or equal to {@code index1}.
476+ * <p>
477+ * If either index is {@code -1}, this method does nothing and returns
478+ * without exception. Otherwise, if either index is less than {@code -1},
479+ * an {@code IndexOutOfBoundsException} is thrown.
480+ *
481+ * @param index0 one end of the interval.
482+ * @param index1 other end of the interval
483+ * @throws IndexOutOfBoundsException if either index is less than {@code -1}
484+ * (and neither index is {@code -1})
485+ * @see #addListSelectionListener
486+ * @see #setSelectionInterval
487+ */
488+ public void addSelectionInterval(int index0, int index1)
489+ {
490+ if (index0 == -1 || index1 == -1) {
491+ return;
492+ }
493+
494+ // If we only allow a single selection, channel through
495+ // setSelectionInterval() to enforce the rule.
496+ if (getSelectionMode() == SINGLE_SELECTION) {
497+ setSelectionInterval(index0, index1);
498+ return;
499+ }
500+
501+ updateLeadAnchorIndices(index0, index1);
502+
503+ int clearMin = MAX;
504+ int clearMax = MIN;
505+ int setMin = Math.min(index0, index1);
506+ int setMax = Math.max(index0, index1);
507+
508+ // If we only allow a single interval and this would result
509+ // in multiple intervals, then set the selection to be just
510+ // the new range.
511+ if (getSelectionMode() == SINGLE_INTERVAL_SELECTION &&
512+ (setMax < minIndex - 1 || setMin > maxIndex + 1)) {
513+
514+ setSelectionInterval(index0, index1);
515+ return;
516+ }
517+
518+ changeSelection(clearMin, clearMax, setMin, setMax);
519+ }
520+
521+
522+ /**
523+ * Changes the selection to be the set difference of the current selection
524+ * and the indices between {@code index0} and {@code index1} inclusive.
525+ * {@code index0} doesn't have to be less than or equal to {@code index1}.
526+ * <p>
527+ * In {@code SINGLE_INTERVAL_SELECTION} selection mode, if the removal
528+ * would produce two disjoint selections, the removal is extended through
529+ * the greater end of the selection. For example, if the selection is
530+ * {@code 0-10} and you supply indices {@code 5,6} (in any order) the
531+ * resulting selection is {@code 0-4}.
532+ * <p>
533+ * If this represents a change to the current selection, then each
534+ * {@code ListSelectionListener} is notified of the change.
535+ * <p>
536+ * If either index is {@code -1}, this method does nothing and returns
537+ * without exception. Otherwise, if either index is less than {@code -1},
538+ * an {@code IndexOutOfBoundsException} is thrown.
539+ *
540+ * @param index0 one end of the interval
541+ * @param index1 other end of the interval
542+ * @throws IndexOutOfBoundsException if either index is less than {@code -1}
543+ * (and neither index is {@code -1})
544+ * @see #addListSelectionListener
545+ */
546+ public void removeSelectionInterval(int index0, int index1)
547+ {
548+ removeSelectionIntervalImpl(index0, index1, true);
549+ }
550+
551+ // private implementation allowing the selection interval
552+ // to be removed without affecting the lead and anchor
553+ private void removeSelectionIntervalImpl(int index0, int index1,
554+ boolean changeLeadAnchor) {
555+
556+ if (index0 == -1 || index1 == -1) {
557+ return;
558+ }
559+
560+ if (changeLeadAnchor) {
561+ updateLeadAnchorIndices(index0, index1);
562+ }
563+
564+ int clearMin = Math.min(index0, index1);
565+ int clearMax = Math.max(index0, index1);
566+ int setMin = MAX;
567+ int setMax = MIN;
568+
569+ // If the removal would produce to two disjoint selections in a mode
570+ // that only allows one, extend the removal to the end of the selection.
571+ if (getSelectionMode() != MULTIPLE_INTERVAL_SELECTION &&
572+ clearMin > minIndex && clearMax < maxIndex) {
573+ clearMax = maxIndex;
574+ }
575+
576+ changeSelection(clearMin, clearMax, setMin, setMax);
577+ }
578+
579+ private void setState(int index, boolean state) {
580+ if (state) {
581+ set(index);
582+ }
583+ else {
584+ clear(index);
585+ }
586+ }
587+
588+ /**
589+ * Insert length indices beginning before/after index. If the value
590+ * at index is itself selected and the selection mode is not
591+ * SINGLE_SELECTION, set all of the newly inserted items as selected.
592+ * Otherwise leave them unselected. This method is typically
593+ * called to sync the selection model with a corresponding change
594+ * in the data model.
595+ */
596+ public void insertIndexInterval(int index, int length, boolean before)
597+ {
598+ /* The first new index will appear at insMinIndex and the last
599+ * one will appear at insMaxIndex
600+ */
601+ int insMinIndex = (before) ? index : index + 1;
602+ int insMaxIndex = (insMinIndex + length) - 1;
603+
604+ /* Right shift the entire bitset by length, beginning with
605+ * index-1 if before is true, index+1 if it's false (i.e. with
606+ * insMinIndex).
607+ */
608+ for(int i = maxIndex; i >= insMinIndex; i--) {
609+ setState(i + length, value.get(i));
610+ }
611+
612+ /* Initialize the newly inserted indices.
613+ */
614+ boolean setInsertedValues = ((getSelectionMode() == SINGLE_SELECTION) ?
615+ false : value.get(index));
616+ for(int i = insMinIndex; i <= insMaxIndex; i++) {
617+ setState(i, setInsertedValues);
618+ }
619+
620+ int leadIndex = this.leadIndex;
621+ if (leadIndex > index || (before && leadIndex == index)) {
622+ leadIndex = this.leadIndex + length;
623+ }
624+ int anchorIndex = this.anchorIndex;
625+ if (anchorIndex > index || (before && anchorIndex == index)) {
626+ anchorIndex = this.anchorIndex + length;
627+ }
628+ if (leadIndex != this.leadIndex || anchorIndex != this.anchorIndex) {
629+ updateLeadAnchorIndices(anchorIndex, leadIndex);
630+ }
631+
632+ fireValueChanged();
633+ }
634+
635+
636+ /**
637+ * Remove the indices in the interval index0,index1 (inclusive) from
638+ * the selection model. This is typically called to sync the selection
639+ * model width a corresponding change in the data model. Note
640+ * that (as always) index0 need not be <= index1.
641+ */
642+ public void removeIndexInterval(int index0, int index1)
643+ {
644+ int rmMinIndex = Math.min(index0, index1);
645+ int rmMaxIndex = Math.max(index0, index1);
646+ int gapLength = (rmMaxIndex - rmMinIndex) + 1;
647+
648+ /* Shift the entire bitset to the left to close the index0, index1
649+ * gap.
650+ */
651+ for(int i = rmMinIndex; i <= maxIndex; i++) {
652+ setState(i, value.get(i + gapLength));
653+ }
654+
655+ int leadIndex = this.leadIndex;
656+ if (leadIndex == 0 && rmMinIndex == 0) {
657+ // do nothing
658+ } else if (leadIndex > rmMaxIndex) {
659+ leadIndex = this.leadIndex - gapLength;
660+ } else if (leadIndex >= rmMinIndex) {
661+ leadIndex = rmMinIndex - 1;
662+ }
663+
664+ int anchorIndex = this.anchorIndex;
665+ if (anchorIndex == 0 && rmMinIndex == 0) {
666+ // do nothing
667+ } else if (anchorIndex > rmMaxIndex) {
668+ anchorIndex = this.anchorIndex - gapLength;
669+ } else if (anchorIndex >= rmMinIndex) {
670+ anchorIndex = rmMinIndex - 1;
671+ }
672+
673+ if (leadIndex != this.leadIndex || anchorIndex != this.anchorIndex) {
674+ updateLeadAnchorIndices(anchorIndex, leadIndex);
675+ }
676+
677+ fireValueChanged();
678+ }
679+
680+
681+ /** {@inheritDoc} */
682+ public void setValueIsAdjusting(boolean isAdjusting) {
683+ if (isAdjusting != this.isAdjusting) {
684+ this.isAdjusting = isAdjusting;
685+ this.fireValueChanged(isAdjusting);
686+ }
687+ }
688+
689+
690+ /**
691+ * Returns a string that displays and identifies this
692+ * object's properties.
693+ *
694+ * @return a <code>String</code> representation of this object
695+ */
696+ public String toString() {
697+ String s = ((getValueIsAdjusting()) ? "~" : "=") + value.toString();
698+ return getClass().getName() + " " + Integer.toString(hashCode()) + " " + s;
699+ }
700+
701+ /**
702+ * Returns a clone of this selection model with the same selection.
703+ * <code>listenerLists</code> are not duplicated.
704+ *
705+ * @exception CloneNotSupportedException if the selection model does not
706+ * both (a) implement the Cloneable interface and (b) define a
707+ * <code>clone</code> method.
708+ */
709+ public Object clone() throws CloneNotSupportedException {
710+ DefaultListSelectionModel clone = (DefaultListSelectionModel)super.clone();
711+ clone.value = (BitSet)value.clone();
712+ clone.listenerList = new EventListenerList();
713+ return clone;
714+ }
715+
716+ /** {@inheritDoc} */
717+ @Transient
718+ public int getAnchorSelectionIndex() {
719+ return anchorIndex;
720+ }
721+
722+ /** {@inheritDoc} */
723+ @Transient
724+ public int getLeadSelectionIndex() {
725+ return leadIndex;
726+ }
727+
728+ /**
729+ * Set the anchor selection index, leaving all selection values unchanged.
730+ * If leadAnchorNotificationEnabled is true, send a notification covering
731+ * the old and new anchor cells.
732+ *
733+ * @see #getAnchorSelectionIndex
734+ * @see #setLeadSelectionIndex
735+ */
736+ public void setAnchorSelectionIndex(int anchorIndex) {
737+ updateLeadAnchorIndices(anchorIndex, this.leadIndex);
738+ fireValueChanged();
739+ }
740+
741+ /**
742+ * Set the lead selection index, leaving all selection values unchanged.
743+ * If leadAnchorNotificationEnabled is true, send a notification covering
744+ * the old and new lead cells.
745+ *
746+ * @param leadIndex the new lead selection index
747+ *
748+ * @see #setAnchorSelectionIndex
749+ * @see #setLeadSelectionIndex
750+ * @see #getLeadSelectionIndex
751+ *
752+ * @since 1.5
753+ */
754+ public void moveLeadSelectionIndex(int leadIndex) {
755+ // disallow a -1 lead unless the anchor is already -1
756+ if (leadIndex == -1) {
757+ if (this.anchorIndex != -1) {
758+ return;
759+ }
760+
761+/* PENDING(shannonh) - The following check is nice, to be consistent with
762+ setLeadSelectionIndex. However, it is not absolutely
763+ necessary: One could work around it by setting the anchor
764+ to something valid, modifying the lead, and then moving
765+ the anchor back to -1. For this reason, there's no sense
766+ in adding it at this time, as that would require
767+ updating the spec and officially committing to it.
768+
769+ // otherwise, don't do anything if the anchor is -1
770+ } else if (this.anchorIndex == -1) {
771+ return;
772+*/
773+
774+ }
775+
776+ updateLeadAnchorIndices(this.anchorIndex, leadIndex);
777+ fireValueChanged();
778+ }
779+
780+ /**
781+ * Sets the lead selection index, ensuring that values between the
782+ * anchor and the new lead are either all selected or all deselected.
783+ * If the value at the anchor index is selected, first clear all the
784+ * values in the range [anchor, oldLeadIndex], then select all the values
785+ * values in the range [anchor, newLeadIndex], where oldLeadIndex is the old
786+ * leadIndex and newLeadIndex is the new one.
787+ * <p>
788+ * If the value at the anchor index is not selected, do the same thing in
789+ * reverse selecting values in the old range and deslecting values in the
790+ * new one.
791+ * <p>
792+ * Generate a single event for this change and notify all listeners.
793+ * For the purposes of generating minimal bounds in this event, do the
794+ * operation in a single pass; that way the first and last index inside the
795+ * ListSelectionEvent that is broadcast will refer to cells that actually
796+ * changed value because of this method. If, instead, this operation were
797+ * done in two steps the effect on the selection state would be the same
798+ * but two events would be generated and the bounds around the changed
799+ * values would be wider, including cells that had been first cleared only
800+ * to later be set.
801+ * <p>
802+ * This method can be used in the <code>mouseDragged</code> method
803+ * of a UI class to extend a selection.
804+ *
805+ * @see #getLeadSelectionIndex
806+ * @see #setAnchorSelectionIndex
807+ */
808+ public void setLeadSelectionIndex(int leadIndex) {
809+ int anchorIndex = this.anchorIndex;
810+
811+ // only allow a -1 lead if the anchor is already -1
812+ if (leadIndex == -1) {
813+ if (anchorIndex == -1) {
814+ updateLeadAnchorIndices(anchorIndex, leadIndex);
815+ fireValueChanged();
816+ }
817+
818+ return;
819+ // otherwise, don't do anything if the anchor is -1
820+ } else if (anchorIndex == -1) {
821+ return;
822+ }
823+
824+ if (this.leadIndex == -1) {
825+ this.leadIndex = leadIndex;
826+ }
827+
828+ boolean shouldSelect = value.get(this.anchorIndex);
829+
830+ if (getSelectionMode() == SINGLE_SELECTION) {
831+ anchorIndex = leadIndex;
832+ shouldSelect = true;
833+ }
834+
835+ int oldMin = Math.min(this.anchorIndex, this.leadIndex);
836+ int oldMax = Math.max(this.anchorIndex, this.leadIndex);
837+ int newMin = Math.min(anchorIndex, leadIndex);
838+ int newMax = Math.max(anchorIndex, leadIndex);
839+
840+ updateLeadAnchorIndices(anchorIndex, leadIndex);
841+
842+ if (shouldSelect) {
843+ changeSelection(oldMin, oldMax, newMin, newMax);
844+ }
845+ else {
846+ changeSelection(newMin, newMax, oldMin, oldMax, false);
847+ }
848+ }
849+}