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