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;
111 return ((index < minIndex) || (index > maxIndex)) ? false : value.get(index);
112 }
113
114 /** {@inheritDoc} */
115 public boolean isSelectionEmpty() {
116 return (minIndex > maxIndex);
117 }
118
119 /** {@inheritDoc} */
120 public void addListSelectionListener(ListSelectionListener l) {
121 listenerList.add(ListSelectionListener.class, l);
122 }
123
124 /** {@inheritDoc} */
125 public void removeListSelectionListener(ListSelectionListener l) {
126 listenerList.remove(ListSelectionListener.class, l);
127 }
128
129 /**
130 * Returns an array of all the list selection listeners
131 * registered on this <code>DefaultListSelectionModel</code>.
132 *
133 * @return all of this model's <code>ListSelectionListener</code>s
134 * or an empty
135 * array if no list selection listeners are currently registered
136 *
137 * @see #addListSelectionListener
138 * @see #removeListSelectionListener
139 *
140 * @since 1.4
141 */
142 public ListSelectionListener[] getListSelectionListeners() {
143 return listenerList.getListeners(ListSelectionListener.class);
144 }
145
146 /**
147 * Notifies listeners that we have ended a series of adjustments.
148 * @param isAdjusting true if this is the final change in a series of
149 * adjustments
150 */
151 protected void fireValueChanged(boolean isAdjusting) {
152 if (lastChangedIndex == MIN) {
153 return;
154 }
155 /* Change the values before sending the event to the
156 * listeners in case the event causes a listener to make
157 * another change to the selection.
158 */
159 int oldFirstChangedIndex = firstChangedIndex;
160 int oldLastChangedIndex = lastChangedIndex;
161 firstChangedIndex = MAX;
162 lastChangedIndex = MIN;
163 fireValueChanged(oldFirstChangedIndex, oldLastChangedIndex, isAdjusting);
164 }
165
166
167 /**
168 * Notifies <code>ListSelectionListeners</code> that the value
169 * of the selection, in the closed interval <code>firstIndex</code>,
170 * <code>lastIndex</code>, has changed.
171 *
172 * @param firstIndex the first index in the interval
173 * @param lastIndex the last index in the interval
174 */
175 protected void fireValueChanged(int firstIndex, int lastIndex) {
176 fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
177 }
178
179 /**
180 * @param firstIndex the first index in the interval
181 * @param lastIndex the last index in the interval
182 * @param isAdjusting true if this is the final change in a series of
183 * adjustments
184 * @see EventListenerList
185 */
186 protected void fireValueChanged(int firstIndex, int lastIndex, boolean isAdjusting)
187 {
188 Object[] listeners = listenerList.getListenerList();
189 ListSelectionEvent e = null;
190
213 }
214 /* Change the values before sending the event to the
215 * listeners in case the event causes a listener to make
216 * another change to the selection.
217 */
218 int oldFirstAdjustedIndex = firstAdjustedIndex;
219 int oldLastAdjustedIndex = lastAdjustedIndex;
220 firstAdjustedIndex = MAX;
221 lastAdjustedIndex = MIN;
222
223 fireValueChanged(oldFirstAdjustedIndex, oldLastAdjustedIndex);
224 }
225
226 /**
227 * Returns an array of all the objects currently registered as
228 * <code><em>Foo</em>Listener</code>s
229 * upon this model.
230 * <code><em>Foo</em>Listener</code>s
231 * are registered using the <code>add<em>Foo</em>Listener</code> method.
232 * <p>
233 * You can specify the <code>listenerType</code> argument
234 * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
235 * For example, you can query a <code>DefaultListSelectionModel</code>
236 * instance <code>m</code>
237 * for its list selection listeners
238 * with the following code:
239 *
240 * <pre>ListSelectionListener[] lsls = (ListSelectionListener[])(m.getListeners(ListSelectionListener.class));</pre>
241 *
242 * If no such listeners exist,
243 * this method returns an empty array.
244 *
245 * @param <T> the type of {@code EventListener} class being requested
246 * @param listenerType the type of listeners requested;
247 * this parameter should specify an interface
248 * that descends from <code>java.util.EventListener</code>
249 * @return an array of all objects registered as
250 * <code><em>Foo</em>Listener</code>s
251 * on this model,
252 * or an empty array if no such
253 * listeners have been added
254 * @exception ClassCastException if <code>listenerType</code> doesn't
255 * specify a class or interface that implements
256 * <code>java.util.EventListener</code>
257 *
258 * @see #getListSelectionListeners
259 *
260 * @since 1.3
261 */
262 public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
263 return listenerList.getListeners(listenerType);
264 }
265
266 // Updates first and last change indices
267 private void markAsDirty(int r) {
268 if (r == -1) {
269 return;
270 }
271
272 firstAdjustedIndex = Math.min(firstAdjustedIndex, r);
273 lastAdjustedIndex = Math.max(lastAdjustedIndex, r);
274 }
275
276 // Sets the state at this index and update all relevant state.
336 to their cannonical values so that the next set command always works
337 just by using Math.min and Math.max.
338 */
339 if (isSelectionEmpty()) {
340 minIndex = MAX;
341 maxIndex = MIN;
342 }
343 }
344
345 /**
346 * Sets the value of the leadAnchorNotificationEnabled flag.
347 *
348 * @param flag boolean value for {@code leadAnchorNotificationEnabled}
349 * @see #isLeadAnchorNotificationEnabled()
350 */
351 public void setLeadAnchorNotificationEnabled(boolean flag) {
352 leadAnchorNotificationEnabled = flag;
353 }
354
355 /**
356 * Returns the value of the <code>leadAnchorNotificationEnabled</code> flag.
357 * When <code>leadAnchorNotificationEnabled</code> is true the model
358 * generates notification events with bounds that cover all the changes to
359 * the selection plus the changes to the lead and anchor indices.
360 * Setting the flag to false causes a narrowing of the event's bounds to
361 * include only the elements that have been selected or deselected since
362 * the last change. Either way, the model continues to maintain the lead
363 * and anchor variables internally. The default is true.
364 * <p>
365 * Note: It is possible for the lead or anchor to be changed without a
366 * change to the selection. Notification of these changes is often
367 * important, such as when the new lead or anchor needs to be updated in
368 * the view. Therefore, caution is urged when changing the default value.
369 *
370 * @return the value of the <code>leadAnchorNotificationEnabled</code> flag
371 * @see #setLeadAnchorNotificationEnabled(boolean)
372 */
373 public boolean isLeadAnchorNotificationEnabled() {
374 return leadAnchorNotificationEnabled;
375 }
376
377 private void updateLeadAnchorIndices(int anchorIndex, int leadIndex) {
378 if (leadAnchorNotificationEnabled) {
379 if (this.anchorIndex != anchorIndex) {
380 markAsDirty(this.anchorIndex);
381 markAsDirty(anchorIndex);
382 }
383
384 if (this.leadIndex != leadIndex) {
385 markAsDirty(this.leadIndex);
386 markAsDirty(leadIndex);
387 }
388 }
389 this.anchorIndex = anchorIndex;
390 this.leadIndex = leadIndex;
689 updateLeadAnchorIndices(anchorIndex, leadIndex);
690 }
691
692 fireValueChanged();
693 }
694
695
696 /** {@inheritDoc} */
697 public void setValueIsAdjusting(boolean isAdjusting) {
698 if (isAdjusting != this.isAdjusting) {
699 this.isAdjusting = isAdjusting;
700 this.fireValueChanged(isAdjusting);
701 }
702 }
703
704
705 /**
706 * Returns a string that displays and identifies this
707 * object's properties.
708 *
709 * @return a <code>String</code> representation of this object
710 */
711 public String toString() {
712 String s = ((getValueIsAdjusting()) ? "~" : "=") + value.toString();
713 return getClass().getName() + " " + Integer.toString(hashCode()) + " " + s;
714 }
715
716 /**
717 * Returns a clone of this selection model with the same selection.
718 * <code>listenerLists</code> are not duplicated.
719 *
720 * @exception CloneNotSupportedException if the selection model does not
721 * both (a) implement the Cloneable interface and (b) define a
722 * <code>clone</code> method.
723 */
724 public Object clone() throws CloneNotSupportedException {
725 DefaultListSelectionModel clone = (DefaultListSelectionModel)super.clone();
726 clone.value = (BitSet)value.clone();
727 clone.listenerList = new EventListenerList();
728 return clone;
729 }
730
731 /** {@inheritDoc} */
732 @Transient
733 public int getAnchorSelectionIndex() {
734 return anchorIndex;
735 }
736
737 /** {@inheritDoc} */
738 @Transient
739 public int getLeadSelectionIndex() {
740 return leadIndex;
741 }
742
797 * anchor and the new lead are either all selected or all deselected.
798 * If the value at the anchor index is selected, first clear all the
799 * values in the range [anchor, oldLeadIndex], then select all the values
800 * values in the range [anchor, newLeadIndex], where oldLeadIndex is the old
801 * leadIndex and newLeadIndex is the new one.
802 * <p>
803 * If the value at the anchor index is not selected, do the same thing in
804 * reverse selecting values in the old range and deselecting values in the
805 * new one.
806 * <p>
807 * Generate a single event for this change and notify all listeners.
808 * For the purposes of generating minimal bounds in this event, do the
809 * operation in a single pass; that way the first and last index inside the
810 * ListSelectionEvent that is broadcast will refer to cells that actually
811 * changed value because of this method. If, instead, this operation were
812 * done in two steps the effect on the selection state would be the same
813 * but two events would be generated and the bounds around the changed
814 * values would be wider, including cells that had been first cleared only
815 * to later be set.
816 * <p>
817 * This method can be used in the <code>mouseDragged</code> method
818 * of a UI class to extend a selection.
819 *
820 * @see #getLeadSelectionIndex
821 * @see #setAnchorSelectionIndex
822 */
823 public void setLeadSelectionIndex(int leadIndex) {
824 int anchorIndex = this.anchorIndex;
825
826 // only allow a -1 lead if the anchor is already -1
827 if (leadIndex == -1) {
828 if (anchorIndex == -1) {
829 updateLeadAnchorIndices(anchorIndex, leadIndex);
830 fireValueChanged();
831 }
832
833 return;
834 // otherwise, don't do anything if the anchor is -1
835 } else if (anchorIndex == -1) {
836 return;
837 }
|
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} 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;
111 return ((index < minIndex) || (index > maxIndex)) ? false : value.get(index);
112 }
113
114 /** {@inheritDoc} */
115 public boolean isSelectionEmpty() {
116 return (minIndex > maxIndex);
117 }
118
119 /** {@inheritDoc} */
120 public void addListSelectionListener(ListSelectionListener l) {
121 listenerList.add(ListSelectionListener.class, l);
122 }
123
124 /** {@inheritDoc} */
125 public void removeListSelectionListener(ListSelectionListener l) {
126 listenerList.remove(ListSelectionListener.class, l);
127 }
128
129 /**
130 * Returns an array of all the list selection listeners
131 * registered on this {@code DefaultListSelectionModel}.
132 *
133 * @return all of this model's {@code ListSelectionListener}s
134 * or an empty
135 * array if no list selection listeners are currently registered
136 *
137 * @see #addListSelectionListener
138 * @see #removeListSelectionListener
139 *
140 * @since 1.4
141 */
142 public ListSelectionListener[] getListSelectionListeners() {
143 return listenerList.getListeners(ListSelectionListener.class);
144 }
145
146 /**
147 * Notifies listeners that we have ended a series of adjustments.
148 * @param isAdjusting true if this is the final change in a series of
149 * adjustments
150 */
151 protected void fireValueChanged(boolean isAdjusting) {
152 if (lastChangedIndex == MIN) {
153 return;
154 }
155 /* Change the values before sending the event to the
156 * listeners in case the event causes a listener to make
157 * another change to the selection.
158 */
159 int oldFirstChangedIndex = firstChangedIndex;
160 int oldLastChangedIndex = lastChangedIndex;
161 firstChangedIndex = MAX;
162 lastChangedIndex = MIN;
163 fireValueChanged(oldFirstChangedIndex, oldLastChangedIndex, isAdjusting);
164 }
165
166
167 /**
168 * Notifies {@code ListSelectionListeners} that the value
169 * of the selection, in the closed interval {@code firstIndex},
170 * {@code lastIndex}, has changed.
171 *
172 * @param firstIndex the first index in the interval
173 * @param lastIndex the last index in the interval
174 */
175 protected void fireValueChanged(int firstIndex, int lastIndex) {
176 fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
177 }
178
179 /**
180 * @param firstIndex the first index in the interval
181 * @param lastIndex the last index in the interval
182 * @param isAdjusting true if this is the final change in a series of
183 * adjustments
184 * @see EventListenerList
185 */
186 protected void fireValueChanged(int firstIndex, int lastIndex, boolean isAdjusting)
187 {
188 Object[] listeners = listenerList.getListenerList();
189 ListSelectionEvent e = null;
190
213 }
214 /* Change the values before sending the event to the
215 * listeners in case the event causes a listener to make
216 * another change to the selection.
217 */
218 int oldFirstAdjustedIndex = firstAdjustedIndex;
219 int oldLastAdjustedIndex = lastAdjustedIndex;
220 firstAdjustedIndex = MAX;
221 lastAdjustedIndex = MIN;
222
223 fireValueChanged(oldFirstAdjustedIndex, oldLastAdjustedIndex);
224 }
225
226 /**
227 * Returns an array of all the objects currently registered as
228 * <code><em>Foo</em>Listener</code>s
229 * upon this model.
230 * <code><em>Foo</em>Listener</code>s
231 * are registered using the <code>add<em>Foo</em>Listener</code> method.
232 * <p>
233 * You can specify the {@code listenerType} argument
234 * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
235 * For example, you can query a {@code DefaultListSelectionModel}
236 * instance {@code m}
237 * for its list selection listeners
238 * with the following code:
239 *
240 * <pre>ListSelectionListener[] lsls = (ListSelectionListener[])(m.getListeners(ListSelectionListener.class));</pre>
241 *
242 * If no such listeners exist,
243 * this method returns an empty array.
244 *
245 * @param <T> the type of {@code EventListener} class being requested
246 * @param listenerType the type of listeners requested;
247 * this parameter should specify an interface
248 * that descends from {@code java.util.EventListener}
249 * @return an array of all objects registered as
250 * <code><em>Foo</em>Listener</code>s
251 * on this model,
252 * or an empty array if no such
253 * listeners have been added
254 * @exception ClassCastException if {@code listenerType} doesn't
255 * specify a class or interface that implements
256 * {@code java.util.EventListener}
257 *
258 * @see #getListSelectionListeners
259 *
260 * @since 1.3
261 */
262 public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
263 return listenerList.getListeners(listenerType);
264 }
265
266 // Updates first and last change indices
267 private void markAsDirty(int r) {
268 if (r == -1) {
269 return;
270 }
271
272 firstAdjustedIndex = Math.min(firstAdjustedIndex, r);
273 lastAdjustedIndex = Math.max(lastAdjustedIndex, r);
274 }
275
276 // Sets the state at this index and update all relevant state.
336 to their cannonical values so that the next set command always works
337 just by using Math.min and Math.max.
338 */
339 if (isSelectionEmpty()) {
340 minIndex = MAX;
341 maxIndex = MIN;
342 }
343 }
344
345 /**
346 * Sets the value of the leadAnchorNotificationEnabled flag.
347 *
348 * @param flag boolean value for {@code leadAnchorNotificationEnabled}
349 * @see #isLeadAnchorNotificationEnabled()
350 */
351 public void setLeadAnchorNotificationEnabled(boolean flag) {
352 leadAnchorNotificationEnabled = flag;
353 }
354
355 /**
356 * Returns the value of the {@code leadAnchorNotificationEnabled} flag.
357 * When {@code leadAnchorNotificationEnabled} is true the model
358 * generates notification events with bounds that cover all the changes to
359 * the selection plus the changes to the lead and anchor indices.
360 * Setting the flag to false causes a narrowing of the event's bounds to
361 * include only the elements that have been selected or deselected since
362 * the last change. Either way, the model continues to maintain the lead
363 * and anchor variables internally. The default is true.
364 * <p>
365 * Note: It is possible for the lead or anchor to be changed without a
366 * change to the selection. Notification of these changes is often
367 * important, such as when the new lead or anchor needs to be updated in
368 * the view. Therefore, caution is urged when changing the default value.
369 *
370 * @return the value of the {@code leadAnchorNotificationEnabled} flag
371 * @see #setLeadAnchorNotificationEnabled(boolean)
372 */
373 public boolean isLeadAnchorNotificationEnabled() {
374 return leadAnchorNotificationEnabled;
375 }
376
377 private void updateLeadAnchorIndices(int anchorIndex, int leadIndex) {
378 if (leadAnchorNotificationEnabled) {
379 if (this.anchorIndex != anchorIndex) {
380 markAsDirty(this.anchorIndex);
381 markAsDirty(anchorIndex);
382 }
383
384 if (this.leadIndex != leadIndex) {
385 markAsDirty(this.leadIndex);
386 markAsDirty(leadIndex);
387 }
388 }
389 this.anchorIndex = anchorIndex;
390 this.leadIndex = leadIndex;
689 updateLeadAnchorIndices(anchorIndex, leadIndex);
690 }
691
692 fireValueChanged();
693 }
694
695
696 /** {@inheritDoc} */
697 public void setValueIsAdjusting(boolean isAdjusting) {
698 if (isAdjusting != this.isAdjusting) {
699 this.isAdjusting = isAdjusting;
700 this.fireValueChanged(isAdjusting);
701 }
702 }
703
704
705 /**
706 * Returns a string that displays and identifies this
707 * object's properties.
708 *
709 * @return a {@code String} representation of this object
710 */
711 public String toString() {
712 String s = ((getValueIsAdjusting()) ? "~" : "=") + value.toString();
713 return getClass().getName() + " " + Integer.toString(hashCode()) + " " + s;
714 }
715
716 /**
717 * Returns a clone of this selection model with the same selection.
718 * {@code listenerLists} are not duplicated.
719 *
720 * @exception CloneNotSupportedException if the selection model does not
721 * both (a) implement the Cloneable interface and (b) define a
722 * {@code clone} method.
723 */
724 public Object clone() throws CloneNotSupportedException {
725 DefaultListSelectionModel clone = (DefaultListSelectionModel)super.clone();
726 clone.value = (BitSet)value.clone();
727 clone.listenerList = new EventListenerList();
728 return clone;
729 }
730
731 /** {@inheritDoc} */
732 @Transient
733 public int getAnchorSelectionIndex() {
734 return anchorIndex;
735 }
736
737 /** {@inheritDoc} */
738 @Transient
739 public int getLeadSelectionIndex() {
740 return leadIndex;
741 }
742
797 * anchor and the new lead are either all selected or all deselected.
798 * If the value at the anchor index is selected, first clear all the
799 * values in the range [anchor, oldLeadIndex], then select all the values
800 * values in the range [anchor, newLeadIndex], where oldLeadIndex is the old
801 * leadIndex and newLeadIndex is the new one.
802 * <p>
803 * If the value at the anchor index is not selected, do the same thing in
804 * reverse selecting values in the old range and deselecting values in the
805 * new one.
806 * <p>
807 * Generate a single event for this change and notify all listeners.
808 * For the purposes of generating minimal bounds in this event, do the
809 * operation in a single pass; that way the first and last index inside the
810 * ListSelectionEvent that is broadcast will refer to cells that actually
811 * changed value because of this method. If, instead, this operation were
812 * done in two steps the effect on the selection state would be the same
813 * but two events would be generated and the bounds around the changed
814 * values would be wider, including cells that had been first cleared only
815 * to later be set.
816 * <p>
817 * This method can be used in the {@code mouseDragged} method
818 * of a UI class to extend a selection.
819 *
820 * @see #getLeadSelectionIndex
821 * @see #setAnchorSelectionIndex
822 */
823 public void setLeadSelectionIndex(int leadIndex) {
824 int anchorIndex = this.anchorIndex;
825
826 // only allow a -1 lead if the anchor is already -1
827 if (leadIndex == -1) {
828 if (anchorIndex == -1) {
829 updateLeadAnchorIndices(anchorIndex, leadIndex);
830 fireValueChanged();
831 }
832
833 return;
834 // otherwise, don't do anything if the anchor is -1
835 } else if (anchorIndex == -1) {
836 return;
837 }
|