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