1 /* DefaultListSelectionModel.java --
2 Copyright (C) 2002, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java
.io
.Serializable
;
42 import java
.util
.BitSet
;
43 import java
.util
.EventListener
;
45 import javax
.swing
.event
.EventListenerList
;
46 import javax
.swing
.event
.ListSelectionEvent
;
47 import javax
.swing
.event
.ListSelectionListener
;
50 * <p>This class provides a default implementation of {@link
51 * ListSelectioModel}, which is used by {@link javax.swing.JList} and
52 * similar classes to manage the selection status of a number of data
55 * <p>The class is organized <em>abstractly</em> as a set of intervals of
56 * integers. Each interval indicates an inclusive range of indices in a
57 * list -- held by some other object and unknown to this class -- which is
58 * considered "selected". There are various accessors for querying and
59 * modifying the set of intervals, with simplified forms accepting a single
60 * index, representing an interval with only one element. </p>
62 public class DefaultListSelectionModel
implements Cloneable
,
66 private static final long serialVersionUID
= -5718799865110415860L;
68 /** The list of ListSelectionListeners subscribed to this selection model. */
69 protected EventListenerList listenerList
= new EventListenerList();
73 * The current list selection mode. Must be one of the numeric constants
74 * <code>SINGLE_SELECTION</code>, <code>SINGLE_INTERVAL_SELECTION</code>
75 * or <code>MULTIPLE_INTERVAL_SELECTION</code> from {@link
76 * ListSelectionModel}. The default value is
77 * <code>MULTIPLE_INTERVAL_SELECTION</code>.
79 int selectionMode
= MULTIPLE_INTERVAL_SELECTION
;
83 * The index of the "lead" of the most recent selection. The lead is the
84 * second argument in any call to {@link #setSelectionInterval}, {@link
85 * #addSelectionInterval} or {@link #removeSelectionInterval}. Generally
86 * the lead refers to the most recent position a user dragged their mouse
89 int leadSelectionIndex
= -1;
93 * The index of the "anchor" of the most recent selection. The anchor is
94 * the first argument in any call to {@link #setSelectionInterval},
95 * {@link #addSelectionInterval} or {@link
96 * #removeSelectionInterval}. Generally the anchor refers to the first
97 * recent position a user clicks when they begin to drag their mouse over
100 * @see #getAnchorSelectionIndex
101 * @see #setAnchorSelectionIndex
103 int anchorSelectionIndex
= -1;
107 * controls the range of indices provided in any {@link
108 * ListSelectionEvent} fired by the selectionModel. Let
109 * <code>[A,L]</code> be the range of indices between {@link
110 * anchorSelectionIndex} and {@link leadSelectionIndex} inclusive, and
111 * let <code>[i0,i1]</code> be the range of indices changed in a given
112 * call which generates a {@link ListSelectionEvent}. Then when this
113 * property is <code>true</code>, the {@link ListSelectionEvent} contains
114 * the range <code>[A,L] union [i0,i1]</code>; when <code>false</code> it
115 * will contain only <code>[i0,i1]</code>. The default is
118 * @see #isLeadAnchorNotificationEnabled
119 * @see #setLeadAnchorNotificationEnabled
121 protected boolean leadAnchorNotificationEnabled
= true;
125 * Whether the selection is currently "adjusting". Any {@link
126 * ListSelectionEvent} events constructed in response to changes in this
127 * list selection model will have their {@link
128 * ListSelectionEvent#isAdjusting} field set to this value.
130 * @see #getValueIsAdjusting
131 * @see #setValueIsAdjusting
133 boolean valueIsAdjusting
= false;
137 * The current set of "intervals", represented simply by a {@link
138 * java.util.BitSet}. A set bit indicates a selected index, whereas a
139 * cleared bit indicates a non-selected index.
141 BitSet sel
= new BitSet();
145 * Gets the value of the {@link #selectionMode} property.
147 * @return The current value of the property
149 public int getSelectionMode()
151 return selectionMode
;
155 * Sets the value of the {@link #selectionMode} property.
157 * @param a The new value of the property
159 public void setSelectionMode(int a
)
165 * Gets the value of the {@link #anchorSelectionIndex} property.
167 * @return The current property value
169 * @see #setAnchorSelectionIndex
171 public int getAnchorSelectionIndex()
173 return anchorSelectionIndex
;
177 * Sets the value of the {@link #anchorSelectionIndex} property.
179 * @param anchorIndex The new property value
181 * @see #getAnchorSelectionIndex
183 public void setAnchorSelectionIndex(int anchorIndex
)
185 anchorSelectionIndex
= anchorIndex
;
189 * Gets the value of the {@link #leadSelectionIndex} property.
191 * @return The current property value
193 * @see #setLeadSelectionIndex
195 public int getLeadSelectionIndex()
197 return leadSelectionIndex
;
201 * <p>Sets the value of the {@link #anchorSelectionIndex} property. As a
202 * side effect, alters the selection status of two ranges of indices. Let
203 * <code>OL</code> be the old lead selection index, <code>NL</code> be
204 * the new lead selection index, and <code>A</code> be the anchor
205 * selection index. Then if <code>A</code> is a valid selection index,
206 * one of two things happens depending on the seleciton status of
207 * <code>A</code>:</p>
211 * <li><code>isSelectedIndex(A) == true</code>: set <code>[A,OL]</code>
212 * to <em>deselected</em>, then set <code>[A,NL]</code> to
213 * <em>selected</em>.</li>
215 * <li><code>isSelectedIndex(A) == false</code>: set <code>[A,OL]</code>
216 * to <em>selected</em>, then set <code>[A,NL]</code> to
217 * <em>deselected</em>.</li>
221 * <p>This method generates at most a single {@link ListSelectionEvent}
222 * despite changing multiple ranges. The range of values provided to the
223 * {@link ListSelectionEvent} includes only the minimum range of values
224 * which changed selection status between the beginning and end of the
227 * @param anchorIndex The new property value
229 * @see #getAnchorSelectionIndex
231 public void setLeadSelectionIndex(int leadIndex
)
233 int oldLeadIndex
= leadSelectionIndex
;
234 leadSelectionIndex
= leadIndex
;
236 if (anchorSelectionIndex
== -1)
239 int R1
= Math
.min(anchorSelectionIndex
, oldLeadIndex
);
240 int R2
= Math
.max(anchorSelectionIndex
, oldLeadIndex
);
241 int S1
= Math
.min(anchorSelectionIndex
, leadIndex
);
242 int S2
= Math
.max(anchorSelectionIndex
, leadIndex
);
244 int lo
= Math
.min(R1
, S1
);
245 int hi
= Math
.max(R2
, S2
);
247 BitSet oldRange
= sel
.get(lo
, hi
+1);
249 if (isSelectedIndex(anchorSelectionIndex
))
260 BitSet newRange
= sel
.get(lo
, hi
+1);
261 newRange
.xor(oldRange
);
263 int beg
= sel
.nextSetBit(0), end
= -1;
264 for(int i
=beg
; i
>= 0; i
=sel
.nextSetBit(i
+1))
268 fireValueChanged(beg
, end
, valueIsAdjusting
);
272 * Gets the value of the {@link #leadAnchorNotificationEnabled} property.
274 * @return The current property value
276 * @see #setLeadAnchorNotificationEnabled
278 public boolean isLeadAnchorNotificationEnabled()
280 return leadAnchorNotificationEnabled
;
284 * Sets the value of the {@link #leadAnchorNotificationEnabled} property.
286 * @param flag The new property value
288 * @see #getLeadAnchorNotificationEnabled
290 public void setLeadAnchorNotificationEnabled(boolean l
)
292 leadAnchorNotificationEnabled
= l
;
297 * Gets the value of the {@link #valueIsAdjusting} property.
299 * @return The current property value
301 * @see #setValueIsAdjusting
303 public boolean getValueIsAdjusting()
305 return valueIsAdjusting
;
309 * Sets the value of the {@link #valueIsAdjusting} property.
311 * @param v The new property value
313 * @see #getValueIsAdjusting
315 public void setValueIsAdjusting(boolean v
)
317 valueIsAdjusting
= v
;
321 * Determines whether the selection is empty.
323 * @return <code>true</code> if the selection is empty, otherwise
326 public boolean isSelectionEmpty()
328 return sel
.isEmpty();
333 * Gets the smallest index which is currently a member of a selection
336 * @return The least integer <code>i</code> such that <code>i >=
337 * 0</code> and <code>i</code> is a member of a selected interval, or
338 * <code>-1</code> if there are no selected intervals
340 * @see #getMaxSelectionIndex
342 public int getMinSelectionIndex()
344 if (isSelectionEmpty())
347 return sel
.nextSetBit(0);
351 * Gets the largest index which is currently a member of a selection
354 * @return The greatest integer <code>i</code> such that <code>i >=
355 * 0</code> and <code>i</code> is a member of a selected interval, or
356 * <code>-1</code> if there are no selected intervals
358 * @see #getMinSelectionIndex
360 public int getMaxSelectionIndex()
362 if (isSelectionEmpty())
366 for(int i
=sel
.nextSetBit(0); i
>= 0; i
=sel
.nextSetBit(i
+1))
374 * Determines whether a particular index is a member of a selection
377 * @param a The index to search for
379 * @return <code>true</code> if the index is a member of a selection interval,
380 * otherwise <code>false</code>
382 public boolean isSelectedIndex(int a
)
388 * If the {@link #selectionMode} property is equal to
389 * <code>SINGLE_SELECTION</code> or
390 * <code>SINGLE_INTERVAL_SELECTION</code>, equivalent to calling
391 * <code>setSelectionInterval(index1, index2)</code>; otherwise adds the
392 * range <code>[index0, index1]</code> to the selection interval set.
394 * @param index0 The beginning of the range of indices to select
395 * @param index1 The end of the range of indices to select
397 * @see #setSelectionInterval
398 * @see #removeSelectionInterval
400 public void addSelectionInterval(int index0
, int index1
)
402 if (selectionMode
== SINGLE_SELECTION
403 || selectionMode
== SINGLE_INTERVAL_SELECTION
)
406 if (selectionMode
== SINGLE_SELECTION
)
409 int lo
= Math
.min(index0
, index1
);
410 int hi
= Math
.max(index0
, index1
);
413 fireValueChanged(lo
, hi
, valueIsAdjusting
);
418 * Deselects all indices in the inclusive range
419 * <code>[index0,index1]</code>.
421 * @param index0 The beginning of the range of indices to deselect
422 * @param index1 The end of the range of indices to deselect
424 * @see #addSelectionInterval
425 * @see #setSelectionInterval
427 public void removeSelectionInterval(int index0
,
430 int lo
= Math
.min(index0
, index1
);
431 int hi
= Math
.max(index0
, index1
);
433 fireValueChanged(lo
, hi
, valueIsAdjusting
);
437 * Removes all intervals in the selection set.
439 public void clearSelection()
443 fireValueChanged(0, sz
, valueIsAdjusting
);
447 * Clears the current selection and marks a given interval as
448 * "selected". If the current selection mode is
449 * <code>SINGLE_SELECTION</code> only the index <code>index2</code> is
452 * @param index0 The low end of the new selection
453 * @param index1 The high end of the new selection
455 public void setSelectionInterval(int index0
, int index1
)
458 if (selectionMode
== SINGLE_SELECTION
)
461 int lo
= Math
.min(index0
, index1
);
462 int hi
= Math
.max(index0
, index1
);
464 fireValueChanged(lo
, hi
, valueIsAdjusting
);
468 * Inserts a number of indices either before or after a particular
469 * position in the set of indices. Renumbers all indices after the
470 * inserted range. The new indices in the inserted range are not
471 * selected. This method is typically called to synchronize the selection
472 * model with an inserted range of elements in a {@link ListModel}.
474 * @param index The position to insert indices at
475 * @param length The number of indices to insert
476 * @param before Indicates whether to insert the indices before the index
479 public void insertIndexInterval(int index
,
488 BitSet tmp
= sel
.get(index
, sel
.size());
489 sel
.clear(index
, sel
.size());
491 for (int i
= 0; i
< n
; ++i
)
492 sel
.set(index
+ length
+ i
, tmp
.get(i
));
496 * Removes a range from the set of indices. Renumbers all indices after
497 * the removed range. This method is typically called to synchronize the
498 * selection model with a deleted range of elements in a {@link
501 * @param index0 The first index to remove (inclusive)
502 * @param index1 The last index to remove (inclusive)
504 public void removeIndexInterval(int index0
,
507 int lo
= Math
.min(index0
, index1
);
508 int hi
= Math
.max(index0
, index1
);
510 BitSet tmp
= sel
.get(hi
, sel
.size());
511 sel
.clear(lo
, sel
.size());
513 for (int i
= 0; i
< n
; ++i
)
514 sel
.set(lo
+ i
, tmp
.get(i
));
518 * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
519 * ListSelectionListener} registered with this selection model.
521 * @param firstIndex The low index of the changed range
522 * @param lastIndex The high index of the changed range
524 protected void fireValueChanged(int firstIndex
, int lastIndex
)
526 fireValueChanged(firstIndex
, lastIndex
, getValueIsAdjusting());
530 * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
531 * ListSelectionListener} registered with this selection model.
533 * @param firstIndex The low index of the changed range
534 * @param lastIndex The high index of the changed range
535 * @param isAdjusting Whether this change is part of a seqence of adjustments
536 * made to the selection, such as during interactive scrolling
538 protected void fireValueChanged(int firstIndex
, int lastIndex
,
541 ListSelectionEvent evt
= new ListSelectionEvent(this, firstIndex
,
542 lastIndex
, isAdjusting
);
543 ListSelectionListener
[] listeners
= getListSelectionListeners();
544 for (int i
= 0; i
< listeners
.length
; ++i
)
545 listeners
[i
].valueChanged(evt
);
551 * @param listener The listener to add
553 * @see removeListSelectionListener
554 * @see getListSelectionListeners
556 public void addListSelectionListener(ListSelectionListener listener
)
558 listenerList
.add(ListSelectionListener
.class, listener
);
562 * Removes a registered listener.
564 * @param listener The listener to remove
566 * @see addListSelectionListener
567 * @see getListSelectionListeners
569 public void removeListSelectionListener(ListSelectionListener listener
)
571 listenerList
.remove(ListSelectionListener
.class, listener
);
575 * Returns an array of all registerers listeners.
577 * @param listenerType The type of listener to retrieve
581 * @see getListSelectionListener
584 public EventListener
[] getListeners(Class listenerType
)
586 return listenerList
.getListeners(listenerType
);
590 * Returns an array of all registerd list selection listeners.
594 * @see addListSelectionListener
595 * @see removeListSelectionListener
599 public ListSelectionListener
[] getListSelectionListeners()
601 return (ListSelectionListener
[]) getListeners(ListSelectionListener
.class);
605 * Returns a clone of this object.
606 * <code>listenerList</code> don't gets duplicated.
608 * @return the cloned object
610 * @throws CloneNotSupportedException if an error occurs
612 public Object
clone()
613 throws CloneNotSupportedException
615 DefaultListSelectionModel model
=
616 (DefaultListSelectionModel
) super.clone();
617 model
.sel
= (BitSet
) sel
.clone();