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