001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package javax.swing;
019:
020: import java.awt.Rectangle;
021: import java.io.Serializable;
022: import javax.swing.event.EventListenerList;
023: import javax.swing.event.ListSelectionEvent;
024: import javax.swing.event.ListSelectionListener;
025: import org.apache.harmony.awt.gl.MultiRectArea;
026:
027: import org.apache.harmony.x.swing.internal.nls.Messages;
028:
029: /**
030: * <p>
031: * <i>DefaultListSelectionModel</i>
032: * </p>
033: * <h3>Implementation Notes:</h3>
034: * <ul>
035: * <li>The <code>serialVersionUID</code> fields are explicitly declared as a performance
036: * optimization, not as a guarantee of serialization compatibility.</li>
037: * </ul>
038: */
039: public class DefaultListSelectionModel implements ListSelectionModel,
040: Cloneable, Serializable {
041: private static final long serialVersionUID = -3207109908101807625L;
042:
043: private static final int NOT_SET = -1;
044:
045: private static class Segment extends Rectangle {
046: private static final long serialVersionUID = 1L;
047:
048: public Segment(int begin, int end) {
049: super (Math.min(begin, end), 0, Math.abs(end - begin) + 1, 1);
050: }
051:
052: public void add(Segment s) {
053: if (s == null || s.isEmpty()) {
054: return;
055: } else if (isEmpty()) {
056: setBounds(s);
057: } else {
058: setBounds(this .union(s));
059: }
060: }
061:
062: public int getBeginIndex() {
063: return x;
064: }
065:
066: public int getEndIndex() {
067: return x + width - 1;
068: }
069:
070: public int getLength() {
071: return width;
072: }
073: }
074:
075: private static class Selection extends MultiRectArea implements
076: Cloneable {
077: private static final Rectangle EMPTY_RECTANGLE = new Rectangle();
078:
079: public Selection() {
080: super ();
081: }
082:
083: private Selection(Selection s) {
084: super (s);
085: }
086:
087: public void clear() {
088: intersect(EMPTY_RECTANGLE);
089: }
090:
091: public boolean contains(int index) {
092: return contains(index, 0);
093: }
094:
095: public void insertIndices(int index, int length,
096: boolean multiSelectionAllowed) {
097: MultiRectArea modified = new MultiRectArea();
098: Rectangle[] rects = getRectangles();
099: for (int i = 0; i < rects.length; i++) {
100: Rectangle rect = (Rectangle) rects[i].clone();
101: if (index < rect.x) {
102: rect.x += length;
103: } else if (rect.x <= index
104: && index < rect.x + rect.width) {
105: if (multiSelectionAllowed) {
106: rect.width += length;
107: } else {
108: rect.x += length;
109: }
110: }
111: modified.add(rect);
112: }
113: clear();
114: add(modified);
115: }
116:
117: public void removeIndices(int index, int length) {
118: MultiRectArea modified = new MultiRectArea();
119: Rectangle[] rects = getRectangles();
120: for (int i = 0; i < rects.length; i++) {
121: Rectangle rect = rects[i];
122: int rectEnd = rect.x + rect.width - length - 1;
123: if (index <= rect.x) {
124: if (rectEnd >= index) {
125: int rectBegin = rect.x - length < index ? index
126: : rect.x - length;
127: modified.add(new Segment(rectBegin, rectEnd));
128: }
129: } else if (rect.x < index
130: && index < rect.x + rect.width) {
131: if (rectEnd < index - 1) {
132: rectEnd = index - 1;
133: }
134: modified.add(new Segment(rect.x, rectEnd));
135: } else {
136: modified.add((Rectangle) rect.clone());
137: }
138: }
139: clear();
140: add(modified);
141: }
142:
143: public Segment getDifferenceBounds(Selection anotherSelection) {
144: MultiRectArea this FromAnother = MultiRectArea.subtract(
145: this , anotherSelection);
146: MultiRectArea anotherFromThis = MultiRectArea.subtract(
147: anotherSelection, this );
148: MultiRectArea diff = MultiRectArea.union(this FromAnother,
149: anotherFromThis);
150: if (diff.isEmpty()) {
151: return null;
152: }
153: Rectangle diffBounds = diff.getBounds();
154: return new Segment(diffBounds.x, diffBounds.x
155: + diffBounds.width - 1);
156: }
157:
158: public int getBeginIndex() {
159: return getBounds().x;
160: }
161:
162: public int getEndIndex() {
163: Rectangle bounds = getBounds();
164: return bounds.x + bounds.width - 1;
165: }
166:
167: @Override
168: public Object clone() {
169: return new Selection(this );
170: }
171: }
172:
173: protected boolean leadAnchorNotificationEnabled = true;
174:
175: protected EventListenerList listenerList = new EventListenerList();
176:
177: private Selection selection = new Selection();
178:
179: private int anchorSelectionIndex = NOT_SET;
180:
181: private int leadSelectionIndex = NOT_SET;
182:
183: private Segment adjustingInterval;
184:
185: private int selectionMode = MULTIPLE_INTERVAL_SELECTION;
186:
187: private boolean valueIsAdjusting;
188:
189: public void setSelectionInterval(int intervalEnd1, int intervalEnd2) {
190: if (!isValidInterval(intervalEnd1, intervalEnd2)) {
191: return;
192: }
193: Selection oldSelection = (Selection) selection.clone();
194: selection.clear();
195: setSelectionAndUpdateLeadAnchor(intervalEnd1, intervalEnd2,
196: oldSelection);
197: }
198:
199: public void addSelectionInterval(int intervalEnd1, int intervalEnd2) {
200: if (!isValidInterval(intervalEnd1, intervalEnd2)) {
201: return;
202: }
203: Selection oldSelection = (Selection) selection.clone();
204: if (selectionMode == SINGLE_SELECTION
205: || selectionMode == SINGLE_INTERVAL_SELECTION) {
206: selection.clear();
207: }
208: setSelectionAndUpdateLeadAnchor(intervalEnd1, intervalEnd2,
209: oldSelection);
210: }
211:
212: public void removeSelectionInterval(int intervalEnd1,
213: int intervalEnd2) {
214: if (!isValidInterval(intervalEnd1, intervalEnd2)) {
215: return;
216: }
217: Segment interval = new Segment(intervalEnd1, intervalEnd2);
218: Selection oldSelection = (Selection) selection.clone();
219: selection.substract(interval);
220: int oldAnchor = anchorSelectionIndex;
221: int oldLead = leadSelectionIndex;
222: anchorSelectionIndex = intervalEnd1;
223: leadSelectionIndex = intervalEnd2;
224: doNotification(selection.getDifferenceBounds(oldSelection),
225: oldAnchor, oldLead);
226: }
227:
228: public void clearSelection() {
229: Selection oldSelection = (Selection) selection.clone();
230: selection.clear();
231: doNotification(selection.getDifferenceBounds(oldSelection),
232: anchorSelectionIndex, leadSelectionIndex);
233: }
234:
235: public boolean isSelectedIndex(int index) {
236: return selection.contains(index);
237: }
238:
239: public boolean isSelectionEmpty() {
240: return selection.isEmpty();
241: }
242:
243: public int getMaxSelectionIndex() {
244: return isSelectionEmpty() ? NOT_SET : selection.getEndIndex();
245: }
246:
247: public int getMinSelectionIndex() {
248: return isSelectionEmpty() ? NOT_SET : selection.getBeginIndex();
249: }
250:
251: public void insertIndexInterval(int index, int length,
252: boolean before) {
253: if (!isValidInterval(index, length)) {
254: return;
255: }
256: Selection oldSelection = (Selection) selection.clone();
257: int insertionIndex = before ? index : index + 1;
258: selection.insertIndices(index, length,
259: selectionMode != SINGLE_SELECTION);
260: int oldAnchor = anchorSelectionIndex;
261: int oldLead = leadSelectionIndex;
262: if (anchorSelectionIndex >= insertionIndex) {
263: anchorSelectionIndex += length;
264: }
265: if (leadSelectionIndex >= insertionIndex) {
266: leadSelectionIndex += length;
267: }
268: doNotification(selection.getDifferenceBounds(oldSelection),
269: oldAnchor, oldLead);
270: }
271:
272: public void removeIndexInterval(int intervalEnd1, int intervalEnd2) {
273: if (!isValidInterval(intervalEnd1, intervalEnd2)) {
274: return;
275: }
276: Selection oldSelection = (Selection) selection.clone();
277: Segment removalInterval = new Segment(intervalEnd1,
278: intervalEnd2);
279: selection.removeIndices(removalInterval.getBeginIndex(),
280: removalInterval.getLength());
281: int oldAnchor = anchorSelectionIndex;
282: int oldLead = leadSelectionIndex;
283: anchorSelectionIndex = adjustLeadAnchorIndexForIndicesRemoval(
284: anchorSelectionIndex, removalInterval);
285: leadSelectionIndex = adjustLeadAnchorIndexForIndicesRemoval(
286: leadSelectionIndex, removalInterval);
287: doNotification(selection.getDifferenceBounds(oldSelection),
288: oldAnchor, oldLead);
289: }
290:
291: public void setAnchorSelectionIndex(int anchorIndex) {
292: int oldAnchor = anchorSelectionIndex;
293: anchorSelectionIndex = anchorIndex;
294: doNotification(null, oldAnchor, leadSelectionIndex);
295: }
296:
297: public int getAnchorSelectionIndex() {
298: return anchorSelectionIndex;
299: }
300:
301: public void setLeadSelectionIndex(int leadIndex) {
302: if (leadIndex < 0 && anchorSelectionIndex < 0) {
303: leadSelectionIndex = leadIndex;
304: }
305: if (leadIndex < 0 || anchorSelectionIndex < 0) {
306: return;
307: }
308: Selection oldSelection = (Selection) selection.clone();
309: int oldLead = leadSelectionIndex;
310: leadSelectionIndex = leadIndex;
311: Segment oldSegment = new Segment(anchorSelectionIndex, oldLead);
312: Segment newSegment = new Segment(anchorSelectionIndex,
313: leadSelectionIndex);
314: if (selection.contains(anchorSelectionIndex)) {
315: selection.substract(oldSegment);
316: selection.add(newSegment);
317: } else {
318: selection.add(oldSegment);
319: selection.substract(newSegment);
320: }
321: doNotification(selection.getDifferenceBounds(oldSelection),
322: anchorSelectionIndex, oldLead);
323: }
324:
325: public void moveLeadSelectionIndex(int leadIndex) {
326: if (leadIndex < 0 || leadSelectionIndex == leadIndex) {
327: return;
328: }
329: int oldIndex = leadSelectionIndex;
330: leadSelectionIndex = leadIndex;
331: doNotification(null, anchorSelectionIndex, oldIndex);
332: }
333:
334: public int getLeadSelectionIndex() {
335: return leadSelectionIndex;
336: }
337:
338: public void setLeadAnchorNotificationEnabled(boolean enabled) {
339: leadAnchorNotificationEnabled = enabled;
340: }
341:
342: public boolean isLeadAnchorNotificationEnabled() {
343: return leadAnchorNotificationEnabled;
344: }
345:
346: public int getSelectionMode() {
347: return selectionMode;
348: }
349:
350: public void setSelectionMode(int selectionMode) {
351: if (selectionMode != SINGLE_SELECTION
352: && selectionMode != SINGLE_INTERVAL_SELECTION
353: && selectionMode != MULTIPLE_INTERVAL_SELECTION) {
354: throw new IllegalArgumentException(Messages
355: .getString("swing.08")); //$NON-NLS-1$
356: }
357: this .selectionMode = selectionMode;
358: }
359:
360: public void setValueIsAdjusting(boolean isAdjusting) {
361: valueIsAdjusting = isAdjusting;
362: if (!isAdjusting) {
363: fireValueChanged(isAdjusting);
364: }
365: }
366:
367: public boolean getValueIsAdjusting() {
368: return valueIsAdjusting;
369: }
370:
371: public void addListSelectionListener(ListSelectionListener l) {
372: listenerList.add(ListSelectionListener.class, l);
373: }
374:
375: public void removeListSelectionListener(ListSelectionListener l) {
376: listenerList.remove(ListSelectionListener.class, l);
377: }
378:
379: public ListSelectionListener[] getListSelectionListeners() {
380: return getListeners(ListSelectionListener.class);
381: }
382:
383: public <T extends java.util.EventListener> T[] getListeners(
384: Class<T> listenerType) {
385: return listenerList.getListeners(listenerType);
386: }
387:
388: @Override
389: public Object clone() throws CloneNotSupportedException {
390: DefaultListSelectionModel result = new DefaultListSelectionModel();
391: result.anchorSelectionIndex = anchorSelectionIndex;
392: result.leadSelectionIndex = leadSelectionIndex;
393: result.leadAnchorNotificationEnabled = leadAnchorNotificationEnabled;
394: result.valueIsAdjusting = valueIsAdjusting;
395: result.selectionMode = selectionMode;
396: result.selection = (Selection) selection.clone();
397: return result;
398: }
399:
400: @Override
401: public String toString() {
402: return getClass().toString() + ": leadIndex="
403: + leadSelectionIndex + ", anchorIndex="
404: + anchorSelectionIndex + ", isEmpty="
405: + isSelectionEmpty();
406: }
407:
408: protected void fireValueChanged(boolean isAdjusting) {
409: if (adjustingInterval != null) {
410: fireValueChanged(adjustingInterval.getBeginIndex(),
411: adjustingInterval.getEndIndex(), isAdjusting);
412: adjustingInterval = null;
413: }
414: }
415:
416: protected void fireValueChanged(int firstIndex, int lastIndex) {
417: fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
418: }
419:
420: protected void fireValueChanged(int firstIndex, int lastIndex,
421: boolean isAdjusting) {
422: fireListSelectionEvent(new ListSelectionEvent(this , firstIndex,
423: lastIndex, isAdjusting));
424: }
425:
426: private void fireListSelectionEvent(ListSelectionEvent event) {
427: ListSelectionListener[] listeners = getListSelectionListeners();
428: for (int i = 0; i < listeners.length; i++) {
429: listeners[i].valueChanged(event);
430: }
431: }
432:
433: private void doNotification(Segment changedInterval,
434: int oldAnchorIndex, int oldLeadIndex) {
435: Segment fireInterval = changedInterval;
436: if (leadAnchorNotificationEnabled) {
437: Segment anchorLeadInterval = getLeadAnchorInterval(
438: oldAnchorIndex, oldLeadIndex);
439: fireInterval = mergeIntervals(fireInterval,
440: anchorLeadInterval);
441: }
442: if (fireInterval == null) {
443: return;
444: }
445: if (valueIsAdjusting) {
446: adjustingInterval = mergeIntervals(adjustingInterval,
447: fireInterval);
448: }
449: fireValueChanged(fireInterval.getBeginIndex(), fireInterval
450: .getEndIndex());
451: }
452:
453: private Segment mergeIntervals(Segment interval1, Segment interval2) {
454: Segment result = interval1;
455: if (result != null) {
456: result.add(interval2);
457: } else {
458: result = interval2;
459: }
460: return result;
461: }
462:
463: private Segment getLeadAnchorInterval(int oldAnchorIndex,
464: int oldLeadIndex) {
465: Segment anchorInterval = createInterval(oldAnchorIndex,
466: anchorSelectionIndex);
467: Segment leadInterval = createInterval(oldLeadIndex,
468: leadSelectionIndex);
469: return mergeIntervals(anchorInterval, leadInterval);
470: }
471:
472: private Segment createInterval(int oldLeadAnchorIndex,
473: int newLeadAnchorIndex) {
474: if (oldLeadAnchorIndex == newLeadAnchorIndex) {
475: return null;
476: }
477: if (oldLeadAnchorIndex == NOT_SET) {
478: return new Segment(newLeadAnchorIndex, newLeadAnchorIndex);
479: }
480: if (newLeadAnchorIndex == NOT_SET) {
481: return new Segment(oldLeadAnchorIndex, oldLeadAnchorIndex);
482: }
483: return new Segment(oldLeadAnchorIndex, newLeadAnchorIndex);
484: }
485:
486: private int adjustLeadAnchorIndexForIndicesRemoval(
487: int leadAnchorIndex, Segment removalInterval) {
488: int result = leadAnchorIndex;
489: if (result >= removalInterval.getBeginIndex()) {
490: if (result < removalInterval.getEndIndex()) {
491: result = removalInterval.getBeginIndex() - 1;
492: } else {
493: result -= removalInterval.getLength();
494: }
495: }
496: return result;
497: }
498:
499: private void setSelectionAndUpdateLeadAnchor(int intervalEnd1,
500: int intervalEnd2, Selection oldSelection) {
501: int oldAnchor = anchorSelectionIndex;
502: int oldLead = leadSelectionIndex;
503: if (selectionMode == SINGLE_SELECTION) {
504: anchorSelectionIndex = intervalEnd2;
505: } else {
506: anchorSelectionIndex = intervalEnd1;
507: }
508: leadSelectionIndex = intervalEnd2;
509: selection.add(new Segment(anchorSelectionIndex,
510: leadSelectionIndex));
511: doNotification(selection.getDifferenceBounds(oldSelection),
512: oldAnchor, oldLead);
513: }
514:
515: private boolean isValidInterval(int n1, int n2) {
516: if (n1 == -1 || n2 == -1) {
517: return false;
518: }
519: if (n1 < -1 || n2 < -1) {
520: // According to the API specification
521: throw new IndexOutOfBoundsException();
522: }
523: return true;
524: }
525: }
|