OSDN Git Service

2005-04-19 Roman Kennke <roman@kennke.org>
[pf3gnuchains/gcc-fork.git] / libjava / javax / swing / DefaultListSelectionModel.java
1 /* DefaultListSelectionModel.java --
2    Copyright (C) 2002, 2004 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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.
10
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.
15
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
19 02111-1307 USA.
20
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.
25
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. */
37
38
39 package javax.swing;
40
41 import java.io.Serializable;
42 import java.util.BitSet;
43 import java.util.EventListener;
44
45 import javax.swing.event.EventListenerList;
46 import javax.swing.event.ListSelectionEvent;
47 import javax.swing.event.ListSelectionListener;
48
49 /**
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
53  * elements. </p>
54  *
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>
61  */
62 public class DefaultListSelectionModel implements Cloneable,
63                                                   ListSelectionModel,
64                                                   Serializable
65 {
66   private static final long serialVersionUID = -5718799865110415860L;
67
68   /** The list of ListSelectionListeners subscribed to this selection model. */
69   protected EventListenerList listenerList = new EventListenerList();
70
71
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>.
78    */
79   int selectionMode = MULTIPLE_INTERVAL_SELECTION;
80
81
82   /**
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
87    * over.
88    */
89   int leadSelectionIndex = -1;
90
91
92   /**
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
98    * a list.
99    *
100    * @see #getAnchorSelectionIndex
101    * @see #setAnchorSelectionIndex
102    */
103   int anchorSelectionIndex = -1;
104
105
106   /**
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
116    * <code>true</code>.
117    *
118    * @see #isLeadAnchorNotificationEnabled
119    * @see #setLeadAnchorNotificationEnabled
120    */
121   protected boolean leadAnchorNotificationEnabled = true;
122
123
124   /**
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.
129    *
130    * @see #getValueIsAdjusting
131    * @see #setValueIsAdjusting
132    */
133   boolean valueIsAdjusting = false;
134
135
136   /** 
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.
140    */
141   BitSet sel = new BitSet();
142
143
144   /**
145    * Gets the value of the {@link #selectionMode} property.
146    *
147    * @return The current value of the property
148    */
149   public int getSelectionMode()
150   {
151     return selectionMode;
152   }
153
154   /**
155    * Sets the value of the {@link #selectionMode} property.
156    *
157    * @param a The new value of the property
158    */
159   public void setSelectionMode(int a)
160   {
161     selectionMode = a;
162   }
163
164   /**
165    * Gets the value of the {@link #anchorSelectionIndex} property.
166    * 
167    * @return The current property value
168    *
169    * @see #setAnchorSelectionIndex
170    */
171   public int getAnchorSelectionIndex()
172   {
173     return anchorSelectionIndex;
174   }
175
176   /**
177    * Sets the value of the {@link #anchorSelectionIndex} property.
178    * 
179    * @param anchorIndex The new property value
180    *
181    * @see #getAnchorSelectionIndex
182    */
183   public void setAnchorSelectionIndex(int anchorIndex)
184   {
185     anchorSelectionIndex = anchorIndex;
186   }
187   
188   /**
189    * Gets the value of the {@link #leadSelectionIndex} property.
190    * 
191    * @return The current property value
192    *
193    * @see #setLeadSelectionIndex
194    */
195   public int getLeadSelectionIndex()
196   {
197     return leadSelectionIndex;
198   }
199
200   /**
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>
208    *
209    * <ul>
210    *
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>
214    *
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>
218    *
219    * </ul>
220    *
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
225    * method.</p>
226    * 
227    * @param anchorIndex The new property value
228    *
229    * @see #getAnchorSelectionIndex
230    */
231   public void setLeadSelectionIndex(int leadIndex)
232   {
233     int oldLeadIndex = leadSelectionIndex;
234     leadSelectionIndex = leadIndex;
235
236     if (anchorSelectionIndex == -1)
237       return;
238
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);
243     
244     int lo = Math.min(R1, S1);
245     int hi = Math.max(R2, S2);
246
247     BitSet oldRange = sel.get(lo, hi+1);
248
249     if (isSelectedIndex(anchorSelectionIndex))
250       {
251         sel.clear(R1, R2+1);
252         sel.set(S1, S2+1);
253       }
254     else
255       {
256         sel.set(R1, R2+1);
257         sel.clear(S1, S2+1);
258       }
259     
260     BitSet newRange = sel.get(lo, hi+1);
261     newRange.xor(oldRange);
262
263     int beg = sel.nextSetBit(0), end = -1;
264     for(int i=beg; i >= 0; i=sel.nextSetBit(i+1)) 
265       { 
266         end = i;
267       }
268     fireValueChanged(beg, end, valueIsAdjusting);    
269   }
270
271   /**
272    * Gets the value of the {@link #leadAnchorNotificationEnabled} property.
273    * 
274    * @return The current property value
275    *
276    * @see #setLeadAnchorNotificationEnabled
277    */
278   public boolean isLeadAnchorNotificationEnabled()
279   {
280     return leadAnchorNotificationEnabled;
281   }
282
283   /**
284    * Sets the value of the {@link #leadAnchorNotificationEnabled} property.
285    * 
286    * @param flag The new property value
287    *
288    * @see #getLeadAnchorNotificationEnabled
289    */
290   public void setLeadAnchorNotificationEnabled(boolean l)
291   {
292     leadAnchorNotificationEnabled = l;
293   }
294
295
296   /**
297    * Gets the value of the {@link #valueIsAdjusting} property.
298    *
299    * @return The current property value
300    *
301    * @see #setValueIsAdjusting
302    */
303   public boolean getValueIsAdjusting()
304   {
305     return valueIsAdjusting;
306   }
307
308   /**
309    * Sets the value of the {@link #valueIsAdjusting} property.
310    *
311    * @param v The new property value
312    *
313    * @see #getValueIsAdjusting
314    */
315   public void setValueIsAdjusting(boolean v)
316   {
317     valueIsAdjusting = v;
318   }
319
320   /**
321    * Determines whether the selection is empty.
322    *
323    * @return <code>true</code> if the selection is empty, otherwise
324    * <code>false</code>
325    */
326   public boolean isSelectionEmpty()
327   {
328     return sel.isEmpty();
329   }
330
331
332   /**
333    * Gets the smallest index which is currently a member of a selection
334    * interval.
335    *
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
339    *
340    * @see #getMaxSelectionIndex
341    */
342   public int getMinSelectionIndex()
343   {
344     if (isSelectionEmpty())
345       return -1;
346     
347     return sel.nextSetBit(0);
348   }
349
350   /**
351    * Gets the largest index which is currently a member of a selection
352    * interval.
353    *
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
357    *
358    * @see #getMinSelectionIndex
359    */
360   public int getMaxSelectionIndex()
361   {
362     if (isSelectionEmpty())
363       return -1;
364
365     int mx = -1;
366     for(int i=sel.nextSetBit(0); i >= 0; i=sel.nextSetBit(i+1)) 
367       { 
368         mx = i;
369       }
370     return mx;
371   }
372
373   /**
374    * Determines whether a particular index is a member of a selection
375    * interval.
376    *
377    * @param a The index to search for
378    *
379    * @return <code>true</code> if the index is a member of a selection interval,
380    * otherwise <code>false</code>
381    */
382   public boolean isSelectedIndex(int a)
383   {
384     return sel.get(a);
385   }
386
387   /**
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.
393    *
394    * @param index0 The beginning of the range of indices to select
395    * @param index1 The end of the range of indices to select
396    *
397    * @see #setSelectionInterval
398    * @see #removeSelectionInterval
399    */
400   public void addSelectionInterval(int index0, int index1) 
401   {
402     if (selectionMode == SINGLE_SELECTION
403         || selectionMode == SINGLE_INTERVAL_SELECTION)
404       sel.clear();
405     
406     if (selectionMode == SINGLE_SELECTION)
407       index0 = index1;
408
409     int lo = Math.min(index0, index1);
410     int hi = Math.max(index0, index1);
411
412     sel.set(lo, hi+1);
413     fireValueChanged(lo, hi, valueIsAdjusting);
414   }
415
416
417   /**
418    * Deselects all indices in the inclusive range
419    * <code>[index0,index1]</code>.
420    *
421    * @param index0 The beginning of the range of indices to deselect
422    * @param index1 The end of the range of indices to deselect
423    *
424    * @see #addSelectionInterval
425    * @see #setSelectionInterval
426    */
427   public void removeSelectionInterval(int index0,
428                                       int index1)
429   {
430     int lo = Math.min(index0, index1);
431     int hi = Math.max(index0, index1);
432     sel.clear(lo, hi+1); 
433     fireValueChanged(lo, hi, valueIsAdjusting);
434   }
435
436   /**
437    * Removes all intervals in the selection set.
438    */
439   public void clearSelection()
440   {
441     int sz = sel.size();
442     sel.clear();
443     fireValueChanged(0, sz, valueIsAdjusting);
444   }
445   
446   /**
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
450    * selected.
451    *
452    * @param index0 The low end of the new selection 
453    * @param index1 The high end of the new selection
454    */
455   public void setSelectionInterval(int index0, int index1)
456   {
457     sel.clear();
458     if (selectionMode == SINGLE_SELECTION)
459       index0 = index1;
460
461     int lo = Math.min(index0, index1);
462     int hi = Math.max(index0, index1);
463     sel.set(lo, hi+1);
464     fireValueChanged(lo, hi, valueIsAdjusting);
465   }
466
467   /**
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}.
473    *
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
477    * or after it
478    */
479   public void insertIndexInterval(int index,
480                                   int length,
481                                   boolean before)
482   {
483     if (!before)
484       {        
485         index++;
486         length--;
487       }
488     BitSet tmp = sel.get(index, sel.size());
489     sel.clear(index, sel.size());
490     int n = tmp.size();
491     for (int i = 0; i < n; ++i)
492       sel.set(index + length + i, tmp.get(i));
493   }
494
495   /**
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
499    * ListModel}.
500    *
501    * @param index0 The first index to remove (inclusive)
502    * @param index1 The last index to remove (inclusive)
503    */
504   public void removeIndexInterval(int index0,
505                                   int index1)
506   {
507     int lo = Math.min(index0, index1);
508     int hi = Math.max(index0, index1);
509
510     BitSet tmp = sel.get(hi, sel.size());
511     sel.clear(lo, sel.size());
512     int n = tmp.size();
513     for (int i = 0; i < n; ++i)
514       sel.set(lo + i, tmp.get(i));
515   }
516
517   /**
518    * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
519    * ListSelectionListener} registered with this selection model.
520    *
521    * @param firstIndex The low index of the changed range
522    * @param lastIndex The high index of the changed range
523    */
524   protected void fireValueChanged(int firstIndex, int lastIndex)
525   {
526     fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
527   }
528   
529   /**
530    * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
531    * ListSelectionListener} registered with this selection model.
532    *
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
537    */
538   protected void fireValueChanged(int firstIndex, int lastIndex,
539                                   boolean isAdjusting)
540   {
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);
546   }
547
548   /**
549    * Adds a listener.
550    *
551    * @param listener The listener to add
552    *
553    * @see removeListSelectionListener
554    * @see getListSelectionListeners
555    */
556   public void addListSelectionListener(ListSelectionListener listener)
557   {
558     listenerList.add(ListSelectionListener.class, listener);
559   }
560
561   /**
562    * Removes a registered listener.
563    *
564    * @param listener The listener to remove
565    *
566    * @see addListSelectionListener
567    * @see getListSelectionListeners
568    */
569   public void removeListSelectionListener(ListSelectionListener listener)
570   {
571     listenerList.remove(ListSelectionListener.class, listener);
572   }
573
574   /**
575    * Returns an array of all registerers listeners.
576    *
577    * @param listenerType The type of listener to retrieve
578    *
579    * @return The array
580    *
581    * @see getListSelectionListener
582    * @since 1.3
583    */
584   public EventListener[] getListeners(Class listenerType)
585   {
586     return listenerList.getListeners(listenerType);
587   }
588
589   /**
590    * Returns an array of all registerd list selection listeners.
591    *
592    * @return the array
593    *
594    * @see addListSelectionListener
595    * @see removeListSelectionListener
596    * @see getListeners
597    * @since 1.4
598    */
599   public ListSelectionListener[] getListSelectionListeners()
600   {
601     return (ListSelectionListener[]) getListeners(ListSelectionListener.class);
602   }
603
604   /**
605    * Returns a clone of this object.
606    * <code>listenerList</code> don't gets duplicated.
607    *
608    * @return the cloned object
609    *
610    * @throws CloneNotSupportedException if an error occurs
611    */
612   public Object clone()
613     throws CloneNotSupportedException
614   {
615     DefaultListSelectionModel model =
616       (DefaultListSelectionModel) super.clone();
617     model.sel = (BitSet) sel.clone();
618     return model;
619   }
620 }