Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / javax / swing / DefaultListSelectionModel.java
blob7ec4e614c8f338d72878b1f87e4bb82decf465ac
1 /* DefaultListSelectionModel.java --
2 Copyright (C) 2002, 2004, 2005 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)
9 any later version.
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., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
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
24 combination.
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. */
39 package javax.swing;
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;
49 /**
50 * The default implementation of {@link ListSelectionModel},
51 * which is used by {@link javax.swing.JList} and
52 * similar classes to manage the selection status of a number of data
53 * elements.
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,
63 ListSelectionModel,
64 Serializable
66 private static final long serialVersionUID = -5718799865110415860L;
68 /** The list of ListSelectionListeners subscribed to this selection model. */
69 protected EventListenerList listenerList = new EventListenerList();
72 /**
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;
81 /**
82 * The index of the "lead" of the most recent selection. The lead is the
83 * second argument in any call to {@link #setSelectionInterval}, {@link
84 * #addSelectionInterval} or {@link #removeSelectionInterval}. Generally
85 * the lead refers to the most recent position a user dragged their mouse
86 * over.
88 int leadSelectionIndex = -1;
90 /**
91 * The index of the "anchor" of the most recent selection. The anchor is
92 * the first argument in any call to {@link #setSelectionInterval},
93 * {@link #addSelectionInterval} or {@link
94 * #removeSelectionInterval}. Generally the anchor refers to the first
95 * recent position a user clicks when they begin to drag their mouse over
96 * a list.
98 * @see #getAnchorSelectionIndex
99 * @see #setAnchorSelectionIndex
101 int anchorSelectionIndex = -1;
104 * controls the range of indices provided in any {@link
105 * ListSelectionEvent} fired by the selectionModel. Let
106 * <code>[A,L]</code> be the range of indices between {@link
107 * #anchorSelectionIndex} and {@link #leadSelectionIndex} inclusive, and
108 * let <code>[i0,i1]</code> be the range of indices changed in a given
109 * call which generates a {@link ListSelectionEvent}. Then when this
110 * property is <code>true</code>, the {@link ListSelectionEvent} contains
111 * the range <code>[A,L] union [i0,i1]</code>; when <code>false</code> it
112 * will contain only <code>[i0,i1]</code>. The default is
113 * <code>true</code>.
115 * @see #isLeadAnchorNotificationEnabled
116 * @see #setLeadAnchorNotificationEnabled
118 protected boolean leadAnchorNotificationEnabled = true;
121 * Whether the selection is currently "adjusting". Any {@link
122 * ListSelectionEvent} events constructed in response to changes in this
123 * list selection model will have their {@link
124 * ListSelectionEvent#isAdjusting} field set to this value.
126 * @see #getValueIsAdjusting
127 * @see #setValueIsAdjusting
129 boolean valueIsAdjusting = false;
132 /**
133 * The current set of "intervals", represented simply by a {@link
134 * java.util.BitSet}. A set bit indicates a selected index, whereas a
135 * cleared bit indicates a non-selected index.
137 BitSet sel = new BitSet();
140 * A variable to store the previous value of sel.
141 * Used to make sure we only fireValueChanged when the BitSet
142 * actually does change.
144 Object oldSel;
147 * Whether this call of setLeadSelectionInterval was called locally
148 * from addSelectionInterval
150 boolean setLeadCalledFromAdd = false;
153 * Gets the value of the {@link #selectionMode} property.
155 * @return The current value of the property
157 public int getSelectionMode()
159 return selectionMode;
163 * Sets the value of the {@link #selectionMode} property.
165 * @param a The new value of the property
167 public void setSelectionMode(int a)
169 selectionMode = a;
173 * Gets the value of the {@link #anchorSelectionIndex} property.
175 * @return The current property value
177 * @see #setAnchorSelectionIndex
179 public int getAnchorSelectionIndex()
181 return anchorSelectionIndex;
185 * Sets the value of the {@link #anchorSelectionIndex} property.
187 * @param anchorIndex The new property value
189 * @see #getAnchorSelectionIndex
191 public void setAnchorSelectionIndex(int anchorIndex)
193 anchorSelectionIndex = anchorIndex;
197 * Gets the value of the {@link #leadSelectionIndex} property.
199 * @return The current property value
201 * @see #setLeadSelectionIndex
203 public int getLeadSelectionIndex()
205 return leadSelectionIndex;
209 * <p>Sets the value of the {@link #anchorSelectionIndex} property. As a
210 * side effect, alters the selection status of two ranges of indices. Let
211 * <code>OL</code> be the old lead selection index, <code>NL</code> be
212 * the new lead selection index, and <code>A</code> be the anchor
213 * selection index. Then if <code>A</code> is a valid selection index,
214 * one of two things happens depending on the seleciton status of
215 * <code>A</code>:</p>
217 * <ul>
219 * <li><code>isSelectedIndex(A) == true</code>: set <code>[A,OL]</code>
220 * to <em>deselected</em>, then set <code>[A,NL]</code> to
221 * <em>selected</em>.</li>
223 * <li><code>isSelectedIndex(A) == false</code>: set <code>[A,OL]</code>
224 * to <em>selected</em>, then set <code>[A,NL]</code> to
225 * <em>deselected</em>.</li>
227 * </ul>
229 * <p>This method generates at most a single {@link ListSelectionEvent}
230 * despite changing multiple ranges. The range of values provided to the
231 * {@link ListSelectionEvent} includes only the minimum range of values
232 * which changed selection status between the beginning and end of the
233 * method.</p>
235 * @param leadIndex The new property value
237 * @see #getAnchorSelectionIndex
239 public void setLeadSelectionIndex(int leadIndex)
241 // Only set the lead selection index to < 0 if anchorSelectionIndex < 0.
242 if (leadIndex < 0)
244 if (anchorSelectionIndex < 0)
245 leadSelectionIndex = -1;
246 else
247 return;
250 // Only touch the lead selection index if the anchor is >= 0.
251 if (anchorSelectionIndex < 0)
252 return;
254 if (selectionMode == SINGLE_SELECTION)
255 setSelectionInterval (leadIndex, leadIndex);
257 int oldLeadIndex = leadSelectionIndex;
258 if (oldLeadIndex == -1)
259 oldLeadIndex = leadIndex;
260 if (setLeadCalledFromAdd == false)
261 oldSel = sel.clone();
262 leadSelectionIndex = leadIndex;
264 if (anchorSelectionIndex == -1)
265 return;
267 int R1 = Math.min(anchorSelectionIndex, oldLeadIndex);
268 int R2 = Math.max(anchorSelectionIndex, oldLeadIndex);
269 int S1 = Math.min(anchorSelectionIndex, leadIndex);
270 int S2 = Math.max(anchorSelectionIndex, leadIndex);
272 int lo = Math.min(R1, S1);
273 int hi = Math.max(R2, S2);
275 if (isSelectedIndex(anchorSelectionIndex))
277 sel.clear(R1, R2+1);
278 sel.set(S1, S2+1);
280 else
282 sel.set(R1, R2+1);
283 sel.clear(S1, S2+1);
286 int beg = sel.nextSetBit(0), end = -1;
287 for(int i=beg; i >= 0; i=sel.nextSetBit(i+1))
288 end = i;
289 if (sel.equals(oldSel) == false)
290 fireValueChanged(beg, end, valueIsAdjusting);
294 * Moves the lead selection index to <code>leadIndex</code> without
295 * changing the selection values.
297 * If leadAnchorNotificationEnabled is true, send a notification covering the
298 * old and new lead cells.
300 * @param leadIndex the new lead selection index
301 * @since 1.5
303 public void moveLeadSelectionIndex (int leadIndex)
305 if (leadSelectionIndex == leadIndex)
306 return;
308 leadSelectionIndex = leadIndex;
309 if (isLeadAnchorNotificationEnabled())
310 fireValueChanged(Math.min(leadSelectionIndex, leadIndex),
311 Math.max(leadSelectionIndex, leadIndex));
315 * Gets the value of the {@link #leadAnchorNotificationEnabled} property.
317 * @return The current property value
319 * @see #setLeadAnchorNotificationEnabled
321 public boolean isLeadAnchorNotificationEnabled()
323 return leadAnchorNotificationEnabled;
327 * Sets the value of the {@link #leadAnchorNotificationEnabled} property.
329 * @param l The new property value
331 * @see #isLeadAnchorNotificationEnabled
333 public void setLeadAnchorNotificationEnabled(boolean l)
335 leadAnchorNotificationEnabled = l;
339 * Gets the value of the {@link #valueIsAdjusting} property.
341 * @return The current property value
343 * @see #setValueIsAdjusting
345 public boolean getValueIsAdjusting()
347 return valueIsAdjusting;
351 * Sets the value of the {@link #valueIsAdjusting} property.
353 * @param v The new property value
355 * @see #getValueIsAdjusting
357 public void setValueIsAdjusting(boolean v)
359 valueIsAdjusting = v;
363 * Determines whether the selection is empty.
365 * @return <code>true</code> if the selection is empty, otherwise
366 * <code>false</code>
368 public boolean isSelectionEmpty()
370 return sel.isEmpty();
374 * Gets the smallest index which is currently a member of a selection
375 * interval.
377 * @return The least integer <code>i</code> such that <code>i >=
378 * 0</code> and <code>i</code> is a member of a selected interval, or
379 * <code>-1</code> if there are no selected intervals
381 * @see #getMaxSelectionIndex
383 public int getMinSelectionIndex()
385 if (isSelectionEmpty())
386 return -1;
388 return sel.nextSetBit(0);
392 * Gets the largest index which is currently a member of a selection
393 * interval.
395 * @return The greatest integer <code>i</code> such that <code>i >=
396 * 0</code> and <code>i</code> is a member of a selected interval, or
397 * <code>-1</code> if there are no selected intervals
399 * @see #getMinSelectionIndex
401 public int getMaxSelectionIndex()
403 if (isSelectionEmpty())
404 return -1;
406 int mx = -1;
407 for(int i=sel.nextSetBit(0); i >= 0; i=sel.nextSetBit(i+1))
409 mx = i;
411 return mx;
415 * Determines whether a particular index is a member of a selection
416 * interval.
418 * @param a The index to search for
420 * @return <code>true</code> if the index is a member of a selection interval,
421 * otherwise <code>false</code>
423 public boolean isSelectedIndex(int a)
425 // TODO: Probably throw an exception here?
426 if (a >= sel.length() || a < 0)
427 return false;
428 return sel.get(a);
432 * If the {@link #selectionMode} property is equal to
433 * <code>SINGLE_SELECTION</code> equivalent to calling
434 * <code>setSelectionInterval(index1, index2)</code>;
435 * If the {@link #selectionMode} property is equal to
436 * <code>SINGLE_INTERVAL_SELECTION</code> and the interval being
437 * added is not adjacent to an already selected interval,
438 * equivalent to <code>setSelectionInterval(index1, index2)</code>.
439 * Otherwise adds the range <code>[index0, index1]</code>
440 * to the selection interval set.
442 * @param index0 The beginning of the range of indices to select
443 * @param index1 The end of the range of indices to select
445 * @see #setSelectionInterval
446 * @see #removeSelectionInterval
448 public void addSelectionInterval(int index0, int index1)
450 if (index0 == -1 || index1 == -1)
451 return;
453 int lo = Math.min(index0, index1);
454 int hi = Math.max(index0, index1);
455 oldSel = sel.clone();
457 if (selectionMode == SINGLE_SELECTION)
458 setSelectionInterval(index0, index1);
460 // COMPAT: Like Sun (but not like IBM), we allow calls to
461 // addSelectionInterval when selectionMode is
462 // SINGLE_SELECTION_INTERVAL iff the interval being added
463 // is adjacent to an already selected interval
464 if (selectionMode == SINGLE_INTERVAL_SELECTION)
465 if (!(isSelectedIndex(index0) ||
466 isSelectedIndex(index1) ||
467 isSelectedIndex(Math.max(lo-1,0)) ||
468 isSelectedIndex(Math.min(hi+1,sel.size()))))
469 sel.clear();
471 // We have to update the anchorSelectionIndex and leadSelectionIndex
472 // variables
474 // The next if statements breaks down to "if this selection is adjacent
475 // to the previous selection and going in the same direction"
476 if ((isSelectedIndex(leadSelectionIndex))
477 && ((index0 - 1 == leadSelectionIndex
478 && (index1 >= index0)
479 && (leadSelectionIndex >= anchorSelectionIndex))
480 || (index0 + 1 == leadSelectionIndex && (index1 <= index0)
481 && (leadSelectionIndex <= anchorSelectionIndex)))
482 && (anchorSelectionIndex != -1 || leadSelectionIndex != -1))
484 // setting setLeadCalledFromAdd to true tells setLeadSelectionIndex
485 // not to update oldSel
486 setLeadCalledFromAdd = true;
487 setLeadSelectionIndex(index1);
488 setLeadCalledFromAdd = false;
490 else
492 leadSelectionIndex = index1;
493 anchorSelectionIndex = index0;
494 sel.set(lo, hi+1);
495 if (sel.equals(oldSel) == false)
496 fireValueChanged(lo, hi, valueIsAdjusting);
502 * Deselects all indices in the inclusive range
503 * <code>[index0,index1]</code>.
505 * @param index0 The beginning of the range of indices to deselect
506 * @param index1 The end of the range of indices to deselect
508 * @see #addSelectionInterval
509 * @see #setSelectionInterval
511 public void removeSelectionInterval(int index0,
512 int index1)
514 if (index0 == -1 || index1 == -1)
515 return;
517 oldSel = sel.clone();
518 int lo = Math.min(index0, index1);
519 int hi = Math.max(index0, index1);
521 // if selectionMode is SINGLE_INTERVAL_SELECTION and removing the interval
522 // (index0,index1) would leave two disjoint selection intervals, remove all
523 // selected indices from lo to the last selected index
524 if (getMinSelectionIndex() > 0 && getMinSelectionIndex() < lo &&
525 selectionMode == SINGLE_INTERVAL_SELECTION)
526 hi = sel.size() - 1;
528 sel.clear(lo, hi+1);
529 //update anchorSelectionIndex and leadSelectionIndex variables
530 //TODO: will probably need MouseDragged to test properly and know if this works
531 setAnchorSelectionIndex(index0);
532 leadSelectionIndex = index1;
533 if (sel.equals(oldSel) == false)
534 fireValueChanged(lo, hi, valueIsAdjusting);
538 * Removes all intervals in the selection set.
540 public void clearSelection()
542 oldSel = sel.clone();
543 int sz = sel.size();
544 sel.clear();
545 if (sel.equals(oldSel) == false)
546 fireValueChanged(0, sz, valueIsAdjusting);
550 * Clears the current selection and marks a given interval as
551 * "selected". If the current selection mode is
552 * <code>SINGLE_SELECTION</code> only the index <code>index2</code> is
553 * selected.
555 * @param index0 The low end of the new selection
556 * @param index1 The high end of the new selection
558 public void setSelectionInterval(int index0, int index1)
560 if (index0 == -1 || index1 == -1)
561 return;
563 oldSel = sel.clone();
564 sel.clear();
565 if (selectionMode == SINGLE_SELECTION)
566 index0 = index1;
568 int lo = Math.min(index0, index1);
569 int hi = Math.max(index0, index1);
570 sel.set(lo, hi+1);
571 // update the anchorSelectionIndex and leadSelectionIndex variables
572 setAnchorSelectionIndex(index0);
573 leadSelectionIndex=index1;
574 if (sel.equals(oldSel) == false)
575 fireValueChanged(lo, hi, valueIsAdjusting);
579 * Inserts a number of indices either before or after a particular
580 * position in the set of indices. Renumbers all indices after the
581 * inserted range. The new indices in the inserted range are not
582 * selected. This method is typically called to synchronize the selection
583 * model with an inserted range of elements in a {@link ListModel}.
585 * @param index The position to insert indices at
586 * @param length The number of indices to insert
587 * @param before Indicates whether to insert the indices before the index
588 * or after it
590 public void insertIndexInterval(int index,
591 int length,
592 boolean before)
594 if (!before)
596 index++;
597 length--;
599 BitSet tmp = sel.get(index, sel.size());
600 sel.clear(index, sel.size());
601 int n = tmp.size();
602 for (int i = 0; i < n; ++i)
603 sel.set(index + length + i, tmp.get(i));
607 * Removes a range from the set of indices. Renumbers all indices after
608 * the removed range. This method is typically called to synchronize the
609 * selection model with a deleted range of elements in a {@link
610 * ListModel}.
612 * @param index0 The first index to remove (inclusive)
613 * @param index1 The last index to remove (inclusive)
615 public void removeIndexInterval(int index0,
616 int index1)
618 int lo = Math.min(index0, index1);
619 int hi = Math.max(index0, index1);
621 BitSet tmp = sel.get(hi, sel.size());
622 sel.clear(lo, sel.size());
623 int n = tmp.size();
624 for (int i = 0; i < n; ++i)
625 sel.set(lo + i, tmp.get(i));
629 * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
630 * ListSelectionListener} registered with this selection model to
631 * indicate that a series of adjustment has just ended.
633 * The values of {@link #getMinSelectionIndex} and
634 * {@link #getMaxSelectionIndex} are used in the {@link ListSelectionEvent}
635 * that gets fired.
637 * @param isAdjusting <code>true</code> if this is the final change
638 * in a series of adjustments, <code>false/code> otherwise
640 protected void fireValueChanged(boolean isAdjusting)
642 fireValueChanged(getMinSelectionIndex(), getMaxSelectionIndex(),
643 isAdjusting);
647 * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
648 * ListSelectionListener} registered with this selection model.
650 * @param firstIndex The low index of the changed range
651 * @param lastIndex The high index of the changed range
653 protected void fireValueChanged(int firstIndex, int lastIndex)
655 fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
659 * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
660 * ListSelectionListener} registered with this selection model.
662 * @param firstIndex The low index of the changed range
663 * @param lastIndex The high index of the changed range
664 * @param isAdjusting Whether this change is part of a seqence of adjustments
665 * made to the selection, such as during interactive scrolling
667 protected void fireValueChanged(int firstIndex, int lastIndex,
668 boolean isAdjusting)
670 ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex,
671 lastIndex, isAdjusting);
672 ListSelectionListener[] listeners = getListSelectionListeners();
673 for (int i = 0; i < listeners.length; ++i)
674 listeners[i].valueChanged(evt);
678 * Adds a listener.
680 * @param listener The listener to add
682 * @see #removeListSelectionListener
683 * @see #getListSelectionListeners
685 public void addListSelectionListener(ListSelectionListener listener)
687 listenerList.add(ListSelectionListener.class, listener);
691 * Removes a registered listener.
693 * @param listener The listener to remove
695 * @see #addListSelectionListener
696 * @see #getListSelectionListeners
698 public void removeListSelectionListener(ListSelectionListener listener)
700 listenerList.remove(ListSelectionListener.class, listener);
704 * Returns an array of all registerers listeners.
706 * @param listenerType The type of listener to retrieve
708 * @return The array
710 * @see #getListSelectionListeners
711 * @since 1.3
713 public EventListener[] getListeners(Class listenerType)
715 return listenerList.getListeners(listenerType);
719 * Returns an array of all registerd list selection listeners.
721 * @return the array
723 * @see #addListSelectionListener
724 * @see #removeListSelectionListener
725 * @see #getListeners
726 * @since 1.4
728 public ListSelectionListener[] getListSelectionListeners()
730 return (ListSelectionListener[]) getListeners(ListSelectionListener.class);
734 * Returns a clone of this object.
735 * <code>listenerList</code> don't gets duplicated.
737 * @return the cloned object
739 * @throws CloneNotSupportedException if an error occurs
741 public Object clone()
742 throws CloneNotSupportedException
744 DefaultListSelectionModel model =
745 (DefaultListSelectionModel) super.clone();
746 model.sel = (BitSet) sel.clone();
747 return model;