0001: /*
0002: * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * o Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * o Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
0015: * its contributors may be used to endorse or promote products derived
0016: * from this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
0025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: */
0030:
0031: package com.jgoodies.binding.list;
0032:
0033: import java.beans.PropertyChangeEvent;
0034: import java.beans.PropertyChangeListener;
0035: import java.util.Arrays;
0036: import java.util.List;
0037:
0038: import javax.swing.ListModel;
0039: import javax.swing.event.ListDataEvent;
0040: import javax.swing.event.ListDataListener;
0041:
0042: import com.jgoodies.binding.PresentationModel;
0043: import com.jgoodies.binding.beans.BeanAdapter;
0044: import com.jgoodies.binding.value.ValueHolder;
0045: import com.jgoodies.binding.value.ValueModel;
0046:
0047: /**
0048: * Represents a selection in a list of objects. Provides bound bean properties
0049: * for the list, the selection, the selection index, and the selection empty
0050: * state. The SelectionInList implements ValueModel with the selection as value.
0051: * Selection changes fire an event only if the old and new value are not equal.
0052: * If you need to compare the identity you can use and observe the selection
0053: * index instead of the selection or value.<p>
0054: *
0055: * The SelectionInList uses three ValueModels to hold the list, the selection
0056: * and selection index and provides bound bean properties for these models.
0057: * You can access, observe and replace these ValueModels. This is useful
0058: * to connect a SelectionInList with other ValueModels; for example you can
0059: * use the SelectionInList's selection holder as bean channel for a
0060: * PresentationModel. Since the SelectionInList is a ValueModel, it is often
0061: * used as bean channel. See the Binding tutorial classes for examples on how
0062: * to connect a SelectionInList with a PresentationModel.<p>
0063: *
0064: * This class also implements the {@link ListModel} interface that allows
0065: * API users to observe fine grained changes in the structure and contents
0066: * of the list. Hence instances of this class can be used directly as model of
0067: * a JList. If you want to use a SelectionInList with a JComboBox or JTable,
0068: * you can convert the SelectionInList to the associated component model
0069: * interfaces using the adapter classes
0070: * {@link com.jgoodies.binding.adapter.ComboBoxAdapter}
0071: * and {@link com.jgoodies.binding.adapter.AbstractTableAdapter} respectively.
0072: * These classes are part of the Binding library too.<p>
0073: *
0074: * The SelectionInList supports two list types as content of its list holder:
0075: * <code>List</code> and <code>ListModel</code>. The two modes differ in how
0076: * precise this class can fire events about changes to the content and structure
0077: * of the list. If you use a List, this class can only report
0078: * that the list changes completely; this is done by firing
0079: * a PropertyChangeEvent for the <em>list</em> property.
0080: * Also, a <code>ListDataEvent</code> is fired that reports a complete change.
0081: * In contrast, if you use a ListModel it will report the same
0082: * PropertyChangeEvent. But fine grained changes in the list
0083: * model will be fired by this class to notify observes about changes in
0084: * the content, added and removed elements.<p>
0085: *
0086: * If the list content doesn't change at all, or if it always changes
0087: * completely, you can work well with both List content and ListModel content.
0088: * But if the list structure or content changes, the ListModel reports more
0089: * fine grained events to registered ListDataListeners, which in turn allows
0090: * list views to chooser better user interface gestures: for example, a table
0091: * with scroll pane may retain the current selection and scroll offset.<p>
0092: *
0093: * An example for using a ListModel in a SelectionInList is the asynchronous
0094: * transport of list elements from a server to a client. Let's say you transport
0095: * the list elements in portions of 10 elements to improve the application's
0096: * responsiveness. The user can then select and work with the SelectionInList
0097: * as soon as the ListModel gets populated. If at a later time more elements
0098: * are added to the list model, the SelectionInList can retain the selection
0099: * index (and selection) and will just report a ListDataEvent about
0100: * the interval added. JList, JTable and JComboBox will then just add
0101: * the new elements at the end of the list presentation.<p>
0102: *
0103: * If you want to combine List operations and the ListModel change reports,
0104: * you may consider using an implementation that combines these two interfaces,
0105: * for example {@link ArrayListModel} or {@link LinkedListModel}.<p>
0106: *
0107: * <strong>Important Note:</strong> If you change the ListModel instance,
0108: * either by calling <code>#setListModel(ListModel)</code> or by setting
0109: * a new value to the underlying list holder, you must ensure that
0110: * the list holder throws a PropertyChangeEvent whenever the instance changes.
0111: * This event is used to remove a ListDataListener from the old ListModel
0112: * instance and is later used to add it to the new ListModel instance.
0113: * It is easy to violate this constraint, just because Java's standard
0114: * PropertyChangeSupport helper class that is used by many beans, checks
0115: * a changed property value via <code>#equals</code>, not <code>==</code>.
0116: * For example, if you change the SelectionInList's list model from an empty
0117: * list <code>L1</code> to another empty list instance <code>L2</code>,
0118: * the PropertyChangeSupport won't generate a PropertyChangeEvent,
0119: * and so, the SelectionInList won't know about the change, which
0120: * may lead to unexpected behavior.<p>
0121: *
0122: * This binding library provides some help for firing PropertyChangeEvents
0123: * if the old ListModel and new ListModel are equal but not the same.
0124: * Class {@link com.jgoodies.binding.beans.ExtendedPropertyChangeSupport}
0125: * allows to permanently or individually check the identity (using
0126: * <code>==</code>) instead of checking the equity (using <code>#equals</code>).
0127: * Class {@link com.jgoodies.binding.beans.Model} uses this extended
0128: * property change support. And class {@link ValueHolder} uses it too
0129: * and can be configured to always test the identity.<p>
0130: *
0131: * Since version 1.0.2 this class provides public convenience methods
0132: * for firing ListDataEvents, see the methods <code>#fireContentsChanged</code>,
0133: * <code>#fireIntervalAdded</code>, and <code>#fireIntervalRemoved</code>.
0134: * These are automatically invoked if the list holder holds a ListModel
0135: * that fires these events. If on the other hand the underlying List or
0136: * ListModel does not fire a required ListDataEvent, you can use these
0137: * methods to notify presentations about a change. It is recommended
0138: * to avoid sending duplicate ListDataEvents; hence check if the underlying
0139: * ListModel fires the necessary events or not. Typically an underlying
0140: * ListModel will fire the add and remove events; but often it'll lack
0141: * an event if the (selected) contents has changed. A convenient way to
0142: * indicate that change is <code>#fireSelectedContentsChanged</code>. See
0143: * the tutorial's AlbumManagerModel for an example how to use this feature.<p>
0144: *
0145: * The SelectionInList is partially defined for Lists and ListModels
0146: * that contain {@code null}. Setting the selection to {@code null}
0147: * on a SelectionInList that contains {@code null} won't set the selection index
0148: * to the index of the first {@code null} element. For details see the
0149: * {@link #setSelection(Object)} JavaDocs. This is because the current
0150: * implementation interprets a {@code null} selection as <em>unspecified</em>,
0151: * which maps better to a cleared selection than to a concrete selection index.
0152: * Anyway, as long as you work with the selection index and selection index
0153: * holder, such a SelectionInList will work fine. This is the case if you bind
0154: * a SelectionInList to a JList or JTable. Binding such a SelectionInList
0155: * to a JComboBox won't synchronize the selection index if {@code null}
0156: * is selected.<p>
0157: *
0158: * <strong>Constraints:</strong> The list holder holds instances of {@link List}
0159: * or {@link ListModel}, the selection holder values of type {@code E}
0160: * and the selection index holder of type {@code Integer}. The selection
0161: * index holder must hold non-null index values; however, when firing
0162: * an index value change event, both the old and new value may be null.
0163: * If the ListModel changes, the underlying ValueModel must fire
0164: * a PropertyChangeEvent.
0165: *
0166: * @author Karsten Lentzsch
0167: * @version $Revision: 1.36 $
0168: *
0169: * @see ValueModel
0170: * @see List
0171: * @see ListModel
0172: * @see com.jgoodies.binding.adapter.ComboBoxAdapter
0173: * @see com.jgoodies.binding.adapter.AbstractTableAdapter
0174: * @see com.jgoodies.binding.beans.ExtendedPropertyChangeSupport
0175: * @see com.jgoodies.binding.beans.Model
0176: * @see com.jgoodies.binding.value.ValueHolder
0177: *
0178: * @param <E> the type of the list elements and the selection
0179: */
0180: public final class SelectionInList<E> extends IndirectListModel<E>
0181: implements ValueModel {
0182:
0183: // Constant Names for Bound Properties ************************************
0184:
0185: /**
0186: * The name of the bound read-write <em>selection</em> property.
0187: */
0188: public static final String PROPERTYNAME_SELECTION = "selection";
0189:
0190: /**
0191: * The name of the bound read-only <em>selectionEmpty</em> property.
0192: */
0193: public static final String PROPERTYNAME_SELECTION_EMPTY = "selectionEmpty";
0194:
0195: /**
0196: * The name of the bound read-write <em>selection holder</em> property.
0197: */
0198: public static final String PROPERTYNAME_SELECTION_HOLDER = "selectionHolder";
0199:
0200: /**
0201: * The name of the bound read-write <em>selectionIndex</em> property.
0202: */
0203: public static final String PROPERTYNAME_SELECTION_INDEX = "selectionIndex";
0204:
0205: /**
0206: * The name of the bound read-write <em>selection index holder</em> property.
0207: */
0208: public static final String PROPERTYNAME_SELECTION_INDEX_HOLDER = "selectionIndexHolder";
0209:
0210: /**
0211: * The name of the bound read-write <em>value</em> property.
0212: */
0213: public static final String PROPERTYNAME_VALUE = "value";
0214:
0215: // ************************************************************************
0216:
0217: /**
0218: * A special index that indicates that we have no selection.
0219: */
0220: private static final int NO_SELECTION_INDEX = -1;
0221:
0222: // Instance Fields ********************************************************
0223:
0224: /**
0225: * Holds the selection, an instance of <code>Object</code>.
0226: */
0227: private ValueModel selectionHolder;
0228:
0229: /**
0230: * Holds the selection index, an <code>Integer</code>.
0231: */
0232: private ValueModel selectionIndexHolder;
0233:
0234: /**
0235: * The <code>PropertyChangeListener</code> used to handle
0236: * changes of the selection.
0237: */
0238: private final PropertyChangeListener selectionChangeHandler;
0239:
0240: /**
0241: * The <code>PropertyChangeListener</code> used to handle
0242: * changes of the selection index.
0243: */
0244: private final PropertyChangeListener selectionIndexChangeHandler;
0245:
0246: /**
0247: * Duplicates the value of the selectionHolder.
0248: * Used to provide better old values in PropertyChangeEvents
0249: * fired after selectionIndex changes.
0250: */
0251: private E oldSelection;
0252:
0253: /**
0254: * Duplicates the value of the selectionIndexHolder.
0255: * Used to provide better old values in PropertyChangeEvents
0256: * fired after selectionIndex changes and selection changes.
0257: */
0258: private int oldSelectionIndex;
0259:
0260: // Instance creation ****************************************************
0261:
0262: /**
0263: * Constructs a SelectionInList with an empty initial
0264: * <code>ArrayListModel</code> using defaults for the selection holder
0265: * and selection index holder.
0266: */
0267: public SelectionInList() {
0268: this ((ListModel) new ArrayListModel<E>());
0269: }
0270:
0271: /**
0272: * Constructs a SelectionInList on the given item array
0273: * using defaults for the selection holder and selection index holder.
0274: * The specified array will be converted to a List.<p>
0275: *
0276: * Changes to the list "write through" to the array, and changes
0277: * to the array contents will be reflected in the list.
0278: *
0279: * @param listItems the array of initial items
0280: *
0281: * @throws NullPointerException if <code>listItems</code> is <code>null</code>
0282: */
0283: public SelectionInList(E[] listItems) {
0284: this (Arrays.asList(listItems));
0285: }
0286:
0287: /**
0288: * Constructs a SelectionInList on the given item array and
0289: * selection holder using a default selection index holder.
0290: * The specified array will be converted to a List.<p>
0291: *
0292: * Changes to the list "write through" to the array, and changes
0293: * to the array contents will be reflected in the list.
0294: *
0295: * @param listItems the array of initial items
0296: * @param selectionHolder holds the selection
0297: *
0298: * @throws NullPointerException if <code>listItems</code> or
0299: * <code>selectionHolder</code> is <code>null</code>
0300: */
0301: public SelectionInList(E[] listItems, ValueModel selectionHolder) {
0302: this (Arrays.asList(listItems), selectionHolder);
0303: }
0304:
0305: /**
0306: * Constructs a SelectionInList on the given item array and
0307: * selection holder using a default selection index holder.
0308: * The specified array will be converted to a List.<p>
0309: *
0310: * Changes to the list "write through" to the array, and changes
0311: * to the array contents will be reflected in the list.
0312: *
0313: * @param listItems the array of initial items
0314: * @param selectionHolder holds the selection
0315: * @param selectionIndexHolder holds the selection index
0316: *
0317: * @throws NullPointerException if <code>listItems</code>,
0318: * <code>selectionHolder</code>, or <code>selectionIndexHolder</code>
0319: * is <code>null</code>
0320: */
0321: public SelectionInList(E[] listItems, ValueModel selectionHolder,
0322: ValueModel selectionIndexHolder) {
0323: this (Arrays.asList(listItems), selectionHolder,
0324: selectionIndexHolder);
0325: }
0326:
0327: /**
0328: * Constructs a SelectionInList on the given list
0329: * using defaults for the selection holder and selection index holder.<p>
0330: *
0331: * <strong>Note:</strong> Favor <code>ListModel</code> over
0332: * <code>List</code> when working with the SelectionInList.
0333: * Why? The SelectionInList can work with both types. What's the
0334: * difference? ListModel provides all list access features
0335: * required by the SelectionInList's. In addition it reports more
0336: * fine grained change events, instances of <code>ListDataEvents</code>.
0337: * In contrast developer often create Lists and operate on them
0338: * and the ListModel may be inconvenient for these operations.<p>
0339: *
0340: * A convenient solution for this situation is to use the
0341: * <code>ArrayListModel</code> and <code>LinkedListModel</code> classes.
0342: * These implement both List and ListModel, offer the standard List
0343: * operations and report the fine grained ListDataEvents.
0344: *
0345: * @param list the initial list
0346: */
0347: public SelectionInList(List<E> list) {
0348: this (new ValueHolder(list, true));
0349: }
0350:
0351: /**
0352: * Constructs a SelectionInList on the given list and
0353: * selection holder using a default selection index holder.<p>
0354: *
0355: * <strong>Note:</strong> Favor <code>ListModel</code> over
0356: * <code>List</code> when working with the SelectionInList.
0357: * Why? The SelectionInList can work with both types. What's the
0358: * difference? ListModel provides all list access features
0359: * required by the SelectionInList's. In addition it reports more
0360: * fine grained change events, instances of <code>ListDataEvents</code>.
0361: * In contrast developer often create Lists and operate on them
0362: * and the ListModel may be inconvenient for these operations.<p>
0363: *
0364: * A convenient solution for this situation is to use the
0365: * <code>ArrayListModel</code> and <code>LinkedListModel</code> classes.
0366: * These implement both List and ListModel, offer the standard List
0367: * operations and report the fine grained ListDataEvents.
0368: *
0369: * @param list the initial list
0370: * @param selectionHolder holds the selection
0371: *
0372: * @throws NullPointerException
0373: * if <code>selectionHolder</code> is <code>null</code>
0374: */
0375: public SelectionInList(List<E> list, ValueModel selectionHolder) {
0376: this (new ValueHolder(list, true), selectionHolder);
0377: }
0378:
0379: /**
0380: * Constructs a SelectionInList on the given list,
0381: * selection holder, and selection index holder.<p>
0382: *
0383: * <strong>Note:</strong> Favor <code>ListModel</code> over
0384: * <code>List</code> when working with the SelectionInList.
0385: * Why? The SelectionInList can work with both types. What's the
0386: * difference? ListModel provides all list access features
0387: * required by the SelectionInList's. In addition it reports more
0388: * fine grained change events, instances of <code>ListDataEvents</code>.
0389: * In contrast developer often create Lists and operate on them
0390: * and the ListModel may be inconvenient for these operations.<p>
0391: *
0392: * A convenient solution for this situation is to use the
0393: * <code>ArrayListModel</code> and <code>LinkedListModel</code> classes.
0394: * These implement both List and ListModel, offer the standard List
0395: * operations and report the fine grained ListDataEvents.
0396: *
0397: * @param list the initial list
0398: * @param selectionHolder holds the selection
0399: * @param selectionIndexHolder holds the selection index
0400: *
0401: * @throws NullPointerException if <code>selectionHolder</code>,
0402: * or <code>selectionIndexHolder</code> is <code>null</code>
0403: */
0404: public SelectionInList(List<E> list, ValueModel selectionHolder,
0405: ValueModel selectionIndexHolder) {
0406: this (new ValueHolder(list, true), selectionHolder,
0407: selectionIndexHolder);
0408: }
0409:
0410: /**
0411: * Constructs a SelectionInList on the given list model
0412: * using defaults for the selection holder and selection index holder.
0413: *
0414: * @param listModel the initial list model
0415: */
0416: public SelectionInList(ListModel listModel) {
0417: this (new ValueHolder(listModel, true));
0418: }
0419:
0420: /**
0421: * Constructs a SelectionInList on the given list model
0422: * and selection holder using a default selection index holder.
0423: *
0424: * @param listModel the initial list model
0425: * @param selectionHolder holds the selection
0426: *
0427: * @throws NullPointerException
0428: * if <code>selectionHolder</code> is <code>null</code>
0429: */
0430: public SelectionInList(ListModel listModel,
0431: ValueModel selectionHolder) {
0432: this (new ValueHolder(listModel, true), selectionHolder);
0433: }
0434:
0435: /**
0436: * Constructs a SelectionInList on the given list model,
0437: * selection holder, and selection index holder.
0438: *
0439: * @param listModel the initial list model
0440: * @param selectionHolder holds the selection
0441: * @param selectionIndexHolder holds the selection index
0442: *
0443: * @throws NullPointerException if <code>selectionHolder</code>,
0444: * or <code>selectionIndexHolder</code> is <code>null</code>
0445: */
0446: public SelectionInList(ListModel listModel,
0447: ValueModel selectionHolder, ValueModel selectionIndexHolder) {
0448: this (new ValueHolder(listModel, true), selectionHolder,
0449: selectionIndexHolder);
0450: }
0451:
0452: /**
0453: * Constructs a SelectionInList on the given list holder
0454: * using defaults for the selection holder and selection index holder.<p>
0455: *
0456: * <strong>Constraints:</strong>
0457: * 1) The listHolder must hold instances of List or ListModel and
0458: * 2) must report a value change whenever the value's identity changes.
0459: * Note that many bean properties don't fire a PropertyChangeEvent
0460: * if the old and new value are equal - and so would break this constraint.
0461: * If you provide a ValueHolder, enable its identityCheck feature
0462: * during construction. If you provide an adapted bean property from
0463: * a bean that extends the JGoodies <code>Model</code> class,
0464: * you can enable the identity check feature in the methods
0465: * <code>#firePropertyChange</code> by setting the trailing boolean
0466: * parameter to <code>true</code>.
0467: *
0468: * @param listHolder holds the list or list model
0469: *
0470: * @throws NullPointerException
0471: * if <code>listHolder</code> is <code>null</code>
0472: */
0473: public SelectionInList(ValueModel listHolder) {
0474: this (listHolder, new ValueHolder(null, true));
0475: }
0476:
0477: /**
0478: * Constructs a SelectionInList on the given list holder,
0479: * selection holder and selection index holder.<p>
0480: *
0481: * <strong>Constraints:</strong>
0482: * 1) The listHolder must hold instances of List or ListModel and
0483: * 2) must report a value change whenever the value's identity changes.
0484: * Note that many bean properties don't fire a PropertyChangeEvent
0485: * if the old and new value are equal - and so would break this constraint.
0486: * If you provide a ValueHolder, enable its identityCheck feature
0487: * during construction. If you provide an adapted bean property from
0488: * a bean that extends the JGoodies <code>Model</code> class,
0489: * you can enable the identity check feature in the methods
0490: * <code>#firePropertyChange</code> by setting the trailing boolean
0491: * parameter to <code>true</code>.
0492: *
0493: * @param listHolder holds the list or list model
0494: * @param selectionHolder holds the selection
0495: * @throws NullPointerException if <code>listHolder</code>
0496: * or <code>selectionHolder</code> is <code>null</code>
0497: */
0498: public SelectionInList(ValueModel listHolder,
0499: ValueModel selectionHolder) {
0500: this (listHolder, selectionHolder, new ValueHolder(Integer
0501: .valueOf(NO_SELECTION_INDEX)));
0502: }
0503:
0504: /**
0505: * Constructs a SelectionInList on the given list holder,
0506: * selection holder and selection index holder.<p>
0507: *
0508: * <strong>Constraints:</strong>
0509: * 1) The listHolder must hold instances of List or ListModel and
0510: * 2) must report a value change whenever the value's identity changes.
0511: * Note that many bean properties don't fire a PropertyChangeEvent
0512: * if the old and new value are equal - and so would break this constraint.
0513: * If you provide a ValueHolder, enable its identityCheck feature
0514: * during construction. If you provide an adapted bean property from
0515: * a bean that extends the JGoodies <code>Model</code> class,
0516: * you can enable the identity check feature in the methods
0517: * <code>#firePropertyChange</code> by setting the trailing boolean
0518: * parameter to <code>true</code>.
0519: *
0520: * @param listHolder holds the list or list model
0521: * @param selectionHolder holds the selection
0522: * @param selectionIndexHolder holds the selection index
0523: *
0524: * @throws NullPointerException if the <code>listModelHolder</code>,
0525: * <code>selectionHolder</code>, or <code>selectionIndexHolder</code>
0526: * is <code>null</code>
0527: * @throws IllegalArgumentException if the listHolder is a ValueHolder
0528: * that doesn't check the identity when changing its value
0529: * @throws ClassCastException if the listModelHolder contents
0530: * is neither a List nor a ListModel
0531: */
0532: public SelectionInList(ValueModel listHolder,
0533: ValueModel selectionHolder, ValueModel selectionIndexHolder) {
0534: super (listHolder);
0535: if (selectionHolder == null)
0536: throw new NullPointerException(
0537: "The selection holder must not be null.");
0538: if (selectionIndexHolder == null)
0539: throw new NullPointerException(
0540: "The selection index holder must not be null.");
0541:
0542: selectionChangeHandler = new SelectionChangeHandler();
0543: selectionIndexChangeHandler = new SelectionIndexChangeHandler();
0544:
0545: this .selectionHolder = selectionHolder;
0546: this .selectionIndexHolder = selectionIndexHolder;
0547: initializeSelectionIndex();
0548:
0549: this .selectionHolder
0550: .addValueChangeListener(selectionChangeHandler);
0551: this .selectionIndexHolder
0552: .addValueChangeListener(selectionIndexChangeHandler);
0553: }
0554:
0555: // ListModel Helper Code **************************************************
0556:
0557: /**
0558: * Notifies all registered ListDataListeners that the contents
0559: * of the selected list item - if any - has changed.
0560: * Useful to update a presentation after editing the selection.
0561: * See the tutorial's AlbumManagerModel for an example how to use
0562: * this feature.<p>
0563: *
0564: * If the list holder holds a ListModel, this SelectionInList listens
0565: * to ListDataEvents fired by that ListModel, and forwards these events
0566: * by invoking the associated <code>#fireXXX</code> method, which in turn
0567: * notifies all registered ListDataListeners. Therefore if you fire
0568: * ListDataEvents in an underlying ListModel, you don't need this method
0569: * and should not use it to avoid sending duplicate ListDataEvents.
0570: *
0571: * @see ListModel
0572: * @see ListDataListener
0573: * @see ListDataEvent
0574: *
0575: * @since 1.0.2
0576: */
0577: public void fireSelectedContentsChanged() {
0578: if (hasSelection()) {
0579: int selectionIndex = getSelectionIndex();
0580: fireContentsChanged(selectionIndex, selectionIndex);
0581: }
0582: }
0583:
0584: // Accessing the List, Selection and Index ********************************
0585:
0586: /**
0587: * Looks up and returns the current selection using
0588: * the current selection index. Returns <code>null</code> if
0589: * no object is selected or if the list has no elements.
0590: *
0591: * @return the current selection, <code>null</code> if none is selected
0592: */
0593: public E getSelection() {
0594: return getSafeElementAt(getSelectionIndex());
0595: }
0596:
0597: /**
0598: * Sets the selection index to the index of the first list element
0599: * that equals {@code newSelection}. If {@code newSelection}
0600: * is {@code null}, it is interpreted as <em>unspecified</em>
0601: * and the selection index is set to -1, and this SelectionInList
0602: * has no selection. Does nothing if the list is empty or {@code null}.
0603: *
0604: * @param newSelection the object to be set as new selection,
0605: * or {@code null} to set the selection index to -1
0606: */
0607: public void setSelection(E newSelection) {
0608: if (!isEmpty()) {
0609: setSelectionIndex(indexOf(newSelection));
0610: }
0611: }
0612:
0613: /**
0614: * Checks and answers if an element is selected.
0615: *
0616: * @return true if an element is selected, false otherwise
0617: */
0618: public boolean hasSelection() {
0619: return getSelectionIndex() != NO_SELECTION_INDEX;
0620: }
0621:
0622: /**
0623: * Checks and answers whether the selection is empty or not.
0624: * Unlike #hasSelection, the underlying property #selectionEmpty
0625: * for this method is bound. I.e. you can observe this property
0626: * using a PropertyChangeListener to update UI state.
0627: *
0628: * @return true if nothing is selected, false if there's a selection
0629: * @see #clearSelection
0630: * @see #hasSelection
0631: */
0632: public boolean isSelectionEmpty() {
0633: return !hasSelection();
0634: }
0635:
0636: /**
0637: * Clears the selection of this SelectionInList - if any.
0638: */
0639: public void clearSelection() {
0640: setSelectionIndex(NO_SELECTION_INDEX);
0641: }
0642:
0643: /**
0644: * Returns the selection index.
0645: *
0646: * @return the selection index
0647: *
0648: * @throws NullPointerException if the selection index holder
0649: * has a null Object set
0650: */
0651: public int getSelectionIndex() {
0652: return ((Integer) getSelectionIndexHolder().getValue())
0653: .intValue();
0654: }
0655:
0656: /**
0657: * Sets a new selection index. Does nothing if it is the same as before.
0658: *
0659: * @param newSelectionIndex the selection index to be set
0660: * @throws IndexOutOfBoundsException if the new selection index
0661: * is outside the bounds of the list
0662: */
0663: public void setSelectionIndex(int newSelectionIndex) {
0664: int upperBound = getSize() - 1;
0665: if (newSelectionIndex < NO_SELECTION_INDEX
0666: || newSelectionIndex > upperBound)
0667: throw new IndexOutOfBoundsException("The selection index "
0668: + newSelectionIndex + " must be in [-1, "
0669: + upperBound + "]");
0670:
0671: oldSelectionIndex = getSelectionIndex();
0672: if (oldSelectionIndex == newSelectionIndex)
0673: return;
0674:
0675: getSelectionIndexHolder().setValue(
0676: Integer.valueOf(newSelectionIndex));
0677: }
0678:
0679: // Accessing the Holders for: List, Selection and Index *******************
0680:
0681: /**
0682: * Returns the selection holder.
0683: *
0684: * @return the selection holder
0685: */
0686: public ValueModel getSelectionHolder() {
0687: return selectionHolder;
0688: }
0689:
0690: /**
0691: * Sets a new selection holder.
0692: * Does nothing if the new is the same as before.
0693: * The selection remains unchanged and is still driven
0694: * by the selection index holder. It's just that future
0695: * index changes will update the new selection holder
0696: * and that future selection holder changes affect the
0697: * selection index.
0698: *
0699: * @param newSelectionHolder the selection holder to set
0700: *
0701: * @throws NullPointerException if the new selection holder is null
0702: */
0703: public void setSelectionHolder(ValueModel newSelectionHolder) {
0704: if (newSelectionHolder == null)
0705: throw new NullPointerException(
0706: "The new selection holder must not be null.");
0707:
0708: ValueModel oldSelectionHolder = getSelectionHolder();
0709: oldSelectionHolder
0710: .removeValueChangeListener(selectionChangeHandler);
0711: selectionHolder = newSelectionHolder;
0712: oldSelection = (E) newSelectionHolder.getValue();
0713: newSelectionHolder
0714: .addValueChangeListener(selectionChangeHandler);
0715: firePropertyChange(PROPERTYNAME_SELECTION_HOLDER,
0716: oldSelectionHolder, newSelectionHolder);
0717: }
0718:
0719: /**
0720: * Returns the selection index holder.
0721: *
0722: * @return the selection index holder
0723: */
0724: public ValueModel getSelectionIndexHolder() {
0725: return selectionIndexHolder;
0726: }
0727:
0728: /**
0729: * Sets a new selection index holder.
0730: * Does nothing if the new is the same as before.
0731: *
0732: * @param newSelectionIndexHolder the selection index holder to set
0733: *
0734: * @throws NullPointerException if the new selection index holder is null
0735: * @throws IllegalArgumentException if the value of the new selection index
0736: * holder is null
0737: */
0738: public void setSelectionIndexHolder(
0739: ValueModel newSelectionIndexHolder) {
0740: if (newSelectionIndexHolder == null)
0741: throw new NullPointerException(
0742: "The new selection index holder must not be null.");
0743:
0744: if (newSelectionIndexHolder.getValue() == null)
0745: throw new IllegalArgumentException(
0746: "The value of the new selection index holder must not be null.");
0747:
0748: ValueModel oldSelectionIndexHolder = getSelectionIndexHolder();
0749: if (equals(oldSelectionIndexHolder, newSelectionIndexHolder))
0750: return;
0751:
0752: oldSelectionIndexHolder
0753: .removeValueChangeListener(selectionIndexChangeHandler);
0754: selectionIndexHolder = newSelectionIndexHolder;
0755: newSelectionIndexHolder
0756: .addValueChangeListener(selectionIndexChangeHandler);
0757: oldSelectionIndex = getSelectionIndex();
0758: oldSelection = getSafeElementAt(oldSelectionIndex);
0759: firePropertyChange(PROPERTYNAME_SELECTION_INDEX_HOLDER,
0760: oldSelectionIndexHolder, newSelectionIndexHolder);
0761: }
0762:
0763: // ValueModel Implementation ********************************************
0764:
0765: /**
0766: * Returns the current selection, <code>null</code> if the selection index
0767: * does not represent a selection in the list.
0768: *
0769: * @return the selected element - if any
0770: */
0771: public E getValue() {
0772: return getSelection();
0773: }
0774:
0775: /**
0776: * Sets the selection index to the index of the first list element
0777: * that equals {@code newValue}. If {@code newValue}
0778: * is {@code null}, it is interpreted as <em>unspecified</em>
0779: * and the selection index is set to -1, and this SelectionInList
0780: * has no selection. Does nothing if the list is empty or {@code null}.
0781: *
0782: * @param newValue the object to be set as new selection,
0783: * or {@code null} to set the selection index to -1
0784: */
0785: public void setValue(Object newValue) {
0786: setSelection((E) newValue);
0787: }
0788:
0789: /**
0790: * Registers the given PropertyChangeListener with this model.
0791: * The listener will be notified if the value has changed.<p>
0792: *
0793: * The PropertyChangeEvents delivered to the listener have the name
0794: * set to "value". In other words, the listeners won't get notified
0795: * when a PropertyChangeEvent is fired that has a null object as
0796: * the name to indicate an arbitrary set of the event source's
0797: * properties have changed.<p>
0798: *
0799: * In the rare case, where you want to notify a PropertyChangeListener
0800: * even with PropertyChangeEvents that have no property name set,
0801: * you can register the listener with #addPropertyChangeListener,
0802: * not #addValueChangeListener.
0803: *
0804: * @param l the listener to add
0805: *
0806: * @see ValueModel
0807: */
0808: public void addValueChangeListener(PropertyChangeListener l) {
0809: addPropertyChangeListener(PROPERTYNAME_VALUE, l);
0810: }
0811:
0812: /**
0813: * Removes the given PropertyChangeListener from the model.
0814: *
0815: * @param l the listener to remove
0816: */
0817: public void removeValueChangeListener(PropertyChangeListener l) {
0818: removePropertyChangeListener(PROPERTYNAME_VALUE, l);
0819: }
0820:
0821: /**
0822: * Notifies all listeners that have registered interest for
0823: * notification on this event type. The event instance
0824: * is lazily created using the parameters passed into
0825: * the fire method.
0826: *
0827: * @param oldValue the value before the change
0828: * @param newValue the value after the change
0829: *
0830: * @see java.beans.PropertyChangeSupport
0831: */
0832: void fireValueChange(Object oldValue, Object newValue) {
0833: firePropertyChange(PROPERTYNAME_VALUE, oldValue, newValue);
0834: }
0835:
0836: // Misc ******************************************************************
0837:
0838: /**
0839: * Removes the internal listeners from the list holder, selection holder,
0840: * selection index holder. If the current list is a ListModel, the internal
0841: * ListDataListener is removed from the list model. This SelectionInList
0842: * must not be used after calling <code>#release</code>.<p>
0843: *
0844: * To avoid memory leaks it is recommended to invoke this method,
0845: * if the list holder, selection holder, or selection index holder
0846: * live much longer than this SelectionInList.
0847: * Instead of releasing the SelectionInList, you typically make
0848: * the list holder, selection holder, and selection index holder
0849: * obsolete by releasing the PresentationModel or BeanAdapter that has
0850: * created them before.<p>
0851: *
0852: * As an alternative you may use ValueModels that in turn use
0853: * event listener lists implemented using <code>WeakReference</code>.<p>
0854: *
0855: * Basically this release method performs the reverse operation
0856: * performed during the SelectionInList construction.
0857: *
0858: * @see PresentationModel#release()
0859: * @see BeanAdapter#release()
0860: * @see java.lang.ref.WeakReference
0861: *
0862: * @since 1.2
0863: */
0864: @Override
0865: public void release() {
0866: super .release();
0867: selectionHolder
0868: .removeValueChangeListener(selectionChangeHandler);
0869: selectionIndexHolder
0870: .removeValueChangeListener(selectionIndexChangeHandler);
0871: selectionHolder = null;
0872: selectionIndexHolder = null;
0873: oldSelection = null;
0874: }
0875:
0876: // Helper Code ***********************************************************
0877:
0878: private E getSafeElementAt(int index) {
0879: return (index < 0 || index >= getSize()) ? null
0880: : getElementAt(index);
0881: }
0882:
0883: /**
0884: * Returns the index in the list of the first occurrence of the specified
0885: * element, or -1 if the element is {@code null} or the list does not
0886: * contain this element.<p>
0887: *
0888: * {@code null} is mapped to -1, because the current implementation
0889: * interprets a null selection as <em>unspecified</em>.
0890: *
0891: * @param element the element to search for
0892: * @return the index in the list of the first occurrence of the
0893: * given element, or -1 if the element is {@code null} or
0894: * the list does not contain this element.
0895: */
0896: private int indexOf(Object element) {
0897: return indexOf(getListHolder().getValue(), element);
0898: }
0899:
0900: /**
0901: * Returns the index in the list of the first occurrence of the specified
0902: * element, or -1 if the element is {@code null} or the list does not
0903: * contain this element.<p>
0904: *
0905: * {@code null} is mapped to -1, because the current implementation
0906: * interprets a null selection as <em>unspecified</em>.
0907: *
0908: * @param aList the List or ListModel used to look up the element
0909: * @param element the element to search for
0910: * @return the index in the list of the first occurrence of the
0911: * given element, or -1 if the element is {@code null} or
0912: * the list does not contain this element.
0913: */
0914: private int indexOf(Object aList, Object element) {
0915: if (element == null) {
0916: return NO_SELECTION_INDEX;
0917: } else if (getSize(aList) == 0) {
0918: return NO_SELECTION_INDEX;
0919: }
0920: if (aList instanceof List) {
0921: return ((List<?>) aList).indexOf(element);
0922: }
0923:
0924: // Search the first occurrence of element in the list model.
0925: ListModel listModel = (ListModel) aList;
0926: int size = listModel.getSize();
0927: for (int index = 0; index < size; index++) {
0928: if (element.equals(listModel.getElementAt(index)))
0929: return index;
0930: }
0931: return NO_SELECTION_INDEX;
0932: }
0933:
0934: /**
0935: * Sets the index according to the selection, unless the selection
0936: * is {@code null}.
0937: * Also initializes the copied selection and selection index.
0938: * This method is invoked by the constructors to synchronize
0939: * the selection and index. No listeners are installed yet.<p>
0940: *
0941: * An initial selection of {@code null} may indicate that the selection
0942: * is unspecified. This happens for example, if the selection holder
0943: * adapts a bean property via a PresentationModel, but the bean
0944: * is {@code null}. In this case, the current semantics decides to not
0945: * set the selection index - even if null is a list element.<p>
0946: *
0947: * This leads to an inconsistency. If we construct a SelectionInList
0948: * with {1, 2, 3} and initial selection 1, the selection index is set.
0949: * If we construct {null, 2, 3} and initial selection null, the selection
0950: * index is not set.<p>
0951: *
0952: * TODO: Discuss whether we want to set the selection index if the
0953: * initial selection is {@code null}.
0954: */
0955: private void initializeSelectionIndex() {
0956: E selectionValue = (E) selectionHolder.getValue();
0957: if (selectionValue != null) {
0958: setSelectionIndex(indexOf(selectionValue));
0959: }
0960: oldSelection = selectionValue;
0961: oldSelectionIndex = getSelectionIndex();
0962: }
0963:
0964: // Overriding Superclass Behavior *****************************************
0965:
0966: /**
0967: * Creates and returns the ListDataListener used to observe
0968: * changes in the underlying ListModel. It is re-registered
0969: * in <code>#updateListModel</code>.
0970: *
0971: * @return the ListDataListener that handles changes
0972: * in the underlying ListModel
0973: */
0974: @Override
0975: protected ListDataListener createListDataChangeHandler() {
0976: return new ListDataChangeHandler();
0977: }
0978:
0979: /**
0980: * Removes the list data change handler from the old list in case
0981: * it is a <code>ListModel</code> and adds it to new one in case
0982: * it is a <code>ListModel</code>.
0983: * It then fires a property change for the list and a contents change event
0984: * for the list content. Finally it tries to restore the previous selection
0985: * - if any.<p>
0986: *
0987: * Since version 1.1 the selection will be restored after
0988: * the list content change has been indicated. This is because some
0989: * listeners may clear the selection in a side-effect.
0990: * For example a JTable that is bound to this SelectionInList
0991: * via an AbstractTableAdapter and a SingleSelectionAdapter
0992: * will clear the selection if the new list has a size other
0993: * than the old list.
0994: *
0995: * @param oldList the old list content
0996: * @param oldSize the size of the old List content
0997: * @param newList the new list content
0998: *
0999: * @see javax.swing.JTable#tableChanged(javax.swing.event.TableModelEvent)
1000: */
1001: @Override
1002: protected void updateList(Object oldList, int oldSize,
1003: Object newList) {
1004: boolean hadSelection = hasSelection();
1005: Object oldSelectionHolderValue = hadSelection ? getSelectionHolder()
1006: .getValue()
1007: : null;
1008: super .updateList(oldList, oldSize, newList);
1009: if (hadSelection) {
1010: setSelectionIndex(indexOf(newList, oldSelectionHolderValue));
1011: }
1012: }
1013:
1014: // Event Handlers *********************************************************
1015:
1016: /**
1017: * Handles ListDataEvents in the list model.
1018: * In addition to the ListDataChangeHandler in IndirectListModel,
1019: * this class also updates the selection index.
1020: */
1021: private final class ListDataChangeHandler implements
1022: ListDataListener {
1023:
1024: /**
1025: * Sent after the indices in the index0, index1
1026: * interval have been inserted in the data model.
1027: * The new interval includes both index0 and index1.
1028: *
1029: * @param evt a <code>ListDataEvent</code> encapsulating the
1030: * event information
1031: */
1032: public void intervalAdded(ListDataEvent evt) {
1033: int index0 = evt.getIndex0();
1034: int index1 = evt.getIndex1();
1035: int index = getSelectionIndex();
1036: fireIntervalAdded(index0, index1);
1037: // If the added elements are after the index; do nothing.
1038: if (index >= index0) {
1039: setSelectionIndex(index + (index1 - index0 + 1));
1040: }
1041: }
1042:
1043: /**
1044: * Sent after the indices in the index0, index1 interval
1045: * have been removed from the data model. The interval
1046: * includes both index0 and index1.
1047: *
1048: * @param evt a <code>ListDataEvent</code> encapsulating the
1049: * event information
1050: */
1051: public void intervalRemoved(ListDataEvent evt) {
1052: int index0 = evt.getIndex0();
1053: int index1 = evt.getIndex1();
1054: int index = getSelectionIndex();
1055: fireIntervalRemoved(index0, index1);
1056: if (index < index0) {
1057: // The removed elements are after the index; do nothing.
1058: } else if (index <= index1) {
1059: setSelectionIndex(NO_SELECTION_INDEX);
1060: } else {
1061: setSelectionIndex(index - (index1 - index0 + 1));
1062: }
1063: }
1064:
1065: /**
1066: * Sent when the contents of the list has changed in a way
1067: * that's too complex to characterize with the previous
1068: * methods. For example, this is sent when an item has been
1069: * replaced. Index0 and index1 bracket the change.
1070: *
1071: * @param evt a <code>ListDataEvent</code> encapsulating the
1072: * event information
1073: */
1074: public void contentsChanged(ListDataEvent evt) {
1075: fireContentsChanged(evt.getIndex0(), evt.getIndex1());
1076: updateSelectionContentsChanged(evt.getIndex0(), evt
1077: .getIndex1());
1078: }
1079:
1080: private void updateSelectionContentsChanged(int first, int last) {
1081: if (first < 0)
1082: return;
1083: int selectionIndex = getSelectionIndex();
1084: if (first <= selectionIndex && (selectionIndex <= last)) {
1085: // need to synch directly on the holder because the
1086: // usual methods for setting selection/-index check for
1087: // equality
1088: getSelectionHolder().setValue(
1089: getElementAt(selectionIndex));
1090: }
1091: }
1092:
1093: }
1094:
1095: /**
1096: * Listens to changes of the selection.
1097: */
1098: private final class SelectionChangeHandler implements
1099: PropertyChangeListener {
1100:
1101: /**
1102: * The selection has been changed. Updates the selection index holder's
1103: * value and notifies registered listeners about the changes - if any -
1104: * in the selection index, selection empty, selection, and value.<p>
1105: *
1106: * Adjusts the selection holder's value and the old selection index
1107: * before any event is fired. This ensures that the event old and
1108: * new values are consistent with the SelectionInList's state.<p>
1109: *
1110: * The current implementation assumes that the event sources
1111: * provides a non-{@code null} new value. An arbitrary selection holder
1112: * may fire change events where the new and/or old value is
1113: * {@code null} to indicate that it is unknown, unspecified,
1114: * or difficult to compute (now).<p>
1115: *
1116: * TODO: Consider getting the new selection safely from the selection
1117: * holder in case the new value is {@code null}. See the commented
1118: * code section below.
1119: *
1120: * @param evt the property change event to be handled
1121: */
1122: public void propertyChange(PropertyChangeEvent evt) {
1123: E oldValue = (E) evt.getOldValue();
1124: E newSelection = (E) evt.getNewValue();
1125: // if (newSelection == null) {
1126: // newSelection = (E) selectionHolder.getValue();
1127: // }
1128: int newSelectionIndex = indexOf(newSelection);
1129: if (newSelectionIndex != oldSelectionIndex) {
1130: selectionIndexHolder
1131: .removeValueChangeListener(selectionIndexChangeHandler);
1132: selectionIndexHolder.setValue(Integer
1133: .valueOf(newSelectionIndex));
1134: selectionIndexHolder
1135: .addValueChangeListener(selectionIndexChangeHandler);
1136: }
1137: int theOldSelectionIndex = oldSelectionIndex;
1138: oldSelectionIndex = newSelectionIndex;
1139: oldSelection = newSelection;
1140: firePropertyChange(PROPERTYNAME_SELECTION_INDEX,
1141: theOldSelectionIndex, newSelectionIndex);
1142: firePropertyChange(PROPERTYNAME_SELECTION_EMPTY,
1143: theOldSelectionIndex == NO_SELECTION_INDEX,
1144: newSelectionIndex == NO_SELECTION_INDEX);
1145: /*
1146: * Implementation Note: The following two lines fire the
1147: * PropertyChangeEvents for the 'selection' and 'value' properties.
1148: * If the old and new value are equal, no event is fired.
1149: *
1150: * TODO: Consider using ==, not equals to check for changes.
1151: * That would enable API users to use the selection holder with
1152: * beans that must be checked with ==, not equals.
1153: * However, the SelectionInList's List would still use equals
1154: * to find the index of an element.
1155: */
1156: firePropertyChange(PROPERTYNAME_SELECTION, oldValue,
1157: newSelection);
1158: fireValueChange(oldValue, newSelection);
1159: }
1160: }
1161:
1162: /**
1163: * Listens to changes of the selection index.
1164: */
1165: private final class SelectionIndexChangeHandler implements
1166: PropertyChangeListener {
1167:
1168: /**
1169: * The selection index has been changed. Updates the selection holder
1170: * value and notifies registered listeners about changes - if any -
1171: * in the selection index, selection empty, selection, and value.<p>
1172: *
1173: * Handles null old values in the index PropertyChangeEvent.
1174: * Ignores null new values in this events, because the selection
1175: * index value must always be a non-null value.<p>
1176: *
1177: * Adjusts the selection holder's value and the old selection index
1178: * before any event is fired. This ensures that the event old and
1179: * new values are consistent with the SelectionInList's state.
1180: *
1181: * @param evt the property change event to be handled
1182: */
1183: public void propertyChange(PropertyChangeEvent evt) {
1184: int newSelectionIndex = getSelectionIndex();
1185: E theOldSelection = oldSelection;
1186: //E oldSelection = getSafeElementAt(oldSelectionIndex);
1187: E newSelection = getSafeElementAt(newSelectionIndex);
1188: /*
1189: * Implementation Note: The following conditional suppresses
1190: * value change events if the old and new selection are equal.
1191: *
1192: * TODO: Consider using ==, not equals to check for changes.
1193: * That would enable API users to use the selection holder with
1194: * beans that must be checked with ==, not equals.
1195: * However, the SelectionInList's List would still use equals
1196: * to find the index of an element.
1197: */
1198: if (!SelectionInList.this .equals(theOldSelection,
1199: newSelection)) {
1200: selectionHolder
1201: .removeValueChangeListener(selectionChangeHandler);
1202: selectionHolder.setValue(newSelection);
1203: selectionHolder
1204: .addValueChangeListener(selectionChangeHandler);
1205: }
1206: int theOldSelectionIndex = oldSelectionIndex;
1207: oldSelectionIndex = newSelectionIndex;
1208: oldSelection = newSelection;
1209: firePropertyChange(PROPERTYNAME_SELECTION_INDEX,
1210: theOldSelectionIndex, newSelectionIndex);
1211: firePropertyChange(PROPERTYNAME_SELECTION_EMPTY,
1212: theOldSelectionIndex == NO_SELECTION_INDEX,
1213: newSelectionIndex == NO_SELECTION_INDEX);
1214: firePropertyChange(PROPERTYNAME_SELECTION, theOldSelection,
1215: newSelection);
1216: fireValueChange(theOldSelection, newSelection);
1217: }
1218: }
1219:
1220: }
|