History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/DefaultListSelectionModel.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/DefaultListSelectionModel.java+849−0
@@ -0,0 +1,849 @@
1/*
2 * Copyright (c) 1997, 2008, 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
26package javax.swing;
27
28import java.util.EventListener;
29import java.util.BitSet;
30import java.io.Serializable;
31import java.beans.Transient;
32
33import 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<sup><font size="-2">TM</font></sup>
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
53public 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 firstAdjustedIndex = Math.min(firstAdjustedIndex, r);
256 lastAdjustedIndex = Math.max(lastAdjustedIndex, r);
257 }
258
259 // Sets the state at this index and update all relevant state.
260 private void set(int r) {
261 if (value.get(r)) {
262 return;
263 }
264 value.set(r);
265 markAsDirty(r);
266
267 // Update minimum and maximum indices
268 minIndex = Math.min(minIndex, r);
269 maxIndex = Math.max(maxIndex, r);
270 }
271
272 // Clears the state at this index and update all relevant state.
273 private void clear(int r) {
274 if (!value.get(r)) {
275 return;
276 }
277 value.clear(r);
278 markAsDirty(r);
279
280 // Update minimum and maximum indices
281 /*
282 If (r > minIndex) the minimum has not changed.
283 The case (r < minIndex) is not possible because r'th value was set.
284 We only need to check for the case when lowest entry has been cleared,
285 and in this case we need to search for the first value set above it.
286 */
287 if (r == minIndex) {
288 for(minIndex = minIndex + 1; minIndex <= maxIndex; minIndex++) {
289 if (value.get(minIndex)) {
290 break;
291 }
292 }
293 }
294 /*
295 If (r < maxIndex) the maximum has not changed.
296 The case (r > maxIndex) is not possible because r'th value was set.
297 We only need to check for the case when highest entry has been cleared,
298 and in this case we need to search for the first value set below it.
299 */
300 if (r == maxIndex) {
301 for(maxIndex = maxIndex - 1; minIndex <= maxIndex; maxIndex--) {
302 if (value.get(maxIndex)) {
303 break;
304 }
305 }
306 }
307 /* Performance note: This method is called from inside a loop in
308 changeSelection() but we will only iterate in the loops
309 above on the basis of one iteration per deselected cell - in total.
310 Ie. the next time this method is called the work of the previous
311 deselection will not be repeated.
312
313 We also don't need to worry about the case when the min and max
314 values are in their unassigned states. This cannot happen because
315 this method's initial check ensures that the selection was not empty
316 and therefore that the minIndex and maxIndex had 'real' values.
317
318 If we have cleared the whole selection, set the minIndex and maxIndex
319 to their cannonical values so that the next set command always works
320 just by using Math.min and Math.max.
321 */
322 if (isSelectionEmpty()) {
323 minIndex = MAX;
324 maxIndex = MIN;
325 }
326 }
327
328 /**
329 * Sets the value of the leadAnchorNotificationEnabled flag.
330 * @see #isLeadAnchorNotificationEnabled()
331 */
332 public void setLeadAnchorNotificationEnabled(boolean flag) {
333 leadAnchorNotificationEnabled = flag;
334 }
335
336 /**
337 * Returns the value of the <code>leadAnchorNotificationEnabled</code> flag.
338 * When <code>leadAnchorNotificationEnabled</code> is true the model
339 * generates notification events with bounds that cover all the changes to
340 * the selection plus the changes to the lead and anchor indices.
341 * Setting the flag to false causes a narrowing of the event's bounds to
342 * include only the elements that have been selected or deselected since
343 * the last change. Either way, the model continues to maintain the lead
344 * and anchor variables internally. The default is true.
345 * <p>
346 * Note: It is possible for the lead or anchor to be changed without a
347 * change to the selection. Notification of these changes is often
348 * important, such as when the new lead or anchor needs to be updated in
349 * the view. Therefore, caution is urged when changing the default value.
350 *
351 * @return the value of the <code>leadAnchorNotificationEnabled</code> flag
352 * @see #setLeadAnchorNotificationEnabled(boolean)
353 */
354 public boolean isLeadAnchorNotificationEnabled() {
355 return leadAnchorNotificationEnabled;
356 }
357
358 private void updateLeadAnchorIndices(int anchorIndex, int leadIndex) {
359 if (leadAnchorNotificationEnabled) {
360 if (this.anchorIndex != anchorIndex) {
361 if (this.anchorIndex != -1) { // The unassigned state.
362 markAsDirty(this.anchorIndex);
363 }
364 markAsDirty(anchorIndex);
365 }
366
367 if (this.leadIndex != leadIndex) {
368 if (this.leadIndex != -1) { // The unassigned state.
369 markAsDirty(this.leadIndex);
370 }
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 deslecting 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}