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