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 */
25package javax.swing.text.html;
26
27import javax.swing.*;
28import javax.swing.event.*;
29import java.util.EventListener;
30import java.util.BitSet;
31import 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
47class 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}