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