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/text/html/OptionListModel.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/text/html/OptionListModel.java+570−0
@@ -0,0 +1,570 @@
1+/*
2+ * Copyright (c) 1998, 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+package javax.swing.text.html;
26+
27+import javax.swing.*;
28+import javax.swing.event.*;
29+import java.util.EventListener;
30+import java.util.BitSet;
31+import java.io.Serializable;
32+
33+
34+/**
35+ * This class extends DefaultListModel, and also implements
36+ * the ListSelectionModel interface, allowing for it to store state
37+ * relevant to a SELECT form element which is implemented as a List.
38+ * If SELECT has a size attribute whose value is greater than 1,
39+ * or if allows multiple selection then a JList is used to
40+ * represent it and the OptionListModel is used as its model.
41+ * It also stores the initial state of the JList, to ensure an
42+ * accurate reset, if the user requests a reset of the form.
43+ *
44+ @author Sunita Mani
45+ */
46+
47+class OptionListModel extends DefaultListModel implements ListSelectionModel, Serializable {
48+
49+
50+ private static final int MIN = -1;
51+ private static final int MAX = Integer.MAX_VALUE;
52+ private int selectionMode = SINGLE_SELECTION;
53+ private int minIndex = MAX;
54+ private int maxIndex = MIN;
55+ private int anchorIndex = -1;
56+ private int leadIndex = -1;
57+ private int firstChangedIndex = MAX;
58+ private int lastChangedIndex = MIN;
59+ private boolean isAdjusting = false;
60+ private BitSet value = new BitSet(32);
61+ private BitSet initialValue = new BitSet(32);
62+ protected EventListenerList listenerList = new EventListenerList();
63+
64+ protected boolean leadAnchorNotificationEnabled = true;
65+
66+ public int getMinSelectionIndex() { return isSelectionEmpty() ? -1 : minIndex; }
67+
68+ public int getMaxSelectionIndex() { return maxIndex; }
69+
70+ public boolean getValueIsAdjusting() { return isAdjusting; }
71+
72+ public int getSelectionMode() { return selectionMode; }
73+
74+ public void setSelectionMode(int selectionMode) {
75+ switch (selectionMode) {
76+ case SINGLE_SELECTION:
77+ case SINGLE_INTERVAL_SELECTION:
78+ case MULTIPLE_INTERVAL_SELECTION:
79+ this.selectionMode = selectionMode;
80+ break;
81+ default:
82+ throw new IllegalArgumentException("invalid selectionMode");
83+ }
84+ }
85+
86+ public boolean isSelectedIndex(int index) {
87+ return ((index < minIndex) || (index > maxIndex)) ? false : value.get(index);
88+ }
89+
90+ public boolean isSelectionEmpty() {
91+ return (minIndex > maxIndex);
92+ }
93+
94+ public void addListSelectionListener(ListSelectionListener l) {
95+ listenerList.add(ListSelectionListener.class, l);
96+ }
97+
98+ public void removeListSelectionListener(ListSelectionListener l) {
99+ listenerList.remove(ListSelectionListener.class, l);
100+ }
101+
102+ /**
103+ * Returns an array of all the <code>ListSelectionListener</code>s added
104+ * to this OptionListModel with addListSelectionListener().
105+ *
106+ * @return all of the <code>ListSelectionListener</code>s added or an empty
107+ * array if no listeners have been added
108+ * @since 1.4
109+ */
110+ public ListSelectionListener[] getListSelectionListeners() {
111+ return listenerList.getListeners(ListSelectionListener.class);
112+ }
113+
114+ /**
115+ * Notify listeners that we are beginning or ending a
116+ * series of value changes
117+ */
118+ protected void fireValueChanged(boolean isAdjusting) {
119+ fireValueChanged(getMinSelectionIndex(), getMaxSelectionIndex(), isAdjusting);
120+ }
121+
122+
123+ /**
124+ * Notify ListSelectionListeners that the value of the selection,
125+ * in the closed interval firstIndex,lastIndex, has changed.
126+ */
127+ protected void fireValueChanged(int firstIndex, int lastIndex) {
128+ fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
129+ }
130+
131+ /**
132+ * @param firstIndex The first index in the interval.
133+ * @param lastIndex The last index in the interval.
134+ * @param isAdjusting True if this is the final change in a series of them.
135+ * @see EventListenerList
136+ */
137+ protected void fireValueChanged(int firstIndex, int lastIndex, boolean isAdjusting)
138+ {
139+ Object[] listeners = listenerList.getListenerList();
140+ ListSelectionEvent e = null;
141+
142+ for (int i = listeners.length - 2; i >= 0; i -= 2) {
143+ if (listeners[i] == ListSelectionListener.class) {
144+ if (e == null) {
145+ e = new ListSelectionEvent(this, firstIndex, lastIndex, isAdjusting);
146+ }
147+ ((ListSelectionListener)listeners[i+1]).valueChanged(e);
148+ }
149+ }
150+ }
151+
152+ private void fireValueChanged() {
153+ if (lastChangedIndex == MIN) {
154+ return;
155+ }
156+ /* Change the values before sending the event to the
157+ * listeners in case the event causes a listener to make
158+ * another change to the selection.
159+ */
160+ int oldFirstChangedIndex = firstChangedIndex;
161+ int oldLastChangedIndex = lastChangedIndex;
162+ firstChangedIndex = MAX;
163+ lastChangedIndex = MIN;
164+ fireValueChanged(oldFirstChangedIndex, oldLastChangedIndex);
165+ }
166+
167+
168+ // Update first and last change indices
169+ private void markAsDirty(int r) {
170+ firstChangedIndex = Math.min(firstChangedIndex, r);
171+ lastChangedIndex = Math.max(lastChangedIndex, r);
172+ }
173+
174+ // Set the state at this index and update all relevant state.
175+ private void set(int r) {
176+ if (value.get(r)) {
177+ return;
178+ }
179+ value.set(r);
180+ Option option = (Option)get(r);
181+ option.setSelection(true);
182+ markAsDirty(r);
183+
184+ // Update minimum and maximum indices
185+ minIndex = Math.min(minIndex, r);
186+ maxIndex = Math.max(maxIndex, r);
187+ }
188+
189+ // Clear the state at this index and update all relevant state.
190+ private void clear(int r) {
191+ if (!value.get(r)) {
192+ return;
193+ }
194+ value.clear(r);
195+ Option option = (Option)get(r);
196+ option.setSelection(false);
197+ markAsDirty(r);
198+
199+ // Update minimum and maximum indices
200+ /*
201+ If (r > minIndex) the minimum has not changed.
202+ The case (r < minIndex) is not possible because r'th value was set.
203+ We only need to check for the case when lowest entry has been cleared,
204+ and in this case we need to search for the first value set above it.
205+ */
206+ if (r == minIndex) {
207+ for(minIndex = minIndex + 1; minIndex <= maxIndex; minIndex++) {
208+ if (value.get(minIndex)) {
209+ break;
210+ }
211+ }
212+ }
213+ /*
214+ If (r < maxIndex) the maximum has not changed.
215+ The case (r > maxIndex) is not possible because r'th value was set.
216+ We only need to check for the case when highest entry has been cleared,
217+ and in this case we need to search for the first value set below it.
218+ */
219+ if (r == maxIndex) {
220+ for(maxIndex = maxIndex - 1; minIndex <= maxIndex; maxIndex--) {
221+ if (value.get(maxIndex)) {
222+ break;
223+ }
224+ }
225+ }
226+ /* Performance note: This method is called from inside a loop in
227+ changeSelection() but we will only iterate in the loops
228+ above on the basis of one iteration per deselected cell - in total.
229+ Ie. the next time this method is called the work of the previous
230+ deselection will not be repeated.
231+
232+ We also don't need to worry about the case when the min and max
233+ values are in their unassigned states. This cannot happen because
234+ this method's initial check ensures that the selection was not empty
235+ and therefore that the minIndex and maxIndex had 'real' values.
236+
237+ If we have cleared the whole selection, set the minIndex and maxIndex
238+ to their cannonical values so that the next set command always works
239+ just by using Math.min and Math.max.
240+ */
241+ if (isSelectionEmpty()) {
242+ minIndex = MAX;
243+ maxIndex = MIN;
244+ }
245+ }
246+
247+ /**
248+ * Sets the value of the leadAnchorNotificationEnabled flag.
249+ * @see #isLeadAnchorNotificationEnabled()
250+ */
251+ public void setLeadAnchorNotificationEnabled(boolean flag) {
252+ leadAnchorNotificationEnabled = flag;
253+ }
254+
255+ /**
256+ * Returns the value of the leadAnchorNotificationEnabled flag.
257+ * When leadAnchorNotificationEnabled is true the model
258+ * generates notification events with bounds that cover all the changes to
259+ * the selection plus the changes to the lead and anchor indices.
260+ * Setting the flag to false causes a norrowing of the event's bounds to
261+ * include only the elements that have been selected or deselected since
262+ * the last change. Either way, the model continues to maintain the lead
263+ * and anchor variables internally. The default is true.
264+ * @return the value of the leadAnchorNotificationEnabled flag
265+ * @see #setLeadAnchorNotificationEnabled(boolean)
266+ */
267+ public boolean isLeadAnchorNotificationEnabled() {
268+ return leadAnchorNotificationEnabled;
269+ }
270+
271+ private void updateLeadAnchorIndices(int anchorIndex, int leadIndex) {
272+ if (leadAnchorNotificationEnabled) {
273+ if (this.anchorIndex != anchorIndex) {
274+ if (this.anchorIndex != -1) { // The unassigned state.
275+ markAsDirty(this.anchorIndex);
276+ }
277+ markAsDirty(anchorIndex);
278+ }
279+
280+ if (this.leadIndex != leadIndex) {
281+ if (this.leadIndex != -1) { // The unassigned state.
282+ markAsDirty(this.leadIndex);
283+ }
284+ markAsDirty(leadIndex);
285+ }
286+ }
287+ this.anchorIndex = anchorIndex;
288+ this.leadIndex = leadIndex;
289+ }
290+
291+ private boolean contains(int a, int b, int i) {
292+ return (i >= a) && (i <= b);
293+ }
294+
295+ private void changeSelection(int clearMin, int clearMax,
296+ int setMin, int setMax, boolean clearFirst) {
297+ for(int i = Math.min(setMin, clearMin); i <= Math.max(setMax, clearMax); i++) {
298+
299+ boolean shouldClear = contains(clearMin, clearMax, i);
300+ boolean shouldSet = contains(setMin, setMax, i);
301+
302+ if (shouldSet && shouldClear) {
303+ if (clearFirst) {
304+ shouldClear = false;
305+ }
306+ else {
307+ shouldSet = false;
308+ }
309+ }
310+
311+ if (shouldSet) {
312+ set(i);
313+ }
314+ if (shouldClear) {
315+ clear(i);
316+ }
317+ }
318+ fireValueChanged();
319+ }
320+
321+ /* Change the selection with the effect of first clearing the values
322+ * in the inclusive range [clearMin, clearMax] then setting the values
323+ * in the inclusive range [setMin, setMax]. Do this in one pass so
324+ * that no values are cleared if they would later be set.
325+ */
326+ private void changeSelection(int clearMin, int clearMax, int setMin, int setMax) {
327+ changeSelection(clearMin, clearMax, setMin, setMax, true);
328+ }
329+
330+ public void clearSelection() {
331+ removeSelectionInterval(minIndex, maxIndex);
332+ }
333+
334+ public void setSelectionInterval(int index0, int index1) {
335+ if (index0 == -1 || index1 == -1) {
336+ return;
337+ }
338+
339+ if (getSelectionMode() == SINGLE_SELECTION) {
340+ index0 = index1;
341+ }
342+
343+ updateLeadAnchorIndices(index0, index1);
344+
345+ int clearMin = minIndex;
346+ int clearMax = maxIndex;
347+ int setMin = Math.min(index0, index1);
348+ int setMax = Math.max(index0, index1);
349+ changeSelection(clearMin, clearMax, setMin, setMax);
350+ }
351+
352+ public void addSelectionInterval(int index0, int index1)
353+ {
354+ if (index0 == -1 || index1 == -1) {
355+ return;
356+ }
357+
358+ if (getSelectionMode() != MULTIPLE_INTERVAL_SELECTION) {
359+ setSelectionInterval(index0, index1);
360+ return;
361+ }
362+
363+ updateLeadAnchorIndices(index0, index1);
364+
365+ int clearMin = MAX;
366+ int clearMax = MIN;
367+ int setMin = Math.min(index0, index1);
368+ int setMax = Math.max(index0, index1);
369+ changeSelection(clearMin, clearMax, setMin, setMax);
370+ }
371+
372+
373+ public void removeSelectionInterval(int index0, int index1)
374+ {
375+ if (index0 == -1 || index1 == -1) {
376+ return;
377+ }
378+
379+ updateLeadAnchorIndices(index0, index1);
380+
381+ int clearMin = Math.min(index0, index1);
382+ int clearMax = Math.max(index0, index1);
383+ int setMin = MAX;
384+ int setMax = MIN;
385+ changeSelection(clearMin, clearMax, setMin, setMax);
386+ }
387+
388+ private void setState(int index, boolean state) {
389+ if (state) {
390+ set(index);
391+ }
392+ else {
393+ clear(index);
394+ }
395+ }
396+
397+ /**
398+ * Insert length indices beginning before/after index. If the value
399+ * at index is itself selected, set all of the newly inserted
400+ * items, otherwise leave them unselected. This method is typically
401+ * called to sync the selection model with a corresponding change
402+ * in the data model.
403+ */
404+ public void insertIndexInterval(int index, int length, boolean before)
405+ {
406+ /* The first new index will appear at insMinIndex and the last
407+ * one will appear at insMaxIndex
408+ */
409+ int insMinIndex = (before) ? index : index + 1;
410+ int insMaxIndex = (insMinIndex + length) - 1;
411+
412+ /* Right shift the entire bitset by length, beginning with
413+ * index-1 if before is true, index+1 if it's false (i.e. with
414+ * insMinIndex).
415+ */
416+ for(int i = maxIndex; i >= insMinIndex; i--) {
417+ setState(i + length, value.get(i));
418+ }
419+
420+ /* Initialize the newly inserted indices.
421+ */
422+ boolean setInsertedValues = value.get(index);
423+ for(int i = insMinIndex; i <= insMaxIndex; i++) {
424+ setState(i, setInsertedValues);
425+ }
426+ }
427+
428+
429+ /**
430+ * Remove the indices in the interval index0,index1 (inclusive) from
431+ * the selection model. This is typically called to sync the selection
432+ * model width a corresponding change in the data model. Note
433+ * that (as always) index0 can be greater than index1.
434+ */
435+ public void removeIndexInterval(int index0, int index1)
436+ {
437+ int rmMinIndex = Math.min(index0, index1);
438+ int rmMaxIndex = Math.max(index0, index1);
439+ int gapLength = (rmMaxIndex - rmMinIndex) + 1;
440+
441+ /* Shift the entire bitset to the left to close the index0, index1
442+ * gap.
443+ */
444+ for(int i = rmMinIndex; i <= maxIndex; i++) {
445+ setState(i, value.get(i + gapLength));
446+ }
447+ }
448+
449+
450+ public void setValueIsAdjusting(boolean isAdjusting) {
451+ if (isAdjusting != this.isAdjusting) {
452+ this.isAdjusting = isAdjusting;
453+ this.fireValueChanged(isAdjusting);
454+ }
455+ }
456+
457+
458+ public String toString() {
459+ String s = ((getValueIsAdjusting()) ? "~" : "=") + value.toString();
460+ return getClass().getName() + " " + Integer.toString(hashCode()) + " " + s;
461+ }
462+
463+ /**
464+ * Returns a clone of the receiver with the same selection.
465+ * <code>listenerLists</code> are not duplicated.
466+ *
467+ * @return a clone of the receiver
468+ * @exception CloneNotSupportedException if the receiver does not
469+ * both (a) implement the <code>Cloneable</code> interface
470+ * and (b) define a <code>clone</code> method
471+ */
472+ public Object clone() throws CloneNotSupportedException {
473+ OptionListModel clone = (OptionListModel)super.clone();
474+ clone.value = (BitSet)value.clone();
475+ clone.listenerList = new EventListenerList();
476+ return clone;
477+ }
478+
479+ public int getAnchorSelectionIndex() {
480+ return anchorIndex;
481+ }
482+
483+ public int getLeadSelectionIndex() {
484+ return leadIndex;
485+ }
486+
487+ /**
488+ * Set the anchor selection index, leaving all selection values unchanged.
489+ *
490+ * @see #getAnchorSelectionIndex
491+ * @see #setLeadSelectionIndex
492+ */
493+ public void setAnchorSelectionIndex(int anchorIndex) {
494+ this.anchorIndex = anchorIndex;
495+ }
496+
497+ /**
498+ * Set the lead selection index, ensuring that values between the
499+ * anchor and the new lead are either all selected or all deselected.
500+ * If the value at the anchor index is selected, first clear all the
501+ * values in the range [anchor, oldLeadIndex], then select all the values
502+ * values in the range [anchor, newLeadIndex], where oldLeadIndex is the old
503+ * leadIndex and newLeadIndex is the new one.
504+ * <p>
505+ * If the value at the anchor index is not selected, do the same thing in reverse,
506+ * selecting values in the old range and deslecting values in the new one.
507+ * <p>
508+ * Generate a single event for this change and notify all listeners.
509+ * For the purposes of generating minimal bounds in this event, do the
510+ * operation in a single pass; that way the first and last index inside the
511+ * ListSelectionEvent that is broadcast will refer to cells that actually
512+ * changed value because of this method. If, instead, this operation were
513+ * done in two steps the effect on the selection state would be the same
514+ * but two events would be generated and the bounds around the changed values
515+ * would be wider, including cells that had been first cleared and only
516+ * to later be set.
517+ * <p>
518+ * This method can be used in the mouseDragged() method of a UI class
519+ * to extend a selection.
520+ *
521+ * @see #getLeadSelectionIndex
522+ * @see #setAnchorSelectionIndex
523+ */
524+ public void setLeadSelectionIndex(int leadIndex) {
525+ int anchorIndex = this.anchorIndex;
526+ if (getSelectionMode() == SINGLE_SELECTION) {
527+ anchorIndex = leadIndex;
528+ }
529+
530+ int oldMin = Math.min(this.anchorIndex, this.leadIndex);
531+ int oldMax = Math.max(this.anchorIndex, this.leadIndex);
532+ int newMin = Math.min(anchorIndex, leadIndex);
533+ int newMax = Math.max(anchorIndex, leadIndex);
534+ if (value.get(this.anchorIndex)) {
535+ changeSelection(oldMin, oldMax, newMin, newMax);
536+ }
537+ else {
538+ changeSelection(newMin, newMax, oldMin, oldMax, false);
539+ }
540+ this.anchorIndex = anchorIndex;
541+ this.leadIndex = leadIndex;
542+ }
543+
544+
545+ /**
546+ * This method is responsible for storing the state
547+ * of the initial selection. If the selectionMode
548+ * is the default, i.e allowing only for SINGLE_SELECTION,
549+ * then the very last OPTION that has the selected
550+ * attribute set wins.
551+ */
552+ public void setInitialSelection(int i) {
553+ if (initialValue.get(i)) {
554+ return;
555+ }
556+ if (selectionMode == SINGLE_SELECTION) {
557+ // reset to empty
558+ initialValue.and(new BitSet());
559+ }
560+ initialValue.set(i);
561+ }
562+
563+ /**
564+ * Fetches the BitSet that represents the initial
565+ * set of selected items in the list.
566+ */
567+ public BitSet getInitialSelection() {
568+ return initialValue;
569+ }
570+}