    rev 48858 : Work in progress for JDK-6303622
    Use generic in Swing components like JComboBox
    1. Added generic return type and parameter type for JComboBox.
    2. Modified JList related classes as well.
    3. Reverted the changes made to "getSelectedValues" as suggested by Joe.
    4. Modified files that were failing on Linux and Mac as well.

       1 /*
       2  * Copyright (c) 1997, 2017, 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;
      26
      27 import java.awt.*;
      28 import java.awt.event.*;
      29
      30 import java.util.Vector;
      31 import java.util.Locale;
      32 import java.util.ArrayList;
      33 import java.util.Collections;
      34 import java.util.List;
      35
      36 import java.beans.JavaBean;
      37 import java.beans.BeanProperty;
      38 import java.beans.PropertyChangeEvent;
      39 import java.beans.PropertyChangeListener;
      40 import java.beans.Transient;
      41
      42 import javax.swing.event.*;
      43 import javax.accessibility.*;
      44 import javax.swing.plaf.*;
      45 import javax.swing.text.Position;
      46
      47 import java.io.ObjectOutputStream;
      48 import java.io.IOException;
      49 import java.io.Serializable;
      50
      51 import sun.awt.AWTAccessor;
      52 import sun.awt.AWTAccessor.MouseEventAccessor;
      53 import sun.swing.SwingUtilities2;
      54 import sun.swing.SwingUtilities2.Section;
      55 import static sun.swing.SwingUtilities2.Section.*;
      56
      57 /**
      58  * A component that displays a list of objects and allows the user to select
      59  * one or more items. A separate model, {@code ListModel}, maintains the
      60  * contents of the list.
      61  * <p>
      62  * It's easy to display an array or Vector of objects, using the {@code JList}
      63  * constructor that automatically builds a read-only {@code ListModel} instance
      64  * for you:
      65  * <pre>
      66  * {@code
      67  * // Create a JList that displays strings from an array
      68  *
      69  * String[] data = {"one", "two", "three", "four"};
      70  * JList<String> myList = new JList<String>(data);
      71  *
      72  * // Create a JList that displays the superclasses of JList.class, by
      73  * // creating it with a Vector populated with this data
      74  *
      75  * Vector<Class<?>> superClasses = new Vector<Class<?>>();
      76  * Class<JList> rootClass = javax.swing.JList.class;
      77  * for(Class<?> cls = rootClass; cls != null; cls = cls.getSuperclass()) {
      78  *     superClasses.addElement(cls);
      79  * }
      80  * JList<Class<?>> myList = new JList<Class<?>>(superClasses);
      81  *
      82  * // The automatically created model is stored in JList's "model"
      83  * // property, which you can retrieve
      84  *
      85  * ListModel<Class<?>> model = myList.getModel();
      86  * for(int i = 0; i < model.getSize(); i++) {
      87  *     System.out.println(model.getElementAt(i));
      88  * }
      89  * }
      90  * </pre>
      91  * <p>
      92  * A {@code ListModel} can be supplied directly to a {@code JList} by way of a
      93  * constructor or the {@code setModel} method. The contents need not be static -
      94  * the number of items, and the values of items can change over time. A correct
      95  * {@code ListModel} implementation notifies the set of
      96  * {@code javax.swing.event.ListDataListener}s that have been added to it, each
      97  * time a change occurs. These changes are characterized by a
      98  * {@code javax.swing.event.ListDataEvent}, which identifies the range of list
      99  * indices that have been modified, added, or removed. {@code JList}'s
     100  * {@code ListUI} is responsible for keeping the visual representation up to
     101  * date with changes, by listening to the model.
     102  * <p>
     103  * Simple, dynamic-content, {@code JList} applications can use the
     104  * {@code DefaultListModel} class to maintain list elements. This class
     105  * implements the {@code ListModel} interface and also provides a
     106  * <code>java.util.Vector</code>-like API. Applications that need a more
     107  * custom <code>ListModel</code> implementation may instead wish to subclass
     108  * {@code AbstractListModel}, which provides basic support for managing and
     109  * notifying listeners. For example, a read-only implementation of
     110  * {@code AbstractListModel}:
     111  * <pre>
     112  * {@code
     113  * // This list model has about 2^16 elements.  Enjoy scrolling.
     114  *
     115  * ListModel<String> bigData = new AbstractListModel<String>() {
     116  *     public int getSize() { return Short.MAX_VALUE; }
     117  *     public String getElementAt(int index) { return "Index " + index; }
     118  * };
     119  * }
     120  * </pre>
     121  * <p>
     122  * The selection state of a {@code JList} is managed by another separate
     123  * model, an instance of {@code ListSelectionModel}. {@code JList} is
     124  * initialized with a selection model on construction, and also contains
     125  * methods to query or set this selection model. Additionally, {@code JList}
     126  * provides convenient methods for easily managing the selection. These methods,
     127  * such as {@code setSelectedIndex} and {@code getSelectedValue}, are cover
     128  * methods that take care of the details of interacting with the selection
     129  * model. By default, {@code JList}'s selection model is configured to allow any
     130  * combination of items to be selected at a time; selection mode
     131  * {@code MULTIPLE_INTERVAL_SELECTION}. The selection mode can be changed
     132  * on the selection model directly, or via {@code JList}'s cover method.
     133  * Responsibility for updating the selection model in response to user gestures
     134  * lies with the list's {@code ListUI}.
     135  * <p>
     136  * A correct {@code ListSelectionModel} implementation notifies the set of
     137  * {@code javax.swing.event.ListSelectionListener}s that have been added to it
     138  * each time a change to the selection occurs. These changes are characterized
     139  * by a {@code javax.swing.event.ListSelectionEvent}, which identifies the range
     140  * of the selection change.
     141  * <p>
     142  * The preferred way to listen for changes in list selection is to add
     143  * {@code ListSelectionListener}s directly to the {@code JList}. {@code JList}
     144  * then takes care of listening to the selection model and notifying your
     145  * listeners of change.
     146  * <p>
     147  * Responsibility for listening to selection changes in order to keep the list's
     148  * visual representation up to date lies with the list's {@code ListUI}.
     149  * <p>
     150  * <a id="renderer"></a>
     151  * Painting of cells in a {@code JList} is handled by a delegate called a
     152  * cell renderer, installed on the list as the {@code cellRenderer} property.
     153  * The renderer provides a {@code java.awt.Component} that is used
     154  * like a "rubber stamp" to paint the cells. Each time a cell needs to be
     155  * painted, the list's {@code ListUI} asks the cell renderer for the component,
     156  * moves it into place, and has it paint the contents of the cell by way of its
     157  * {@code paint} method. A default cell renderer, which uses a {@code JLabel}
     158  * component to render, is installed by the lists's {@code ListUI}. You can
     159  * substitute your own renderer using code like this:
     160  * <pre>
     161  * {@code
     162  *  // Display an icon and a string for each object in the list.
     163  *
     164  * class MyCellRenderer extends JLabel implements ListCellRenderer<Object> {
     165  *     static final ImageIcon longIcon = new ImageIcon("long.gif");
     166  *     static final ImageIcon shortIcon = new ImageIcon("short.gif");
     167  *
     168  *     // This is the only method defined by ListCellRenderer.
     169  *     // We just reconfigure the JLabel each time we're called.
     170  *
     171  *     public Component getListCellRendererComponent(
     172  *       JList<?> list,           // the list
     173  *       Object value,            // value to display
     174  *       int index,               // cell index
     175  *       boolean isSelected,      // is the cell selected
     176  *       boolean cellHasFocus)    // does the cell have focus
     177  *     {
     178  *         String s = value.toString();
     179  *         setText(s);
     180  *         setIcon((s.length() > 10) ? longIcon : shortIcon);
     181  *         if (isSelected) {
     182  *             setBackground(list.getSelectionBackground());
     183  *             setForeground(list.getSelectionForeground());
     184  *         } else {
     185  *             setBackground(list.getBackground());
     186  *             setForeground(list.getForeground());
     187  *         }
     188  *         setEnabled(list.isEnabled());
     189  *         setFont(list.getFont());
     190  *         setOpaque(true);
     191  *         return this;
     192  *     }
     193  * }
     194  *
     195  * myList.setCellRenderer(new MyCellRenderer());
     196  * }
     197  * </pre>
     198  * <p>
     199  * Another job for the cell renderer is in helping to determine sizing
     200  * information for the list. By default, the list's {@code ListUI} determines
     201  * the size of cells by asking the cell renderer for its preferred
     202  * size for each list item. This can be expensive for large lists of items.
     203  * To avoid these calculations, you can set a {@code fixedCellWidth} and
     204  * {@code fixedCellHeight} on the list, or have these values calculated
     205  * automatically based on a single prototype value:
     206  * <a id="prototype_example"></a>
     207  * <pre>
     208  * {@code
     209  * JList<String> bigDataList = new JList<String>(bigData);
     210  *
     211  * // We don't want the JList implementation to compute the width
     212  * // or height of all of the list cells, so we give it a string
     213  * // that's as big as we'll need for any cell.  It uses this to
     214  * // compute values for the fixedCellWidth and fixedCellHeight
     215  * // properties.
     216  *
     217  * bigDataList.setPrototypeCellValue("Index 1234567890");
     218  * }
     219  * </pre>
     220  * <p>
     221  * {@code JList} doesn't implement scrolling directly. To create a list that
     222  * scrolls, make it the viewport view of a {@code JScrollPane}. For example:
     223  * <pre>
     224  * JScrollPane scrollPane = new JScrollPane(myList);
     225  *
     226  * // Or in two steps:
     227  * JScrollPane scrollPane = new JScrollPane();
     228  * scrollPane.getViewport().setView(myList);
     229  * </pre>
     230  * <p>
     231  * {@code JList} doesn't provide any special handling of double or triple
     232  * (or N) mouse clicks, but it's easy to add a {@code MouseListener} if you
     233  * wish to take action on these events. Use the {@code locationToIndex}
     234  * method to determine what cell was clicked. For example:
     235  * <pre>
     236  * MouseListener mouseListener = new MouseAdapter() {
     237  *     public void mouseClicked(MouseEvent e) {
     238  *         if (e.getClickCount() == 2) {
     239  *             int index = list.locationToIndex(e.getPoint());
     240  *             System.out.println("Double clicked on Item " + index);
     241  *          }
     242  *     }
     243  * };
     244  * list.addMouseListener(mouseListener);
     245  * </pre>
     246  * <p>
     247  * <strong>Warning:</strong> Swing is not thread safe. For more
     248  * information see <a
     249  * href="package-summary.html#threading">Swing's Threading
     250  * Policy</a>.
     251  * <p>
     252  * <strong>Warning:</strong>
     253  * Serialized objects of this class will not be compatible with
     254  * future Swing releases. The current serialization support is
     255  * appropriate for short term storage or RMI between applications running
     256  * the same version of Swing.  As of 1.4, support for long term storage
     257  * of all JavaBeans&trade;
     258  * has been added to the <code>java.beans</code> package.
     259  * Please see {@link java.beans.XMLEncoder}.
     260  * <p>
     261  * See <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/list.html">How to Use Lists</a>
     262  * in <a href="http://docs.oracle.com/javase/tutorial/"><em>The Java Tutorial</em></a>
     263  * for further documentation.
     264  *
     265  * @see ListModel
     266  * @see AbstractListModel
     267  * @see DefaultListModel
     268  * @see ListSelectionModel
     269  * @see DefaultListSelectionModel
     270  * @see ListCellRenderer
     271  * @see DefaultListCellRenderer
     272  *
     273  * @param <E> the type of the elements of this list
     274  *
     275  * @author Hans Muller
     276  * @since 1.2
     277  */
     278 @JavaBean(defaultProperty = "UI", description = "A component which allows for the selection of one or more objects from a list.")
     279 @SwingContainer(false)
     280 @SuppressWarnings("serial") // Same-version serialization only
     281 public class JList<E> extends JComponent implements Scrollable, Accessible
     282 {
     283     /**
     284      * @see #getUIClassID
     285      * @see #readObject
     286      */
     287     private static final String uiClassID = "ListUI";
     288
     289     /**
     290      * Indicates a vertical layout of cells, in a single column;
     291      * the default layout.
     292      * @see #setLayoutOrientation
     293      * @since 1.4
     294      */
     295     public static final int VERTICAL = 0;
     296
     297     /**
     298      * Indicates a "newspaper style" layout with cells flowing vertically
     299      * then horizontally.
     300      * @see #setLayoutOrientation
     301      * @since 1.4
     302      */
     303     public static final int VERTICAL_WRAP = 1;
     304
     305     /**
     306      * Indicates a "newspaper style" layout with cells flowing horizontally
     307      * then vertically.
     308      * @see #setLayoutOrientation
     309      * @since 1.4
     310      */
     311     public static final int HORIZONTAL_WRAP = 2;
     312
     313     private int fixedCellWidth = -1;
     314     private int fixedCellHeight = -1;
     315     private int horizontalScrollIncrement = -1;
     316     private E prototypeCellValue;
     317     private int visibleRowCount = 8;
     318     private Color selectionForeground;
     319     private Color selectionBackground;
     320     private boolean dragEnabled;
     321
     322     private ListSelectionModel selectionModel;
     323     private ListModel<E> dataModel;
     324     private ListCellRenderer<? super E> cellRenderer;
     325     private ListSelectionListener selectionListener;
     326
     327     /**
     328      * How to lay out the cells; defaults to <code>VERTICAL</code>.
     329      */
     330     private int layoutOrientation;
     331
     332     /**
     333      * The drop mode for this component.
     334      */
     335     private DropMode dropMode = DropMode.USE_SELECTION;
     336
     337     /**
     338      * The drop location.
     339      */
     340     private transient DropLocation dropLocation;
     341
     342     /**
     343      * Flag to indicate UI update is in progress
     344      */
     345     private transient boolean updateInProgress;
     346
     347     /**
     348      * A subclass of <code>TransferHandler.DropLocation</code> representing
     349      * a drop location for a <code>JList</code>.
     350      *
     351      * @see #getDropLocation
     352      * @since 1.6
     353      */
     354     public static final class DropLocation extends TransferHandler.DropLocation {
     355         private final int index;
     356         private final boolean isInsert;
     357
     358         private DropLocation(Point p, int index, boolean isInsert) {
     359             super(p);
     360             this.index = index;
     361             this.isInsert = isInsert;
     362         }
     363
     364         /**
     365          * Returns the index where dropped data should be placed in the
     366          * list. Interpretation of the value depends on the drop mode set on
     367          * the associated component. If the drop mode is either
     368          * <code>DropMode.USE_SELECTION</code> or <code>DropMode.ON</code>,
     369          * the return value is an index of a row in the list. If the drop mode is
     370          * <code>DropMode.INSERT</code>, the return value refers to the index
     371          * where the data should be inserted. If the drop mode is
     372          * <code>DropMode.ON_OR_INSERT</code>, the value of
     373          * <code>isInsert()</code> indicates whether the index is an index
     374          * of a row, or an insert index.
     375          * <p>
     376          * <code>-1</code> indicates that the drop occurred over empty space,
     377          * and no index could be calculated.
     378          *
     379          * @return the drop index
     380          */
     381         public int getIndex() {
     382             return index;
     383         }
     384
     385         /**
     386          * Returns whether or not this location represents an insert
     387          * location.
     388          *
     389          * @return whether or not this is an insert location
     390          */
     391         public boolean isInsert() {
     392             return isInsert;
     393         }
     394
     395         /**
     396          * Returns a string representation of this drop location.
     397          * This method is intended to be used for debugging purposes,
     398          * and the content and format of the returned string may vary
     399          * between implementations.
     400          *
     401          * @return a string representation of this drop location
     402          */
     403         public String toString() {
     404             return getClass().getName()
     405                    + "[dropPoint=" + getDropPoint() + ","
     406                    + "index=" + index + ","
     407                    + "insert=" + isInsert + "]";
     408         }
     409     }
     410
     411     /**
     412      * Constructs a {@code JList} that displays elements from the specified,
     413      * {@code non-null}, model. All {@code JList} constructors delegate to
     414      * this one.
     415      * <p>
     416      * This constructor registers the list with the {@code ToolTipManager},
     417      * allowing for tooltips to be provided by the cell renderers.
     418      *
     419      * @param dataModel the model for the list
     420      * @exception IllegalArgumentException if the model is {@code null}
     421      */
     422     public JList(ListModel<E> dataModel)
     423     {
     424         if (dataModel == null) {
     425             throw new IllegalArgumentException("dataModel must be non null");
     426         }
     427
     428         // Register with the ToolTipManager so that tooltips from the
     429         // renderer show through.
     430         ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
     431         toolTipManager.registerComponent(this);
     432
     433         layoutOrientation = VERTICAL;
     434
     435         this.dataModel = dataModel;
     436         selectionModel = createSelectionModel();
     437         setAutoscrolls(true);
     438         setOpaque(true);
     439         updateUI();
     440     }
     441
     442
     443     /**
     444      * Constructs a <code>JList</code> that displays the elements in
     445      * the specified array. This constructor creates a read-only model
     446      * for the given array, and then delegates to the constructor that
     447      * takes a {@code ListModel}.
     448      * <p>
     449      * Attempts to pass a {@code null} value to this method results in
     450      * undefined behavior and, most likely, exceptions. The created model
     451      * references the given array directly. Attempts to modify the array
     452      * after constructing the list results in undefined behavior.
     453      *
     454      * @param  listData  the array of Objects to be loaded into the data model,
     455      *                   {@code non-null}
     456      */
     457     public JList(final E[] listData)
     458     {
     459         this (
     460             new AbstractListModel<E>() {
     461                 public int getSize() { return listData.length; }
     462                 public E getElementAt(int i) { return listData[i]; }
     463             }
     464         );
     465     }
     466
     467
     468     /**
     469      * Constructs a <code>JList</code> that displays the elements in
     470      * the specified <code>Vector</code>. This constructor creates a read-only
     471      * model for the given {@code Vector}, and then delegates to the constructor
     472      * that takes a {@code ListModel}.
     473      * <p>
     474      * Attempts to pass a {@code null} value to this method results in
     475      * undefined behavior and, most likely, exceptions. The created model
     476      * references the given {@code Vector} directly. Attempts to modify the
     477      * {@code Vector} after constructing the list results in undefined behavior.
     478      *
     479      * @param  listData  the <code>Vector</code> to be loaded into the
     480      *                   data model, {@code non-null}
     481      */
     482     public JList(final Vector<? extends E> listData) {
     483         this (
     484             new AbstractListModel<E>() {
     485                 public int getSize() { return listData.size(); }
     486                 public E getElementAt(int i) { return listData.elementAt(i); }
     487             }
     488         );
     489     }
     490
     491
     492     /**
     493      * Constructs a <code>JList</code> with an empty, read-only, model.
     494      */
     495     public JList() {
     496         this (
     497             new AbstractListModel<E>() {
     498               public int getSize() { return 0; }
     499               public E getElementAt(int i) { throw new IndexOutOfBoundsException("No Data Model"); }
     500             }
     501         );
     502     }
     503
     504
     505     /**
     506      * Returns the {@code ListUI}, the look and feel object that
     507      * renders this component.
     508      *
     509      * @return the <code>ListUI</code> object that renders this component
     510      */
     511     public ListUI getUI() {
     512         return (ListUI)ui;
     513     }
     514
     515
     516     /**
     517      * Sets the {@code ListUI}, the look and feel object that
     518      * renders this component.
     519      *
     520      * @param ui  the <code>ListUI</code> object
     521      * @see UIDefaults#getUI
     522      */
     523     @BeanProperty(hidden = true, visualUpdate = true, description
     524             = "The UI object that implements the Component's LookAndFeel.")
     525     public void setUI(ListUI ui) {
     526         super.setUI(ui);
     527     }
     528
     529
     530     /**
     531      * Resets the {@code ListUI} property by setting it to the value provided
     532      * by the current look and feel. If the current cell renderer was installed
     533      * by the developer (rather than the look and feel itself), this also causes
     534      * the cell renderer and its children to be updated, by calling
     535      * {@code SwingUtilities.updateComponentTreeUI} on it.
     536      *
     537      * @see UIManager#getUI
     538      * @see SwingUtilities#updateComponentTreeUI
     539      */
     540     public void updateUI() {
     541         if (!updateInProgress) {
     542             updateInProgress = true;
     543             try {
     544                 setUI((ListUI)UIManager.getUI(this));
     545
     546                 ListCellRenderer<? super E> renderer = getCellRenderer();
     547                 if (renderer instanceof Component) {
     548                     SwingUtilities.updateComponentTreeUI((Component)renderer);
     549                 }
     550             } finally {
     551                 updateInProgress = false;
     552             }
     553         }
     554     }
     555
     556
     557     /**
     558      * Returns {@code "ListUI"}, the <code>UIDefaults</code> key used to look
     559      * up the name of the {@code javax.swing.plaf.ListUI} class that defines
     560      * the look and feel for this component.
     561      *
     562      * @return the string "ListUI"
     563      * @see JComponent#getUIClassID
     564      * @see UIDefaults#getUI
     565      */
     566     @BeanProperty(bound = false)
     567     public String getUIClassID() {
     568         return uiClassID;
     569     }
     570
     571
     572     /* -----private-----
     573      * This method is called by setPrototypeCellValue and setCellRenderer
     574      * to update the fixedCellWidth and fixedCellHeight properties from the
     575      * current value of prototypeCellValue (if it's non null).
     576      * <p>
     577      * This method sets fixedCellWidth and fixedCellHeight but does <b>not</b>
     578      * generate PropertyChangeEvents for them.
     579      *
     580      * @see #setPrototypeCellValue
     581      * @see #setCellRenderer
     582      */
     583     private void updateFixedCellSize()
     584     {
     585         ListCellRenderer<? super E> cr = getCellRenderer();
     586         E value = getPrototypeCellValue();
     587
     588         if ((cr != null) && (value != null)) {
     589             Component c = cr.getListCellRendererComponent(this, value, 0, false, false);
     590
     591             /* The ListUI implementation will add Component c to its private
     592              * CellRendererPane however we can't assume that's already
     593              * been done here.  So we temporarily set the one "inherited"
     594              * property that may affect the renderer components preferred size:
     595              * its font.
     596              */
     597             Font f = c.getFont();
     598             c.setFont(getFont());
     599
     600             Dimension d = c.getPreferredSize();
     601             fixedCellWidth = d.width;
     602             fixedCellHeight = d.height;
     603
     604             c.setFont(f);
     605         }
     606     }
     607
     608
     609     /**
     610      * Returns the "prototypical" cell value -- a value used to calculate a
     611      * fixed width and height for cells. This can be {@code null} if there
     612      * is no such value.
     613      *
     614      * @return the value of the {@code prototypeCellValue} property
     615      * @see #setPrototypeCellValue
     616      */
     617     public E getPrototypeCellValue() {
     618         return prototypeCellValue;
     619     }
     620
     621     /**
     622      * Sets the {@code prototypeCellValue} property, and then (if the new value
     623      * is {@code non-null}), computes the {@code fixedCellWidth} and
     624      * {@code fixedCellHeight} properties by requesting the cell renderer
     625      * component for the given value (and index 0) from the cell renderer, and
     626      * using that component's preferred size.
     627      * <p>
     628      * This method is useful when the list is too long to allow the
     629      * {@code ListUI} to compute the width/height of each cell, and there is a
     630      * single cell value that is known to occupy as much space as any of the
     631      * others, a so-called prototype.
     632      * <p>
     633      * While all three of the {@code prototypeCellValue},
     634      * {@code fixedCellHeight}, and {@code fixedCellWidth} properties may be
     635      * modified by this method, {@code PropertyChangeEvent} notifications are
     636      * only sent when the {@code prototypeCellValue} property changes.
     637      * <p>
     638      * To see an example which sets this property, see the
     639      * <a href="#prototype_example">class description</a> above.
     640      * <p>
     641      * The default value of this property is <code>null</code>.
     642      * <p>
     643      * This is a JavaBeans bound property.
     644      *
     645      * @param prototypeCellValue  the value on which to base
     646      *                          <code>fixedCellWidth</code> and
     647      *                          <code>fixedCellHeight</code>
     648      * @see #getPrototypeCellValue
     649      * @see #setFixedCellWidth
     650      * @see #setFixedCellHeight
     651      * @see JComponent#addPropertyChangeListener
     652      */
     653     @BeanProperty(visualUpdate = true, description
     654             = "The cell prototype value, used to compute cell width and height.")
     655     public void setPrototypeCellValue(E prototypeCellValue) {
     656         E oldValue = this.prototypeCellValue;
     657         this.prototypeCellValue = prototypeCellValue;
     658
     659         /* If the prototypeCellValue has changed and is non-null,
     660          * then recompute fixedCellWidth and fixedCellHeight.
     661          */
     662
     663         if ((prototypeCellValue != null) && !prototypeCellValue.equals(oldValue)) {
     664             updateFixedCellSize();
     665         }
     666
     667         firePropertyChange("prototypeCellValue", oldValue, prototypeCellValue);
     668     }
     669
     670
     671     /**
     672      * Returns the value of the {@code fixedCellWidth} property.
     673      *
     674      * @return the fixed cell width
     675      * @see #setFixedCellWidth
     676      */
     677     public int getFixedCellWidth() {
     678         return fixedCellWidth;
     679     }
     680
     681     /**
     682      * Sets a fixed value to be used for the width of every cell in the list.
     683      * If {@code width} is -1, cell widths are computed in the {@code ListUI}
     684      * by applying <code>getPreferredSize</code> to the cell renderer component
     685      * for each list element.
     686      * <p>
     687      * The default value of this property is {@code -1}.
     688      * <p>
     689      * This is a JavaBeans bound property.
     690      *
     691      * @param width the width to be used for all cells in the list
     692      * @see #setPrototypeCellValue
     693      * @see #setFixedCellWidth
     694      * @see JComponent#addPropertyChangeListener
     695      */
     696     @BeanProperty(visualUpdate = true, description
     697             = "Defines a fixed cell width when greater than zero.")
     698     public void setFixedCellWidth(int width) {
     699         int oldValue = fixedCellWidth;
     700         fixedCellWidth = width;
     701         firePropertyChange("fixedCellWidth", oldValue, fixedCellWidth);
     702     }
     703
     704
     705     /**
     706      * Returns the value of the {@code fixedCellHeight} property.
     707      *
     708      * @return the fixed cell height
     709      * @see #setFixedCellHeight
     710      */
     711     public int getFixedCellHeight() {
     712         return fixedCellHeight;
     713     }
     714
     715     /**
     716      * Sets a fixed value to be used for the height of every cell in the list.
     717      * If {@code height} is -1, cell heights are computed in the {@code ListUI}
     718      * by applying <code>getPreferredSize</code> to the cell renderer component
     719      * for each list element.
     720      * <p>
     721      * The default value of this property is {@code -1}.
     722      * <p>
     723      * This is a JavaBeans bound property.
     724      *
     725      * @param height the height to be used for all cells in the list
     726      * @see #setPrototypeCellValue
     727      * @see #setFixedCellWidth
     728      * @see JComponent#addPropertyChangeListener
     729      */
     730     @BeanProperty(visualUpdate = true, description
     731             = "Defines a fixed cell height when greater than zero.")
     732     public void setFixedCellHeight(int height) {
     733         int oldValue = fixedCellHeight;
     734         fixedCellHeight = height;
     735         firePropertyChange("fixedCellHeight", oldValue, fixedCellHeight);
     736     }
     737
     738
     739     /**
     740      * Returns the object responsible for painting list items.
     741      *
     742      * @return the value of the {@code cellRenderer} property
     743      * @see #setCellRenderer
     744      */
     745     @Transient
     746     public ListCellRenderer<? super E> getCellRenderer() {
     747         return cellRenderer;
     748     }
     749
     750     /**
     751      * Sets the delegate that is used to paint each cell in the list.
     752      * The job of a cell renderer is discussed in detail in the
     753      * <a href="#renderer">class level documentation</a>.
     754      * <p>
     755      * If the {@code prototypeCellValue} property is {@code non-null},
     756      * setting the cell renderer also causes the {@code fixedCellWidth} and
     757      * {@code fixedCellHeight} properties to be re-calculated. Only one
     758      * <code>PropertyChangeEvent</code> is generated however -
     759      * for the <code>cellRenderer</code> property.
     760      * <p>
     761      * The default value of this property is provided by the {@code ListUI}
     762      * delegate, i.e. by the look and feel implementation.
     763      * <p>
     764      * This is a JavaBeans bound property.
     765      *
     766      * @param cellRenderer the <code>ListCellRenderer</code>
     767      *                          that paints list cells
     768      * @see #getCellRenderer
     769      */
     770     @BeanProperty(visualUpdate = true, description
     771             = "The component used to draw the cells.")
     772     public void setCellRenderer(ListCellRenderer<? super E> cellRenderer) {
     773         ListCellRenderer<? super E> oldValue = this.cellRenderer;
     774         this.cellRenderer = cellRenderer;
     775
     776         /* If the cellRenderer has changed and prototypeCellValue
     777          * was set, then recompute fixedCellWidth and fixedCellHeight.
     778          */
     779         if ((cellRenderer != null) && !cellRenderer.equals(oldValue)) {
     780             updateFixedCellSize();
     781         }
     782
     783         firePropertyChange("cellRenderer", oldValue, cellRenderer);
     784     }
     785
     786
     787     /**
     788      * Returns the color used to draw the foreground of selected items.
     789      * {@code DefaultListCellRenderer} uses this color to draw the foreground
     790      * of items in the selected state, as do the renderers installed by most
     791      * {@code ListUI} implementations.
     792      *
     793      * @return the color to draw the foreground of selected items
     794      * @see #setSelectionForeground
     795      * @see DefaultListCellRenderer
     796      */
     797     public Color getSelectionForeground() {
     798         return selectionForeground;
     799     }
     800
     801
     802     /**
     803      * Sets the color used to draw the foreground of selected items, which
     804      * cell renderers can use to render text and graphics.
     805      * {@code DefaultListCellRenderer} uses this color to draw the foreground
     806      * of items in the selected state, as do the renderers installed by most
     807      * {@code ListUI} implementations.
     808      * <p>
     809      * The default value of this property is defined by the look and feel
     810      * implementation.
     811      * <p>
     812      * This is a JavaBeans bound property.
     813      *
     814      * @param selectionForeground  the {@code Color} to use in the foreground
     815      *                             for selected list items
     816      * @see #getSelectionForeground
     817      * @see #setSelectionBackground
     818      * @see #setForeground
     819      * @see #setBackground
     820      * @see #setFont
     821      * @see DefaultListCellRenderer
     822      */
     823     @BeanProperty(visualUpdate = true, description
     824             = "The foreground color of selected cells.")
     825     public void setSelectionForeground(Color selectionForeground) {
     826         Color oldValue = this.selectionForeground;
     827         this.selectionForeground = selectionForeground;
     828         firePropertyChange("selectionForeground", oldValue, selectionForeground);
     829     }
     830
     831
     832     /**
     833      * Returns the color used to draw the background of selected items.
     834      * {@code DefaultListCellRenderer} uses this color to draw the background
     835      * of items in the selected state, as do the renderers installed by most
     836      * {@code ListUI} implementations.
     837      *
     838      * @return the color to draw the background of selected items
     839      * @see #setSelectionBackground
     840      * @see DefaultListCellRenderer
     841      */
     842     public Color getSelectionBackground() {
     843         return selectionBackground;
     844     }
     845
     846
     847     /**
     848      * Sets the color used to draw the background of selected items, which
     849      * cell renderers can use fill selected cells.
     850      * {@code DefaultListCellRenderer} uses this color to fill the background
     851      * of items in the selected state, as do the renderers installed by most
     852      * {@code ListUI} implementations.
     853      * <p>
     854      * The default value of this property is defined by the look
     855      * and feel implementation.
     856      * <p>
     857      * This is a JavaBeans bound property.
     858      *
     859      * @param selectionBackground  the {@code Color} to use for the
     860      *                             background of selected cells
     861      * @see #getSelectionBackground
     862      * @see #setSelectionForeground
     863      * @see #setForeground
     864      * @see #setBackground
     865      * @see #setFont
     866      * @see DefaultListCellRenderer
     867      */
     868     @BeanProperty(visualUpdate = true, description
     869             = "The background color of selected cells.")
     870     public void setSelectionBackground(Color selectionBackground) {
     871         Color oldValue = this.selectionBackground;
     872         this.selectionBackground = selectionBackground;
     873         firePropertyChange("selectionBackground", oldValue, selectionBackground);
     874     }
     875
     876
     877     /**
     878      * Returns the value of the {@code visibleRowCount} property. See the
     879      * documentation for {@link #setVisibleRowCount} for details on how to
     880      * interpret this value.
     881      *
     882      * @return the value of the {@code visibleRowCount} property.
     883      * @see #setVisibleRowCount
     884      */
     885     public int getVisibleRowCount() {
     886         return visibleRowCount;
     887     }
     888
     889     /**
     890      * Sets the {@code visibleRowCount} property, which has different meanings
     891      * depending on the layout orientation: For a {@code VERTICAL} layout
     892      * orientation, this sets the preferred number of rows to display without
     893      * requiring scrolling; for other orientations, it affects the wrapping of
     894      * cells.
     895      * <p>
     896      * In {@code VERTICAL} orientation:<br>
     897      * Setting this property affects the return value of the
     898      * {@link #getPreferredScrollableViewportSize} method, which is used to
     899      * calculate the preferred size of an enclosing viewport. See that method's
     900      * documentation for more details.
     901      * <p>
     902      * In {@code HORIZONTAL_WRAP} and {@code VERTICAL_WRAP} orientations:<br>
     903      * This affects how cells are wrapped. See the documentation of
     904      * {@link #setLayoutOrientation} for more details.
     905      * <p>
     906      * The default value of this property is {@code 8}.
     907      * <p>
     908      * Calling this method with a negative value results in the property
     909      * being set to {@code 0}.
     910      * <p>
     911      * This is a JavaBeans bound property.
     912      *
     913      * @param visibleRowCount  an integer specifying the preferred number of
     914      *                         rows to display without requiring scrolling
     915      * @see #getVisibleRowCount
     916      * @see #getPreferredScrollableViewportSize
     917      * @see #setLayoutOrientation
     918      * @see JComponent#getVisibleRect
     919      * @see JViewport
     920      */
     921     @BeanProperty(visualUpdate = true, description
     922             = "The preferred number of rows to display without requiring scrolling")
     923     public void setVisibleRowCount(int visibleRowCount) {
     924         int oldValue = this.visibleRowCount;
     925         this.visibleRowCount = Math.max(0, visibleRowCount);
     926         firePropertyChange("visibleRowCount", oldValue, visibleRowCount);
     927     }
     928
     929
     930     /**
     931      * Returns the layout orientation property for the list: {@code VERTICAL}
     932      * if the layout is a single column of cells, {@code VERTICAL_WRAP} if the
     933      * layout is "newspaper style" with the content flowing vertically then
     934      * horizontally, or {@code HORIZONTAL_WRAP} if the layout is "newspaper
     935      * style" with the content flowing horizontally then vertically.
     936      *
     937      * @return the value of the {@code layoutOrientation} property
     938      * @see #setLayoutOrientation
     939      * @since 1.4
     940      */
     941     public int getLayoutOrientation() {
     942         return layoutOrientation;
     943     }
     944
     945
     946     /**
     947      * Defines the way list cells are layed out. Consider a {@code JList}
     948      * with five cells. Cells can be layed out in one of the following ways:
     949      *
     950      * <pre>
     951      * VERTICAL:          0
     952      *                    1
     953      *                    2
     954      *                    3
     955      *                    4
     956      *
     957      * HORIZONTAL_WRAP:   0  1  2
     958      *                    3  4
     959      *
     960      * VERTICAL_WRAP:     0  3
     961      *                    1  4
     962      *                    2
     963      * </pre>
     964      * <p>
     965      * A description of these layouts follows:
     966      *
     967      * <table class="striped">
     968      * <caption>Describes layouts VERTICAL,HORIZONTAL_WRAP, and VERTICAL_WRAP
     969      * </caption>
     970      * <thead>
     971      *   <tr>
     972      *     <th scope="col">Value
     973      *     <th scope="col">Description
     974      * </thead>
     975      * <tbody>
     976      *   <tr>
     977      *     <th scope="row">{@code VERTICAL}
     978      *     <td>Cells are layed out vertically in a single column.
     979      *   <tr>
     980      *     <th scope="row">{@code HORIZONTAL_WRAP}
     981      *     <td>Cells are layed out horizontally, wrapping to a new row as
     982      *     necessary. If the {@code visibleRowCount} property is less than or
     983      *     equal to zero, wrapping is determined by the width of the list;
     984      *     otherwise wrapping is done in such a way as to ensure
     985      *     {@code visibleRowCount} rows in the list.
     986      *   <tr>
     987      *     <th scope="row">{@code VERTICAL_WRAP}
     988      *     <td>Cells are layed out vertically, wrapping to a new column as
     989      *     necessary. If the {@code visibleRowCount} property is less than or
     990      *     equal to zero, wrapping is determined by the height of the list;
     991      *     otherwise wrapping is done at {@code visibleRowCount} rows.
     992      * </tbody>
     993      * </table>
     994      *
     995      * The default value of this property is <code>VERTICAL</code>.
     996      *
     997      * @param layoutOrientation the new layout orientation, one of:
     998      *        {@code VERTICAL}, {@code HORIZONTAL_WRAP} or {@code VERTICAL_WRAP}
     999      * @see #getLayoutOrientation
    1000      * @see #setVisibleRowCount
    1001      * @see #getScrollableTracksViewportHeight
    1002      * @see #getScrollableTracksViewportWidth
    1003      * @throws IllegalArgumentException if {@code layoutOrientation} isn't one of the
    1004      *         allowable values
    1005      * @since 1.4
    1006      */
    1007     @BeanProperty(visualUpdate = true, enumerationValues = {
    1008             "JList.VERTICAL",
    1009             "JList.HORIZONTAL_WRAP",
    1010             "JList.VERTICAL_WRAP"}, description
    1011             = "Defines the way list cells are layed out.")
    1012     public void setLayoutOrientation(int layoutOrientation) {
    1013         int oldValue = this.layoutOrientation;
    1014         switch (layoutOrientation) {
    1015         case VERTICAL:
    1016         case VERTICAL_WRAP:
    1017         case HORIZONTAL_WRAP:
    1018             this.layoutOrientation = layoutOrientation;
    1019             firePropertyChange("layoutOrientation", oldValue, layoutOrientation);
    1020             break;
    1021         default:
    1022             throw new IllegalArgumentException("layoutOrientation must be one of: VERTICAL, HORIZONTAL_WRAP or VERTICAL_WRAP");
    1023         }
    1024     }
    1025
    1026
    1027     /**
    1028      * Returns the smallest list index that is currently visible.
    1029      * In a left-to-right {@code componentOrientation}, the first visible
    1030      * cell is found closest to the list's upper-left corner. In right-to-left
    1031      * orientation, it is found closest to the upper-right corner.
    1032      * If nothing is visible or the list is empty, {@code -1} is returned.
    1033      * Note that the returned cell may only be partially visible.
    1034      *
    1035      * @return the index of the first visible cell
    1036      * @see #getLastVisibleIndex
    1037      * @see JComponent#getVisibleRect
    1038      */
    1039     @BeanProperty(bound = false)
    1040     public int getFirstVisibleIndex() {
    1041         Rectangle r = getVisibleRect();
    1042         int first;
    1043         if (this.getComponentOrientation().isLeftToRight()) {
    1044             first = locationToIndex(r.getLocation());
    1045         } else {
    1046             first = locationToIndex(new Point((r.x + r.width) - 1, r.y));
    1047         }
    1048         if (first != -1) {
    1049             Rectangle bounds = getCellBounds(first, first);
    1050             if (bounds != null) {
    1051                 SwingUtilities.computeIntersection(r.x, r.y, r.width, r.height, bounds);
    1052                 if (bounds.width == 0 || bounds.height == 0) {
    1053                     first = -1;
    1054                 }
    1055             }
    1056         }
    1057         return first;
    1058     }
    1059
    1060
    1061     /**
    1062      * Returns the largest list index that is currently visible.
    1063      * If nothing is visible or the list is empty, {@code -1} is returned.
    1064      * Note that the returned cell may only be partially visible.
    1065      *
    1066      * @return the index of the last visible cell
    1067      * @see #getFirstVisibleIndex
    1068      * @see JComponent#getVisibleRect
    1069      */
    1070     @BeanProperty(bound = false)
    1071     public int getLastVisibleIndex() {
    1072         boolean leftToRight = this.getComponentOrientation().isLeftToRight();
    1073         Rectangle r = getVisibleRect();
    1074         Point lastPoint;
    1075         if (leftToRight) {
    1076             lastPoint = new Point((r.x + r.width) - 1, (r.y + r.height) - 1);
    1077         } else {
    1078             lastPoint = new Point(r.x, (r.y + r.height) - 1);
    1079         }
    1080         int location = locationToIndex(lastPoint);
    1081
    1082         if (location != -1) {
    1083             Rectangle bounds = getCellBounds(location, location);
    1084
    1085             if (bounds != null) {
    1086                 SwingUtilities.computeIntersection(r.x, r.y, r.width, r.height, bounds);
    1087                 if (bounds.width == 0 || bounds.height == 0) {
    1088                     // Try the top left(LTR) or top right(RTL) corner, and
    1089                     // then go across checking each cell for HORIZONTAL_WRAP.
    1090                     // Try the lower left corner, and then go across checking
    1091                     // each cell for other list layout orientation.
    1092                     boolean isHorizontalWrap =
    1093                         (getLayoutOrientation() == HORIZONTAL_WRAP);
    1094                     Point visibleLocation = isHorizontalWrap ?
    1095                         new Point(lastPoint.x, r.y) :
    1096                         new Point(r.x, lastPoint.y);
    1097                     int last;
    1098                     int visIndex = -1;
    1099                     int lIndex = location;
    1100                     location = -1;
    1101
    1102                     do {
    1103                         last = visIndex;
    1104                         visIndex = locationToIndex(visibleLocation);
    1105
    1106                         if (visIndex != -1) {
    1107                             bounds = getCellBounds(visIndex, visIndex);
    1108                             if (visIndex != lIndex && bounds != null &&
    1109                                 bounds.contains(visibleLocation)) {
    1110                                 location = visIndex;
    1111                                 if (isHorizontalWrap) {
    1112                                     visibleLocation.y = bounds.y + bounds.height;
    1113                                     if (visibleLocation.y >= lastPoint.y) {
    1114                                         // Past visible region, bail.
    1115                                         last = visIndex;
    1116                                     }
    1117                                 }
    1118                                 else {
    1119                                     visibleLocation.x = bounds.x + bounds.width;
    1120                                     if (visibleLocation.x >= lastPoint.x) {
    1121                                         // Past visible region, bail.
    1122                                         last = visIndex;
    1123                                     }
    1124                                 }
    1125
    1126                             }
    1127                             else {
    1128                                 last = visIndex;
    1129                             }
    1130                         }
    1131                     } while (visIndex != -1 && last != visIndex);
    1132                 }
    1133             }
    1134         }
    1135         return location;
    1136     }
    1137
    1138
    1139     /**
    1140      * Scrolls the list within an enclosing viewport to make the specified
    1141      * cell completely visible. This calls {@code scrollRectToVisible} with
    1142      * the bounds of the specified cell. For this method to work, the
    1143      * {@code JList} must be within a <code>JViewport</code>.
    1144      * <p>
    1145      * If the given index is outside the list's range of cells, this method
    1146      * results in nothing.
    1147      *
    1148      * @param index  the index of the cell to make visible
    1149      * @see JComponent#scrollRectToVisible
    1150      * @see #getVisibleRect
    1151      */
    1152     public void ensureIndexIsVisible(int index) {
    1153         Rectangle cellBounds = getCellBounds(index, index);
    1154         if (cellBounds != null) {
    1155             scrollRectToVisible(cellBounds);
    1156         }
    1157     }
    1158
    1159     /**
    1160      * Turns on or off automatic drag handling. In order to enable automatic
    1161      * drag handling, this property should be set to {@code true}, and the
    1162      * list's {@code TransferHandler} needs to be {@code non-null}.
    1163      * The default value of the {@code dragEnabled} property is {@code false}.
    1164      * <p>
    1165      * The job of honoring this property, and recognizing a user drag gesture,
    1166      * lies with the look and feel implementation, and in particular, the list's
    1167      * {@code ListUI}. When automatic drag handling is enabled, most look and
    1168      * feels (including those that subclass {@code BasicLookAndFeel}) begin a
    1169      * drag and drop operation whenever the user presses the mouse button over
    1170      * an item and then moves the mouse a few pixels. Setting this property to
    1171      * {@code true} can therefore have a subtle effect on how selections behave.
    1172      * <p>
    1173      * If a look and feel is used that ignores this property, you can still
    1174      * begin a drag and drop operation by calling {@code exportAsDrag} on the
    1175      * list's {@code TransferHandler}.
    1176      *
    1177      * @param b whether or not to enable automatic drag handling
    1178      * @exception HeadlessException if
    1179      *            <code>b</code> is <code>true</code> and
    1180      *            <code>GraphicsEnvironment.isHeadless()</code>
    1181      *            returns <code>true</code>
    1182      * @see java.awt.GraphicsEnvironment#isHeadless
    1183      * @see #getDragEnabled
    1184      * @see #setTransferHandler
    1185      * @see TransferHandler
    1186      * @since 1.4
    1187      */
    1188     @BeanProperty(bound = false, description
    1189             = "determines whether automatic drag handling is enabled")
    1190     public void setDragEnabled(boolean b) {
    1191         if (b && GraphicsEnvironment.isHeadless()) {
    1192             throw new HeadlessException();
    1193         }
    1194         dragEnabled = b;
    1195     }
    1196
    1197     /**
    1198      * Returns whether or not automatic drag handling is enabled.
    1199      *
    1200      * @return the value of the {@code dragEnabled} property
    1201      * @see #setDragEnabled
    1202      * @since 1.4
    1203      */
    1204     public boolean getDragEnabled() {
    1205         return dragEnabled;
    1206     }
    1207
    1208     /**
    1209      * Sets the drop mode for this component. For backward compatibility,
    1210      * the default for this property is <code>DropMode.USE_SELECTION</code>.
    1211      * Usage of one of the other modes is recommended, however, for an
    1212      * improved user experience. <code>DropMode.ON</code>, for instance,
    1213      * offers similar behavior of showing items as selected, but does so without
    1214      * affecting the actual selection in the list.
    1215      * <p>
    1216      * <code>JList</code> supports the following drop modes:
    1217      * <ul>
    1218      *    <li><code>DropMode.USE_SELECTION</code></li>
    1219      *    <li><code>DropMode.ON</code></li>
    1220      *    <li><code>DropMode.INSERT</code></li>
    1221      *    <li><code>DropMode.ON_OR_INSERT</code></li>
    1222      * </ul>
    1223      * The drop mode is only meaningful if this component has a
    1224      * <code>TransferHandler</code> that accepts drops.
    1225      *
    1226      * @param dropMode the drop mode to use
    1227      * @throws IllegalArgumentException if the drop mode is unsupported
    1228      *         or <code>null</code>
    1229      * @see #getDropMode
    1230      * @see #getDropLocation
    1231      * @see #setTransferHandler
    1232      * @see TransferHandler
    1233      * @since 1.6
    1234      */
    1235     public final void setDropMode(DropMode dropMode) {
    1236         if (dropMode != null) {
    1237             switch (dropMode) {
    1238                 case USE_SELECTION:
    1239                 case ON:
    1240                 case INSERT:
    1241                 case ON_OR_INSERT:
    1242                     this.dropMode = dropMode;
    1243                     return;
    1244             }
    1245         }
    1246
    1247         throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for list");
    1248     }
    1249
    1250     /**
    1251      * Returns the drop mode for this component.
    1252      *
    1253      * @return the drop mode for this component
    1254      * @see #setDropMode
    1255      * @since 1.6
    1256      */
    1257     public final DropMode getDropMode() {
    1258         return dropMode;
    1259     }
    1260
    1261     /**
    1262      * Calculates a drop location in this component, representing where a
    1263      * drop at the given point should insert data.
    1264      *
    1265      * @param p the point to calculate a drop location for
    1266      * @return the drop location, or <code>null</code>
    1267      */
    1268     DropLocation dropLocationForPoint(Point p) {
    1269         DropLocation location = null;
    1270         Rectangle rect = null;
    1271
    1272         int index = locationToIndex(p);
    1273         if (index != -1) {
    1274             rect = getCellBounds(index, index);
    1275         }
    1276
    1277         switch(dropMode) {
    1278             case USE_SELECTION:
    1279             case ON:
    1280                 location = new DropLocation(p,
    1281                     (rect != null && rect.contains(p)) ? index : -1,
    1282                     false);
    1283
    1284                 break;
    1285             case INSERT:
    1286                 if (index == -1) {
    1287                     location = new DropLocation(p, getModel().getSize(), true);
    1288                     break;
    1289                 }
    1290
    1291                 if (layoutOrientation == HORIZONTAL_WRAP) {
    1292                     boolean ltr = getComponentOrientation().isLeftToRight();
    1293
    1294                     if (SwingUtilities2.liesInHorizontal(rect, p, ltr, false) == TRAILING) {
    1295                         index++;
    1296                     // special case for below all cells
    1297                     } else if (index == getModel().getSize() - 1 && p.y >= rect.y + rect.height) {
    1298                         index++;
    1299                     }
    1300                 } else {
    1301                     if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) {
    1302                         index++;
    1303                     }
    1304                 }
    1305
    1306                 location = new DropLocation(p, index, true);
    1307
    1308                 break;
    1309             case ON_OR_INSERT:
    1310                 if (index == -1) {
    1311                     location = new DropLocation(p, getModel().getSize(), true);
    1312                     break;
    1313                 }
    1314
    1315                 boolean between = false;
    1316
    1317                 if (layoutOrientation == HORIZONTAL_WRAP) {
    1318                     boolean ltr = getComponentOrientation().isLeftToRight();
    1319
    1320                     Section section = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
    1321                     if (section == TRAILING) {
    1322                         index++;
    1323                         between = true;
    1324                     // special case for below all cells
    1325                     } else if (index == getModel().getSize() - 1 && p.y >= rect.y + rect.height) {
    1326                         index++;
    1327                         between = true;
    1328                     } else if (section == LEADING) {
    1329                         between = true;
    1330                     }
    1331                 } else {
    1332                     Section section = SwingUtilities2.liesInVertical(rect, p, true);
    1333                     if (section == LEADING) {
    1334                         between = true;
    1335                     } else if (section == TRAILING) {
    1336                         index++;
    1337                         between = true;
    1338                     }
    1339                 }
    1340
    1341                 location = new DropLocation(p, index, between);
    1342
    1343                 break;
    1344             default:
    1345                 assert false : "Unexpected drop mode";
    1346         }
    1347
    1348         return location;
    1349     }
    1350
    1351     /**
    1352      * Called to set or clear the drop location during a DnD operation.
    1353      * In some cases, the component may need to use it's internal selection
    1354      * temporarily to indicate the drop location. To help facilitate this,
    1355      * this method returns and accepts as a parameter a state object.
    1356      * This state object can be used to store, and later restore, the selection
    1357      * state. Whatever this method returns will be passed back to it in
    1358      * future calls, as the state parameter. If it wants the DnD system to
    1359      * continue storing the same state, it must pass it back every time.
    1360      * Here's how this is used:
    1361      * <p>
    1362      * Let's say that on the first call to this method the component decides
    1363      * to save some state (because it is about to use the selection to show
    1364      * a drop index). It can return a state object to the caller encapsulating
    1365      * any saved selection state. On a second call, let's say the drop location
    1366      * is being changed to something else. The component doesn't need to
    1367      * restore anything yet, so it simply passes back the same state object
    1368      * to have the DnD system continue storing it. Finally, let's say this
    1369      * method is messaged with <code>null</code>. This means DnD
    1370      * is finished with this component for now, meaning it should restore
    1371      * state. At this point, it can use the state parameter to restore
    1372      * said state, and of course return <code>null</code> since there's
    1373      * no longer anything to store.
    1374      *
    1375      * @param location the drop location (as calculated by
    1376      *        <code>dropLocationForPoint</code>) or <code>null</code>
    1377      *        if there's no longer a valid drop location
    1378      * @param state the state object saved earlier for this component,
    1379      *        or <code>null</code>
    1380      * @param forDrop whether or not the method is being called because an
    1381      *        actual drop occurred
    1382      * @return any saved state for this component, or <code>null</code> if none
    1383      */
    1384     Object setDropLocation(TransferHandler.DropLocation location,
    1385                            Object state,
    1386                            boolean forDrop) {
    1387
    1388         Object retVal = null;
    1389         DropLocation listLocation = (DropLocation)location;
    1390
    1391         if (dropMode == DropMode.USE_SELECTION) {
    1392             if (listLocation == null) {
    1393                 if (!forDrop && state != null) {
    1394                     setSelectedIndices(((int[][])state)[0]);
    1395
    1396                     int anchor = ((int[][])state)[1][0];
    1397                     int lead = ((int[][])state)[1][1];
    1398
    1399                     SwingUtilities2.setLeadAnchorWithoutSelection(
    1400                             getSelectionModel(), lead, anchor);
    1401                 }
    1402             } else {
    1403                 if (dropLocation == null) {
    1404                     int[] inds = getSelectedIndices();
    1405                     retVal = new int[][] {inds, {getAnchorSelectionIndex(),
    1406                                                  getLeadSelectionIndex()}};
    1407                 } else {
    1408                     retVal = state;
    1409                 }
    1410
    1411                 int index = listLocation.getIndex();
    1412                 if (index == -1) {
    1413                     clearSelection();
    1414                     getSelectionModel().setAnchorSelectionIndex(-1);
    1415                     getSelectionModel().setLeadSelectionIndex(-1);
    1416                 } else {
    1417                     setSelectionInterval(index, index);
    1418                 }
    1419             }
    1420         }
    1421
    1422         DropLocation old = dropLocation;
    1423         dropLocation = listLocation;
    1424         firePropertyChange("dropLocation", old, dropLocation);
    1425
    1426         return retVal;
    1427     }
    1428
    1429     /**
    1430      * Returns the location that this component should visually indicate
    1431      * as the drop location during a DnD operation over the component,
    1432      * or {@code null} if no location is to currently be shown.
    1433      * <p>
    1434      * This method is not meant for querying the drop location
    1435      * from a {@code TransferHandler}, as the drop location is only
    1436      * set after the {@code TransferHandler}'s <code>canImport</code>
    1437      * has returned and has allowed for the location to be shown.
    1438      * <p>
    1439      * When this property changes, a property change event with
    1440      * name "dropLocation" is fired by the component.
    1441      * <p>
    1442      * By default, responsibility for listening for changes to this property
    1443      * and indicating the drop location visually lies with the list's
    1444      * {@code ListUI}, which may paint it directly and/or install a cell
    1445      * renderer to do so. Developers wishing to implement custom drop location
    1446      * painting and/or replace the default cell renderer, may need to honor
    1447      * this property.
    1448      *
    1449      * @return the drop location
    1450      * @see #setDropMode
    1451      * @see TransferHandler#canImport(TransferHandler.TransferSupport)
    1452      * @since 1.6
    1453      */
    1454     @BeanProperty(bound = false)
    1455     public final DropLocation getDropLocation() {
    1456         return dropLocation;
    1457     }
    1458
    1459     /**
    1460      * Returns the next list element whose {@code toString} value
    1461      * starts with the given prefix.
    1462      *
    1463      * @param prefix the string to test for a match
    1464      * @param startIndex the index for starting the search
    1465      * @param bias the search direction, either
    1466      * Position.Bias.Forward or Position.Bias.Backward.
    1467      * @return the index of the next list element that
    1468      * starts with the prefix; otherwise {@code -1}
    1469      * @exception IllegalArgumentException if prefix is {@code null}
    1470      * or startIndex is out of bounds
    1471      * @since 1.4
    1472      */
    1473     public int getNextMatch(String prefix, int startIndex, Position.Bias bias) {
    1474         ListModel<E> model = getModel();
    1475         int max = model.getSize();
    1476         if (prefix == null) {
    1477             throw new IllegalArgumentException();
    1478         }
    1479         if (startIndex < 0 || startIndex >= max) {
    1480             throw new IllegalArgumentException();
    1481         }
    1482         prefix = prefix.toUpperCase();
    1483
    1484         // start search from the next element after the selected element
    1485         int increment = (bias == Position.Bias.Forward) ? 1 : -1;
    1486         int index = startIndex;
    1487         do {
    1488             E element = model.getElementAt(index);
    1489
    1490             if (element != null) {
    1491                 String string;
    1492
    1493                 if (element instanceof String) {
    1494                     string = ((String)element).toUpperCase();
    1495                 }
    1496                 else {
    1497                     string = element.toString();
    1498                     if (string != null) {
    1499                         string = string.toUpperCase();
    1500                     }
    1501                 }
    1502
    1503                 if (string != null && string.startsWith(prefix)) {
    1504                     return index;
    1505                 }
    1506             }
    1507             index = (index + increment + max) % max;
    1508         } while (index != startIndex);
    1509         return -1;
    1510     }
    1511
    1512     /**
    1513      * Returns the tooltip text to be used for the given event. This overrides
    1514      * {@code JComponent}'s {@code getToolTipText} to first check the cell
    1515      * renderer component for the cell over which the event occurred, returning
    1516      * its tooltip text, if any. This implementation allows you to specify
    1517      * tooltip text on the cell level, by using {@code setToolTipText} on your
    1518      * cell renderer component.
    1519      * <p>
    1520      * <strong>Note:</strong> For <code>JList</code> to properly display the
    1521      * tooltips of its renderers in this manner, <code>JList</code> must be a
    1522      * registered component with the <code>ToolTipManager</code>. This registration
    1523      * is done automatically in the constructor. However, if at a later point
    1524      * <code>JList</code> is unregistered, by way of a call to
    1525      * {@code setToolTipText(null)}, tips from the renderers will no longer display.
    1526      *
    1527      * @param event the {@code MouseEvent} to fetch the tooltip text for
    1528      * @see JComponent#setToolTipText
    1529      * @see JComponent#getToolTipText
    1530      */
    1531     @SuppressWarnings("deprecation")
    1532     public String getToolTipText(MouseEvent event) {
    1533         if(event != null) {
    1534             Point p = event.getPoint();
    1535             int index = locationToIndex(p);
    1536             ListCellRenderer<? super E> r = getCellRenderer();
    1537             Rectangle cellBounds;
    1538
    1539             if (index != -1 && r != null && (cellBounds =
    1540                                getCellBounds(index, index)) != null &&
    1541                                cellBounds.contains(p.x, p.y)) {
    1542                 ListSelectionModel lsm = getSelectionModel();
    1543                 Component rComponent = r.getListCellRendererComponent(
    1544                            this, getModel().getElementAt(index), index,
    1545                            lsm.isSelectedIndex(index),
    1546                            (hasFocus() && (lsm.getLeadSelectionIndex() ==
    1547                                            index)));
    1548
    1549                 if(rComponent instanceof JComponent) {
    1550                     MouseEvent      newEvent;
    1551
    1552                     p.translate(-cellBounds.x, -cellBounds.y);
    1553                     newEvent = new MouseEvent(rComponent, event.getID(),
    1554                                               event.getWhen(),
    1555                                               event.getModifiers(),
    1556                                               p.x, p.y,
    1557                                               event.getXOnScreen(),
    1558                                               event.getYOnScreen(),
    1559                                               event.getClickCount(),
    1560                                               event.isPopupTrigger(),
    1561                                               MouseEvent.NOBUTTON);
    1562                     MouseEventAccessor meAccessor =
    1563                         AWTAccessor.getMouseEventAccessor();
    1564                     meAccessor.setCausedByTouchEvent(newEvent,
    1565                         meAccessor.isCausedByTouchEvent(event));
    1566
    1567                     String tip = ((JComponent)rComponent).getToolTipText(
    1568                                               newEvent);
    1569
    1570                     if (tip != null) {
    1571                         return tip;
    1572                     }
    1573                 }
    1574             }
    1575         }
    1576         return super.getToolTipText();
    1577     }
    1578
    1579     /**
    1580      * --- ListUI Delegations ---
    1581      */
    1582
    1583
    1584     /**
    1585      * Returns the cell index closest to the given location in the list's
    1586      * coordinate system. To determine if the cell actually contains the
    1587      * specified location, compare the point against the cell's bounds,
    1588      * as provided by {@code getCellBounds}. This method returns {@code -1}
    1589      * if the model is empty
    1590      * <p>
    1591      * This is a cover method that delegates to the method of the same name
    1592      * in the list's {@code ListUI}. It returns {@code -1} if the list has
    1593      * no {@code ListUI}.
    1594      *
    1595      * @param location the coordinates of the point
    1596      * @return the cell index closest to the given location, or {@code -1}
    1597      */
    1598     public int locationToIndex(Point location) {
    1599         ListUI ui = getUI();
    1600         return (ui != null) ? ui.locationToIndex(this, location) : -1;
    1601     }
    1602
    1603
    1604     /**
    1605      * Returns the origin of the specified item in the list's coordinate
    1606      * system. This method returns {@code null} if the index isn't valid.
    1607      * <p>
    1608      * This is a cover method that delegates to the method of the same name
    1609      * in the list's {@code ListUI}. It returns {@code null} if the list has
    1610      * no {@code ListUI}.
    1611      *
    1612      * @param index the cell index
    1613      * @return the origin of the cell, or {@code null}
    1614      */
    1615     public Point indexToLocation(int index) {
    1616         ListUI ui = getUI();
    1617         return (ui != null) ? ui.indexToLocation(this, index) : null;
    1618     }
    1619
    1620
    1621     /**
    1622      * Returns the bounding rectangle, in the list's coordinate system,
    1623      * for the range of cells specified by the two indices.
    1624      * These indices can be supplied in any order.
    1625      * <p>
    1626      * If the smaller index is outside the list's range of cells, this method
    1627      * returns {@code null}. If the smaller index is valid, but the larger
    1628      * index is outside the list's range, the bounds of just the first index
    1629      * is returned. Otherwise, the bounds of the valid range is returned.
    1630      * <p>
    1631      * This is a cover method that delegates to the method of the same name
    1632      * in the list's {@code ListUI}. It returns {@code null} if the list has
    1633      * no {@code ListUI}.
    1634      *
    1635      * @param index0 the first index in the range
    1636      * @param index1 the second index in the range
    1637      * @return the bounding rectangle for the range of cells, or {@code null}
    1638      */
    1639     public Rectangle getCellBounds(int index0, int index1) {
    1640         ListUI ui = getUI();
    1641         return (ui != null) ? ui.getCellBounds(this, index0, index1) : null;
    1642     }
    1643
    1644
    1645     /**
    1646      * --- ListModel Support ---
    1647      */
    1648
    1649
    1650     /**
    1651      * Returns the data model that holds the list of items displayed
    1652      * by the <code>JList</code> component.
    1653      *
    1654      * @return the <code>ListModel</code> that provides the displayed
    1655      *                          list of items
    1656      * @see #setModel
    1657      */
    1658     public ListModel<E> getModel() {
    1659         return dataModel;
    1660     }
    1661
    1662     /**
    1663      * Sets the model that represents the contents or "value" of the
    1664      * list, notifies property change listeners, and then clears the
    1665      * list's selection.
    1666      * <p>
    1667      * This is a JavaBeans bound property.
    1668      *
    1669      * @param model  the <code>ListModel</code> that provides the
    1670      *                                          list of items for display
    1671      * @exception IllegalArgumentException  if <code>model</code> is
    1672      *                                          <code>null</code>
    1673      * @see #getModel
    1674      * @see #clearSelection
    1675      */
    1676     @BeanProperty(visualUpdate = true, description
    1677             = "The object that contains the data to be drawn by this JList.")
    1678     public void setModel(ListModel<E> model) {
    1679         if (model == null) {
    1680             throw new IllegalArgumentException("model must be non null");
    1681         }
    1682         ListModel<E> oldValue = dataModel;
    1683         dataModel = model;
    1684         firePropertyChange("model", oldValue, dataModel);
    1685         clearSelection();
    1686     }
    1687
    1688
    1689     /**
    1690      * Constructs a read-only <code>ListModel</code> from an array of items,
    1691      * and calls {@code setModel} with this model.
    1692      * <p>
    1693      * Attempts to pass a {@code null} value to this method results in
    1694      * undefined behavior and, most likely, exceptions. The created model
    1695      * references the given array directly. Attempts to modify the array
    1696      * after invoking this method results in undefined behavior.
    1697      *
    1698      * @param listData an array of {@code E} containing the items to
    1699      *        display in the list
    1700      * @see #setModel
    1701      */
    1702     public void setListData(final E[] listData) {
    1703         setModel (
    1704             new AbstractListModel<E>() {
    1705                 public int getSize() { return listData.length; }
    1706                 public E getElementAt(int i) { return listData[i]; }
    1707             }
    1708         );
    1709     }
    1710
    1711
    1712     /**
    1713      * Constructs a read-only <code>ListModel</code> from a <code>Vector</code>
    1714      * and calls {@code setModel} with this model.
    1715      * <p>
    1716      * Attempts to pass a {@code null} value to this method results in
    1717      * undefined behavior and, most likely, exceptions. The created model
    1718      * references the given {@code Vector} directly. Attempts to modify the
    1719      * {@code Vector} after invoking this method results in undefined behavior.
    1720      *
    1721      * @param listData a <code>Vector</code> containing the items to
    1722      *                                          display in the list
    1723      * @see #setModel
    1724      */
    1725     public void setListData(final Vector<? extends E> listData) {
    1726         setModel (
    1727             new AbstractListModel<E>() {
    1728                 public int getSize() { return listData.size(); }
    1729                 public E getElementAt(int i) { return listData.elementAt(i); }
    1730             }
    1731         );
    1732     }
    1733
    1734
    1735     /**
    1736      * --- ListSelectionModel delegations and extensions ---
    1737      */
    1738
    1739
    1740     /**
    1741      * Returns an instance of {@code DefaultListSelectionModel}; called
    1742      * during construction to initialize the list's selection model
    1743      * property.
    1744      *
    1745      * @return a {@code DefaultListSelecitonModel}, used to initialize
    1746      *         the list's selection model property during construction
    1747      * @see #setSelectionModel
    1748      * @see DefaultListSelectionModel
    1749      */
    1750     protected ListSelectionModel createSelectionModel() {
    1751         return new DefaultListSelectionModel();
    1752     }
    1753
    1754
    1755     /**
    1756      * Returns the current selection model. The selection model maintains the
    1757      * selection state of the list. See the class level documentation for more
    1758      * details.
    1759      *
    1760      * @return the <code>ListSelectionModel</code> that maintains the
    1761      *         list's selections
    1762      *
    1763      * @see #setSelectionModel
    1764      * @see ListSelectionModel
    1765      */
    1766     public ListSelectionModel getSelectionModel() {
    1767         return selectionModel;
    1768     }
    1769
    1770
    1771     /**
    1772      * Notifies {@code ListSelectionListener}s added directly to the list
    1773      * of selection changes made to the selection model. {@code JList}
    1774      * listens for changes made to the selection in the selection model,
    1775      * and forwards notification to listeners added to the list directly,
    1776      * by calling this method.
    1777      * <p>
    1778      * This method constructs a {@code ListSelectionEvent} with this list
    1779      * as the source, and the specified arguments, and sends it to the
    1780      * registered {@code ListSelectionListeners}.
    1781      *
    1782      * @param firstIndex the first index in the range, {@code <= lastIndex}
    1783      * @param lastIndex the last index in the range, {@code >= firstIndex}
    1784      * @param isAdjusting whether or not this is one in a series of
    1785      *        multiple events, where changes are still being made
    1786      *
    1787      * @see #addListSelectionListener
    1788      * @see #removeListSelectionListener
    1789      * @see javax.swing.event.ListSelectionEvent
    1790      * @see EventListenerList
    1791      */
    1792     protected void fireSelectionValueChanged(int firstIndex, int lastIndex,
    1793                                              boolean isAdjusting)
    1794     {
    1795         Object[] listeners = listenerList.getListenerList();
    1796         ListSelectionEvent e = null;
    1797
    1798         for (int i = listeners.length - 2; i >= 0; i -= 2) {
    1799             if (listeners[i] == ListSelectionListener.class) {
    1800                 if (e == null) {
    1801                     e = new ListSelectionEvent(this, firstIndex, lastIndex,
    1802                                                isAdjusting);
    1803                 }
    1804                 ((ListSelectionListener)listeners[i+1]).valueChanged(e);
    1805             }
    1806         }
    1807     }
    1808
    1809
    1810     /* A ListSelectionListener that forwards ListSelectionEvents from
    1811      * the selectionModel to the JList ListSelectionListeners.  The
    1812      * forwarded events only differ from the originals in that their
    1813      * source is the JList instead of the selectionModel itself.
    1814      */
    1815     private class ListSelectionHandler implements ListSelectionListener, Serializable
    1816     {
    1817         public void valueChanged(ListSelectionEvent e) {
    1818             fireSelectionValueChanged(e.getFirstIndex(),
    1819                                       e.getLastIndex(),
    1820                                       e.getValueIsAdjusting());
    1821         }
    1822     }
    1823
    1824
    1825     /**
    1826      * Adds a listener to the list, to be notified each time a change to the
    1827      * selection occurs; the preferred way of listening for selection state
    1828      * changes. {@code JList} takes care of listening for selection state
    1829      * changes in the selection model, and notifies the given listener of
    1830      * each change. {@code ListSelectionEvent}s sent to the listener have a
    1831      * {@code source} property set to this list.
    1832      *
    1833      * @param listener the {@code ListSelectionListener} to add
    1834      * @see #getSelectionModel
    1835      * @see #getListSelectionListeners
    1836      */
    1837     public void addListSelectionListener(ListSelectionListener listener)
    1838     {
    1839         if (selectionListener == null) {
    1840             selectionListener = new ListSelectionHandler();
    1841             getSelectionModel().addListSelectionListener(selectionListener);
    1842         }
    1843
    1844         listenerList.add(ListSelectionListener.class, listener);
    1845     }
    1846
    1847
    1848     /**
    1849      * Removes a selection listener from the list.
    1850      *
    1851      * @param listener the {@code ListSelectionListener} to remove
    1852      * @see #addListSelectionListener
    1853      * @see #getSelectionModel
    1854      */
    1855     public void removeListSelectionListener(ListSelectionListener listener) {
    1856         listenerList.remove(ListSelectionListener.class, listener);
    1857     }
    1858
    1859
    1860     /**
    1861      * Returns an array of all the {@code ListSelectionListener}s added
    1862      * to this {@code JList} by way of {@code addListSelectionListener}.
    1863      *
    1864      * @return all of the {@code ListSelectionListener}s on this list, or
    1865      *         an empty array if no listeners have been added
    1866      * @see #addListSelectionListener
    1867      * @since 1.4
    1868      */
    1869     @BeanProperty(bound = false)
    1870     public ListSelectionListener[] getListSelectionListeners() {
    1871         return listenerList.getListeners(ListSelectionListener.class);
    1872     }
    1873
    1874
    1875     /**
    1876      * Sets the <code>selectionModel</code> for the list to a
    1877      * non-<code>null</code> <code>ListSelectionModel</code>
    1878      * implementation. The selection model handles the task of making single
    1879      * selections, selections of contiguous ranges, and non-contiguous
    1880      * selections.
    1881      * <p>
    1882      * This is a JavaBeans bound property.
    1883      *
    1884      * @param selectionModel  the <code>ListSelectionModel</code> that
    1885      *                          implements the selections
    1886      * @exception IllegalArgumentException   if <code>selectionModel</code>
    1887      *                                          is <code>null</code>
    1888      * @see #getSelectionModel
    1889      */
    1890     @BeanProperty(description
    1891             = "The selection model, recording which cells are selected.")
    1892     public void setSelectionModel(ListSelectionModel selectionModel) {
    1893         if (selectionModel == null) {
    1894             throw new IllegalArgumentException("selectionModel must be non null");
    1895         }
    1896
    1897         /* Remove the forwarding ListSelectionListener from the old
    1898          * selectionModel, and add it to the new one, if necessary.
    1899          */
    1900         if (selectionListener != null) {
    1901             this.selectionModel.removeListSelectionListener(selectionListener);
    1902             selectionModel.addListSelectionListener(selectionListener);
    1903         }
    1904
    1905         ListSelectionModel oldValue = this.selectionModel;
    1906         this.selectionModel = selectionModel;
    1907         firePropertyChange("selectionModel", oldValue, selectionModel);
    1908     }
    1909
    1910
    1911     /**
    1912      * Sets the selection mode for the list. This is a cover method that sets
    1913      * the selection mode directly on the selection model.
    1914      * <p>
    1915      * The following list describes the accepted selection modes:
    1916      * <ul>
    1917      * <li>{@code ListSelectionModel.SINGLE_SELECTION} -
    1918      *   Only one list index can be selected at a time. In this mode,
    1919      *   {@code setSelectionInterval} and {@code addSelectionInterval} are
    1920      *   equivalent, both replacing the current selection with the index
    1921      *   represented by the second argument (the "lead").
    1922      * <li>{@code ListSelectionModel.SINGLE_INTERVAL_SELECTION} -
    1923      *   Only one contiguous interval can be selected at a time.
    1924      *   In this mode, {@code addSelectionInterval} behaves like
    1925      *   {@code setSelectionInterval} (replacing the current selection},
    1926      *   unless the given interval is immediately adjacent to or overlaps
    1927      *   the existing selection, and can be used to grow the selection.
    1928      * <li>{@code ListSelectionModel.MULTIPLE_INTERVAL_SELECTION} -
    1929      *   In this mode, there's no restriction on what can be selected.
    1930      *   This mode is the default.
    1931      * </ul>
    1932      *
    1933      * @param selectionMode the selection mode
    1934      * @see #getSelectionMode
    1935      * @throws IllegalArgumentException if the selection mode isn't
    1936      *         one of those allowed
    1937      */
    1938     @BeanProperty(bound = false, enumerationValues = {
    1939             "ListSelectionModel.SINGLE_SELECTION",
    1940             "ListSelectionModel.SINGLE_INTERVAL_SELECTION",
    1941             "ListSelectionModel.MULTIPLE_INTERVAL_SELECTION"}, description
    1942             = "The selection mode.")
    1943     public void setSelectionMode(int selectionMode) {
    1944         getSelectionModel().setSelectionMode(selectionMode);
    1945     }
    1946
    1947     /**
    1948      * Returns the current selection mode for the list. This is a cover
    1949      * method that delegates to the method of the same name on the
    1950      * list's selection model.
    1951      *
    1952      * @return the current selection mode
    1953      * @see #setSelectionMode
    1954      */
    1955     public int getSelectionMode() {
    1956         return getSelectionModel().getSelectionMode();
    1957     }
    1958
    1959
    1960     /**
    1961      * Returns the anchor selection index. This is a cover method that
    1962      * delegates to the method of the same name on the list's selection model.
    1963      *
    1964      * @return the anchor selection index
    1965      * @see ListSelectionModel#getAnchorSelectionIndex
    1966      */
    1967     @BeanProperty(bound = false)
    1968     public int getAnchorSelectionIndex() {
    1969         return getSelectionModel().getAnchorSelectionIndex();
    1970     }
    1971
    1972
    1973     /**
    1974      * Returns the lead selection index. This is a cover method that
    1975      * delegates to the method of the same name on the list's selection model.
    1976      *
    1977      * @return the lead selection index
    1978      * @see ListSelectionModel#getLeadSelectionIndex
    1979      */
    1980     @BeanProperty(bound = false, description
    1981             = "The lead selection index.")
    1982     public int getLeadSelectionIndex() {
    1983         return getSelectionModel().getLeadSelectionIndex();
    1984     }
    1985
    1986
    1987     /**
    1988      * Returns the smallest selected cell index, or {@code -1} if the selection
    1989      * is empty. This is a cover method that delegates to the method of the same
    1990      * name on the list's selection model.
    1991      *
    1992      * @return the smallest selected cell index, or {@code -1}
    1993      * @see ListSelectionModel#getMinSelectionIndex
    1994      */
    1995     @BeanProperty(bound = false)
    1996     public int getMinSelectionIndex() {
    1997         return getSelectionModel().getMinSelectionIndex();
    1998     }
    1999
    2000
    2001     /**
    2002      * Returns the largest selected cell index, or {@code -1} if the selection
    2003      * is empty. This is a cover method that delegates to the method of the same
    2004      * name on the list's selection model.
    2005      *
    2006      * @return the largest selected cell index
    2007      * @see ListSelectionModel#getMaxSelectionIndex
    2008      */
    2009     @BeanProperty(bound = false)
    2010     public int getMaxSelectionIndex() {
    2011         return getSelectionModel().getMaxSelectionIndex();
    2012     }
    2013
    2014
    2015     /**
    2016      * Returns {@code true} if the specified index is selected,
    2017      * else {@code false}. This is a cover method that delegates to the method
    2018      * of the same name on the list's selection model.
    2019      *
    2020      * @param index index to be queried for selection state
    2021      * @return {@code true} if the specified index is selected,
    2022      *         else {@code false}
    2023      * @see ListSelectionModel#isSelectedIndex
    2024      * @see #setSelectedIndex
    2025      */
    2026     public boolean isSelectedIndex(int index) {
    2027         return getSelectionModel().isSelectedIndex(index);
    2028     }
    2029
    2030
    2031     /**
    2032      * Returns {@code true} if nothing is selected, else {@code false}.
    2033      * This is a cover method that delegates to the method of the same
    2034      * name on the list's selection model.
    2035      *
    2036      * @return {@code true} if nothing is selected, else {@code false}
    2037      * @see ListSelectionModel#isSelectionEmpty
    2038      * @see #clearSelection
    2039      */
    2040     @BeanProperty(bound = false)
    2041     public boolean isSelectionEmpty() {
    2042         return getSelectionModel().isSelectionEmpty();
    2043     }
    2044
    2045
    2046     /**
    2047      * Clears the selection; after calling this method, {@code isSelectionEmpty}
    2048      * will return {@code true}. This is a cover method that delegates to the
    2049      * method of the same name on the list's selection model.
    2050      *
    2051      * @see ListSelectionModel#clearSelection
    2052      * @see #isSelectionEmpty
    2053      */
    2054     public void clearSelection() {
    2055         getSelectionModel().clearSelection();
    2056     }
    2057
    2058
    2059     /**
    2060      * Selects the specified interval. Both {@code anchor} and {@code lead}
    2061      * indices are included. {@code anchor} doesn't have to be less than or
    2062      * equal to {@code lead}. This is a cover method that delegates to the
    2063      * method of the same name on the list's selection model.
    2064      * <p>
    2065      * Refer to the documentation of the selection model class being used
    2066      * for details on how values less than {@code 0} are handled.
    2067      *
    2068      * @param anchor the first index to select
    2069      * @param lead the last index to select
    2070      * @see ListSelectionModel#setSelectionInterval
    2071      * @see DefaultListSelectionModel#setSelectionInterval
    2072      * @see #createSelectionModel
    2073      * @see #addSelectionInterval
    2074      * @see #removeSelectionInterval
    2075      */
    2076     public void setSelectionInterval(int anchor, int lead) {
    2077         getSelectionModel().setSelectionInterval(anchor, lead);
    2078     }
    2079
    2080
    2081     /**
    2082      * Sets the selection to be the union of the specified interval with current
    2083      * selection. Both the {@code anchor} and {@code lead} indices are
    2084      * included. {@code anchor} doesn't have to be less than or
    2085      * equal to {@code lead}. This is a cover method that delegates to the
    2086      * method of the same name on the list's selection model.
    2087      * <p>
    2088      * Refer to the documentation of the selection model class being used
    2089      * for details on how values less than {@code 0} are handled.
    2090      *
    2091      * @param anchor the first index to add to the selection
    2092      * @param lead the last index to add to the selection
    2093      * @see ListSelectionModel#addSelectionInterval
    2094      * @see DefaultListSelectionModel#addSelectionInterval
    2095      * @see #createSelectionModel
    2096      * @see #setSelectionInterval
    2097      * @see #removeSelectionInterval
    2098      */
    2099     public void addSelectionInterval(int anchor, int lead) {
    2100         getSelectionModel().addSelectionInterval(anchor, lead);
    2101     }
    2102
    2103
    2104     /**
    2105      * Sets the selection to be the set difference of the specified interval
    2106      * and the current selection. Both the {@code index0} and {@code index1}
    2107      * indices are removed. {@code index0} doesn't have to be less than or
    2108      * equal to {@code index1}. This is a cover method that delegates to the
    2109      * method of the same name on the list's selection model.
    2110      * <p>
    2111      * Refer to the documentation of the selection model class being used
    2112      * for details on how values less than {@code 0} are handled.
    2113      *
    2114      * @param index0 the first index to remove from the selection
    2115      * @param index1 the last index to remove from the selection
    2116      * @see ListSelectionModel#removeSelectionInterval
    2117      * @see DefaultListSelectionModel#removeSelectionInterval
    2118      * @see #createSelectionModel
    2119      * @see #setSelectionInterval
    2120      * @see #addSelectionInterval
    2121      */
    2122     public void removeSelectionInterval(int index0, int index1) {
    2123         getSelectionModel().removeSelectionInterval(index0, index1);
    2124     }
    2125
    2126
    2127     /**
    2128      * Sets the selection model's {@code valueIsAdjusting} property. When
    2129      * {@code true}, upcoming changes to selection should be considered part
    2130      * of a single change. This property is used internally and developers
    2131      * typically need not call this method. For example, when the model is being
    2132      * updated in response to a user drag, the value of the property is set
    2133      * to {@code true} when the drag is initiated and set to {@code false}
    2134      * when the drag is finished. This allows listeners to update only
    2135      * when a change has been finalized, rather than handling all of the
    2136      * intermediate values.
    2137      * <p>
    2138      * You may want to use this directly if making a series of changes
    2139      * that should be considered part of a single change.
    2140      * <p>
    2141      * This is a cover method that delegates to the method of the same name on
    2142      * the list's selection model. See the documentation for
    2143      * {@link javax.swing.ListSelectionModel#setValueIsAdjusting} for
    2144      * more details.
    2145      *
    2146      * @param b the new value for the property
    2147      * @see ListSelectionModel#setValueIsAdjusting
    2148      * @see javax.swing.event.ListSelectionEvent#getValueIsAdjusting
    2149      * @see #getValueIsAdjusting
    2150      */
    2151     public void setValueIsAdjusting(boolean b) {
    2152         getSelectionModel().setValueIsAdjusting(b);
    2153     }
    2154
    2155
    2156     /**
    2157      * Returns the value of the selection model's {@code isAdjusting} property.
    2158      * <p>
    2159      * This is a cover method that delegates to the method of the same name on
    2160      * the list's selection model.
    2161      *
    2162      * @return the value of the selection model's {@code isAdjusting} property.
    2163      *
    2164      * @see #setValueIsAdjusting
    2165      * @see ListSelectionModel#getValueIsAdjusting
    2166      */
    2167     public boolean getValueIsAdjusting() {
    2168         return getSelectionModel().getValueIsAdjusting();
    2169     }
    2170
    2171
    2172     /**
    2173      * Returns an array of all of the selected indices, in increasing
    2174      * order.
    2175      *
    2176      * @return all of the selected indices, in increasing order,
    2177      *         or an empty array if nothing is selected
    2178      * @see #removeSelectionInterval
    2179      * @see #addListSelectionListener
    2180      */
    2181     @Transient
    2182     public int[] getSelectedIndices() {
    2183         ListSelectionModel sm = getSelectionModel();
    2184         int iMin = sm.getMinSelectionIndex();
    2185         int iMax = sm.getMaxSelectionIndex();
    2186
    2187         if ((iMin < 0) || (iMax < 0)) {
    2188             return new int[0];
    2189         }
    2190
    2191         int[] rvTmp = new int[1+ (iMax - iMin)];
    2192         int n = 0;
    2193         for(int i = iMin; i <= iMax; i++) {
    2194             if (sm.isSelectedIndex(i)) {
    2195                 rvTmp[n++] = i;
    2196             }
    2197         }
    2198         int[] rv = new int[n];
    2199         System.arraycopy(rvTmp, 0, rv, 0, n);
    2200         return rv;
    2201     }
    2202
    2203
    2204     /**
    2205      * Selects a single cell. Does nothing if the given index is greater
    2206      * than or equal to the model size. This is a convenience method that uses
    2207      * {@code setSelectionInterval} on the selection model. Refer to the
    2208      * documentation for the selection model class being used for details on
    2209      * how values less than {@code 0} are handled.
    2210      *
    2211      * @param index the index of the cell to select
    2212      * @see ListSelectionModel#setSelectionInterval
    2213      * @see #isSelectedIndex
    2214      * @see #addListSelectionListener
    2215      */
    2216     @BeanProperty(bound = false, description
    2217             = "The index of the selected cell.")
    2218     public void setSelectedIndex(int index) {
    2219         if (index >= getModel().getSize()) {
    2220             return;
    2221         }
    2222         getSelectionModel().setSelectionInterval(index, index);
    2223     }
    2224
    2225
    2226     /**
    2227      * Changes the selection to be the set of indices specified by the given
    2228      * array. Indices greater than or equal to the model size are ignored.
    2229      * This is a convenience method that clears the selection and then uses
    2230      * {@code addSelectionInterval} on the selection model to add the indices.
    2231      * Refer to the documentation of the selection model class being used for
    2232      * details on how values less than {@code 0} are handled.
    2233      *
    2234      * @param indices an array of the indices of the cells to select,
    2235      *                {@code non-null}
    2236      * @see ListSelectionModel#addSelectionInterval
    2237      * @see #isSelectedIndex
    2238      * @see #addListSelectionListener
    2239      * @throws NullPointerException if the given array is {@code null}
    2240      */
    2241     public void setSelectedIndices(int[] indices) {
    2242         ListSelectionModel sm = getSelectionModel();
    2243         sm.clearSelection();
    2244         int size = getModel().getSize();
    2245         for (int i : indices) {
    2246             if (i < size) {
    2247                 sm.addSelectionInterval(i, i);
    2248             }
    2249         }
    2250     }
    2251
    2252
    2253     /**
    2254      * Returns an array of all the selected values, in increasing order based
    2255      * on their indices in the list.
    2256      *
    2257      * @return the selected values, or an empty array if nothing is selected
    2258      * @see #isSelectedIndex
    2259      * @see #getModel
    2260      * @see #addListSelectionListener
    2261      *
    2262      * @deprecated As of JDK 1.7, replaced by {@link #getSelectedValuesList()}
    2263      */
    2264     @Deprecated
    2265     @BeanProperty(bound = false)
    2266     public Object[] getSelectedValues() {
    2267         ListSelectionModel sm = getSelectionModel();
    2268         ListModel<E> dm = getModel();
    2269
    2270         int iMin = sm.getMinSelectionIndex();
    2271         int iMax = sm.getMaxSelectionIndex();
    2272
    2273         if ((iMin < 0) || (iMax < 0)) {
    2274             return new Object[0];
    2275         }
    2276
    2277         Object[] rvTmp = new Object[1+ (iMax - iMin)];
    2278         int n = 0;
    2279         for(int i = iMin; i <= iMax; i++) {
    2280             if (sm.isSelectedIndex(i)) {
    2281                 rvTmp[n++] = dm.getElementAt(i);
    2282             }
    2283         }
    2284         Object[] rv = new Object[n];
    2285         System.arraycopy(rvTmp, 0, rv, 0, n);
    2286         return rv;
    2287     }
    2288
    2289     /**
    2290      * Returns a list of all the selected items, in increasing order based
    2291      * on their indices in the list.
    2292      *
    2293      * @return the selected items, or an empty list if nothing is selected
    2294      * @see #isSelectedIndex
    2295      * @see #getModel
    2296      * @see #addListSelectionListener
    2297      *
    2298      * @since 1.7
    2299      */
    2300     @BeanProperty(bound = false)
    2301     public List<E> getSelectedValuesList() {
    2302         ListSelectionModel sm = getSelectionModel();
    2303         ListModel<E> dm = getModel();
    2304
    2305         int iMin = sm.getMinSelectionIndex();
    2306         int iMax = sm.getMaxSelectionIndex();
    2307
    2308         if ((iMin < 0) || (iMax < 0)) {
    2309             return Collections.emptyList();
    2310         }
    2311
    2312         List<E> selectedItems = new ArrayList<E>();
    2313         for(int i = iMin; i <= iMax; i++) {
    2314             if (sm.isSelectedIndex(i)) {
    2315                 selectedItems.add(dm.getElementAt(i));
    2316             }
    2317         }
    2318         return selectedItems;
    2319     }
    2320
    2321
    2322     /**
    2323      * Returns the smallest selected cell index; <i>the selection</i> when only
    2324      * a single item is selected in the list. When multiple items are selected,
    2325      * it is simply the smallest selected index. Returns {@code -1} if there is
    2326      * no selection.
    2327      * <p>
    2328      * This method is a cover that delegates to {@code getMinSelectionIndex}.
    2329      *
    2330      * @return the smallest selected cell index
    2331      * @see #getMinSelectionIndex
    2332      * @see #addListSelectionListener
    2333      */
    2334     public int getSelectedIndex() {
    2335         return getMinSelectionIndex();
    2336     }
    2337
    2338
    2339     /**
    2340      * Returns the value for the smallest selected cell index;
    2341      * <i>the selected value</i> when only a single item is selected in the
    2342      * list. When multiple items are selected, it is simply the value for the
    2343      * smallest selected index. Returns {@code null} if there is no selection.
    2344      * <p>
    2345      * This is a convenience method that simply returns the model value for
    2346      * {@code getMinSelectionIndex}.
    2347      *
    2348      * @return the first selected value
    2349      * @see #getMinSelectionIndex
    2350      * @see #getModel
    2351      * @see #addListSelectionListener
    2352      */
    2353     @BeanProperty(bound = false)
    2354     public E getSelectedValue() {
    2355         int i = getMinSelectionIndex();
    2356         return (i == -1) ? null : getModel().getElementAt(i);
    2357     }
    2358
    2359
    2360     /**
    2361      * Selects the specified object from the list.
    2362      *

    2363      * @param anObject      the object to select
    2364      * @param shouldScroll  {@code true} if the list should scroll to display
    2365      *                      the selected object, if one exists; otherwise {@code false}
    2366      */
    2367     public void setSelectedValue(Object anObject,boolean shouldScroll) {
    2368         if(anObject == null)
    2369             setSelectedIndex(-1);
    2370         else if(!anObject.equals(getSelectedValue())) {
    2371             int i,c;
    2372             ListModel<E> dm = getModel();
    2373             for(i=0,c=dm.getSize();i<c;i++)
    2374                 if(anObject.equals(dm.getElementAt(i))){
    2375                     setSelectedIndex(i);
    2376                     if(shouldScroll)
    2377                         ensureIndexIsVisible(i);
    2378                     repaint();  /** FIX-ME setSelectedIndex does not redraw all the time with the basic l&f**/
    2379                     return;
    2380                 }
    2381             setSelectedIndex(-1);
    2382         }
    2383         repaint(); /** FIX-ME setSelectedIndex does not redraw all the time with the basic l&f**/
    2384     }
    2385
    2386
    2387
    2388     /**
    2389      * --- The Scrollable Implementation ---
    2390      */
    2391
    2392     private void checkScrollableParameters(Rectangle visibleRect, int orientation) {
    2393         if (visibleRect == null) {
    2394             throw new IllegalArgumentException("visibleRect must be non-null");
    2395         }
    2396         switch (orientation) {
    2397         case SwingConstants.VERTICAL:
    2398         case SwingConstants.HORIZONTAL:
    2399             break;
    2400         default:
    2401             throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
    2402         }
    2403     }
    2404
    2405
    2406     /**
    2407      * Computes the size of viewport needed to display {@code visibleRowCount}
    2408      * rows. The value returned by this method depends on the layout
    2409      * orientation:
    2410      * <p>
    2411      * <b>{@code VERTICAL}:</b>
    2412      * <br>
    2413      * This is trivial if both {@code fixedCellWidth} and {@code fixedCellHeight}
    2414      * have been set (either explicitly or by specifying a prototype cell value).
    2415      * The width is simply the {@code fixedCellWidth} plus the list's horizontal
    2416      * insets. The height is the {@code fixedCellHeight} multiplied by the
    2417      * {@code visibleRowCount}, plus the list's vertical insets.
    2418      * <p>
    2419      * If either {@code fixedCellWidth} or {@code fixedCellHeight} haven't been
    2420      * specified, heuristics are used. If the model is empty, the width is
    2421      * the {@code fixedCellWidth}, if greater than {@code 0}, or a hard-coded
    2422      * value of {@code 256}. The height is the {@code fixedCellHeight} multiplied
    2423      * by {@code visibleRowCount}, if {@code fixedCellHeight} is greater than
    2424      * {@code 0}, otherwise it is a hard-coded value of {@code 16} multiplied by
    2425      * {@code visibleRowCount}.
    2426      * <p>
    2427      * If the model isn't empty, the width is the preferred size's width,
    2428      * typically the width of the widest list element. The height is the
    2429      * {@code fixedCellHeight} multiplied by the {@code visibleRowCount},
    2430      * plus the list's vertical insets.
    2431      * <p>
    2432      * <b>{@code VERTICAL_WRAP} or {@code HORIZONTAL_WRAP}:</b>
    2433      * <br>
    2434      * This method simply returns the value from {@code getPreferredSize}.
    2435      * The list's {@code ListUI} is expected to override {@code getPreferredSize}
    2436      * to return an appropriate value.
    2437      *
    2438      * @return a dimension containing the size of the viewport needed
    2439      *          to display {@code visibleRowCount} rows
    2440      * @see #getPreferredScrollableViewportSize
    2441      * @see #setPrototypeCellValue
    2442      */
    2443     @BeanProperty(bound = false)
    2444     public Dimension getPreferredScrollableViewportSize()
    2445     {
    2446         if (getLayoutOrientation() != VERTICAL) {
    2447             return getPreferredSize();
    2448         }
    2449         Insets insets = getInsets();
    2450         int dx = insets.left + insets.right;
    2451         int dy = insets.top + insets.bottom;
    2452
    2453         int visibleRowCount = getVisibleRowCount();
    2454         int fixedCellWidth = getFixedCellWidth();
    2455         int fixedCellHeight = getFixedCellHeight();
    2456
    2457         if ((fixedCellWidth > 0) && (fixedCellHeight > 0)) {
    2458             int width = fixedCellWidth + dx;
    2459             int height = (visibleRowCount * fixedCellHeight) + dy;
    2460             return new Dimension(width, height);
    2461         }
    2462         else if (getModel().getSize() > 0) {
    2463             int width = getPreferredSize().width;
    2464             int height;
    2465             Rectangle r = getCellBounds(0, 0);
    2466             if (r != null) {
    2467                 height = (visibleRowCount * r.height) + dy;
    2468             }
    2469             else {
    2470                 // Will only happen if UI null, shouldn't matter what we return
    2471                 height = 1;
    2472             }
    2473             return new Dimension(width, height);
    2474         }
    2475         else {
    2476             fixedCellWidth = (fixedCellWidth > 0) ? fixedCellWidth : 256;
    2477             fixedCellHeight = (fixedCellHeight > 0) ? fixedCellHeight : 16;
    2478             return new Dimension(fixedCellWidth, fixedCellHeight * visibleRowCount);
    2479         }
    2480     }
    2481
    2482
    2483     /**
    2484      * Returns the distance to scroll to expose the next or previous
    2485      * row (for vertical scrolling) or column (for horizontal scrolling).
    2486      * <p>
    2487      * For horizontal scrolling, if the layout orientation is {@code VERTICAL},
    2488      * then the list's font size is returned (or {@code 1} if the font is
    2489      * {@code null}).
    2490      *
    2491      * @param visibleRect the view area visible within the viewport
    2492      * @param orientation {@code SwingConstants.HORIZONTAL} or
    2493      *                    {@code SwingConstants.VERTICAL}
    2494      * @param direction less or equal to zero to scroll up/back,
    2495      *                  greater than zero for down/forward
    2496      * @return the "unit" increment for scrolling in the specified direction;
    2497      *         always positive
    2498      * @see #getScrollableBlockIncrement
    2499      * @see Scrollable#getScrollableUnitIncrement
    2500      * @throws IllegalArgumentException if {@code visibleRect} is {@code null}, or
    2501      *         {@code orientation} isn't one of {@code SwingConstants.VERTICAL} or
    2502      *         {@code SwingConstants.HORIZONTAL}
    2503      */
    2504     public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
    2505     {
    2506         checkScrollableParameters(visibleRect, orientation);
    2507
    2508         if (orientation == SwingConstants.VERTICAL) {
    2509             int row = locationToIndex(visibleRect.getLocation());
    2510
    2511             if (row == -1) {
    2512                 return 0;
    2513             }
    2514             else {
    2515                 /* Scroll Down */
    2516                 if (direction > 0) {
    2517                     Rectangle r = getCellBounds(row, row);
    2518                     return (r == null) ? 0 : r.height - (visibleRect.y - r.y);
    2519                 }
    2520                 /* Scroll Up */
    2521                 else {
    2522                     Rectangle r = getCellBounds(row, row);
    2523
    2524                     /* The first row is completely visible and it's row 0.
    2525                      * We're done.
    2526                      */
    2527                     if ((r.y == visibleRect.y) && (row == 0))  {
    2528                         return 0;
    2529                     }
    2530                     /* The first row is completely visible, return the
    2531                      * height of the previous row or 0 if the first row
    2532                      * is the top row of the list.
    2533                      */
    2534                     else if (r.y == visibleRect.y) {
    2535                         Point loc = r.getLocation();
    2536                         loc.y--;
    2537                         int prevIndex = locationToIndex(loc);
    2538                         Rectangle prevR = getCellBounds(prevIndex, prevIndex);
    2539
    2540                         if (prevR == null || prevR.y >= r.y) {
    2541                             return 0;
    2542                         }
    2543                         return prevR.height;
    2544                     }
    2545                     /* The first row is partially visible, return the
    2546                      * height of hidden part.
    2547                      */
    2548                     else {
    2549                         return visibleRect.y - r.y;
    2550                     }
    2551                 }
    2552             }
    2553         } else if (orientation == SwingConstants.HORIZONTAL &&
    2554                            getLayoutOrientation() != JList.VERTICAL) {
    2555             boolean leftToRight = getComponentOrientation().isLeftToRight();
    2556             int index;
    2557             Point leadingPoint;
    2558
    2559             if (leftToRight) {
    2560                 leadingPoint = visibleRect.getLocation();
    2561             }
    2562             else {
    2563                 leadingPoint = new Point(visibleRect.x + visibleRect.width -1,
    2564                                          visibleRect.y);
    2565             }
    2566             index = locationToIndex(leadingPoint);
    2567
    2568             if (index != -1) {
    2569                 Rectangle cellBounds = getCellBounds(index, index);
    2570                 if (cellBounds != null && cellBounds.contains(leadingPoint)) {
    2571                     int leadingVisibleEdge;
    2572                     int leadingCellEdge;
    2573
    2574                     if (leftToRight) {
    2575                         leadingVisibleEdge = visibleRect.x;
    2576                         leadingCellEdge = cellBounds.x;
    2577                     }
    2578                     else {
    2579                         leadingVisibleEdge = visibleRect.x + visibleRect.width;
    2580                         leadingCellEdge = cellBounds.x + cellBounds.width;
    2581                     }
    2582
    2583                     if (leadingCellEdge != leadingVisibleEdge) {
    2584                         if (direction < 0) {
    2585                             // Show remainder of leading cell
    2586                             return Math.abs(leadingVisibleEdge - leadingCellEdge);
    2587
    2588                         }
    2589                         else if (leftToRight) {
    2590                             // Hide rest of leading cell
    2591                             return leadingCellEdge + cellBounds.width - leadingVisibleEdge;
    2592                         }
    2593                         else {
    2594                             // Hide rest of leading cell
    2595                             return leadingVisibleEdge - cellBounds.x;
    2596                         }
    2597                     }
    2598                     // ASSUME: All cells are the same width
    2599                     return cellBounds.width;
    2600                 }
    2601             }
    2602         }
    2603         Font f = getFont();
    2604         return (f != null) ? f.getSize() : 1;
    2605     }
    2606
    2607
    2608     /**
    2609      * Returns the distance to scroll to expose the next or previous block.
    2610      * <p>
    2611      * For vertical scrolling, the following rules are used:
    2612      * <ul>
    2613      * <li>if scrolling down, returns the distance to scroll so that the last
    2614      * visible element becomes the first completely visible element
    2615      * <li>if scrolling up, returns the distance to scroll so that the first
    2616      * visible element becomes the last completely visible element
    2617      * <li>returns {@code visibleRect.height} if the list is empty
    2618      * </ul>
    2619      * <p>
    2620      * For horizontal scrolling, when the layout orientation is either
    2621      * {@code VERTICAL_WRAP} or {@code HORIZONTAL_WRAP}:
    2622      * <ul>
    2623      * <li>if scrolling right, returns the distance to scroll so that the
    2624      * last visible element becomes
    2625      * the first completely visible element
    2626      * <li>if scrolling left, returns the distance to scroll so that the first
    2627      * visible element becomes the last completely visible element
    2628      * <li>returns {@code visibleRect.width} if the list is empty
    2629      * </ul>
    2630      * <p>
    2631      * For horizontal scrolling and {@code VERTICAL} orientation,
    2632      * returns {@code visibleRect.width}.
    2633      * <p>
    2634      * Note that the value of {@code visibleRect} must be the equal to
    2635      * {@code this.getVisibleRect()}.
    2636      *
    2637      * @param visibleRect the view area visible within the viewport
    2638      * @param orientation {@code SwingConstants.HORIZONTAL} or
    2639      *                    {@code SwingConstants.VERTICAL}
    2640      * @param direction less or equal to zero to scroll up/back,
    2641      *                  greater than zero for down/forward
    2642      * @return the "block" increment for scrolling in the specified direction;
    2643      *         always positive
    2644      * @see #getScrollableUnitIncrement
    2645      * @see Scrollable#getScrollableBlockIncrement
    2646      * @throws IllegalArgumentException if {@code visibleRect} is {@code null}, or
    2647      *         {@code orientation} isn't one of {@code SwingConstants.VERTICAL} or
    2648      *         {@code SwingConstants.HORIZONTAL}
    2649      */
    2650     public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
    2651         checkScrollableParameters(visibleRect, orientation);
    2652         if (orientation == SwingConstants.VERTICAL) {
    2653             int inc = visibleRect.height;
    2654             /* Scroll Down */
    2655             if (direction > 0) {
    2656                 // last cell is the lowest left cell
    2657                 int last = locationToIndex(new Point(visibleRect.x, visibleRect.y+visibleRect.height-1));
    2658                 if (last != -1) {
    2659                     Rectangle lastRect = getCellBounds(last,last);
    2660                     if (lastRect != null) {
    2661                         inc = lastRect.y - visibleRect.y;
    2662                         if ( (inc == 0) && (last < getModel().getSize()-1) ) {
    2663                             inc = lastRect.height;
    2664                         }
    2665                     }
    2666                 }
    2667             }
    2668             /* Scroll Up */
    2669             else {
    2670                 int newFirst = locationToIndex(new Point(visibleRect.x, visibleRect.y-visibleRect.height));
    2671                 int first = getFirstVisibleIndex();
    2672                 if (newFirst != -1) {
    2673                     if (first == -1) {
    2674                         first = locationToIndex(visibleRect.getLocation());
    2675                     }
    2676                     Rectangle newFirstRect = getCellBounds(newFirst,newFirst);
    2677                     Rectangle firstRect = getCellBounds(first,first);
    2678                     if ((newFirstRect != null) && (firstRect!=null)) {
    2679                         while ( (newFirstRect.y + visibleRect.height <
    2680                                  firstRect.y + firstRect.height) &&
    2681                                 (newFirstRect.y < firstRect.y) ) {
    2682                             newFirst++;
    2683                             newFirstRect = getCellBounds(newFirst,newFirst);
    2684                         }
    2685                         inc = visibleRect.y - newFirstRect.y;
    2686                         if ( (inc <= 0) && (newFirstRect.y > 0)) {
    2687                             newFirst--;
    2688                             newFirstRect = getCellBounds(newFirst,newFirst);
    2689                             if (newFirstRect != null) {
    2690                                 inc = visibleRect.y - newFirstRect.y;
    2691                             }
    2692                         }
    2693                     }
    2694                 }
    2695             }
    2696             return inc;
    2697         }
    2698         else if (orientation == SwingConstants.HORIZONTAL &&
    2699                  getLayoutOrientation() != JList.VERTICAL) {
    2700             boolean leftToRight = getComponentOrientation().isLeftToRight();
    2701             int inc = visibleRect.width;
    2702             /* Scroll Right (in ltr mode) or Scroll Left (in rtl mode) */
    2703             if (direction > 0) {
    2704                 // position is upper right if ltr, or upper left otherwise
    2705                 int x = visibleRect.x + (leftToRight ? (visibleRect.width - 1) : 0);
    2706                 int last = locationToIndex(new Point(x, visibleRect.y));
    2707
    2708                 if (last != -1) {
    2709                     Rectangle lastRect = getCellBounds(last,last);
    2710                     if (lastRect != null) {
    2711                         if (leftToRight) {
    2712                             inc = lastRect.x - visibleRect.x;
    2713                         } else {
    2714                             inc = visibleRect.x + visibleRect.width
    2715                                       - (lastRect.x + lastRect.width);
    2716                         }
    2717                         if (inc < 0) {
    2718                             inc += lastRect.width;
    2719                         } else if ( (inc == 0) && (last < getModel().getSize()-1) ) {
    2720                             inc = lastRect.width;
    2721                         }
    2722                     }
    2723                 }
    2724             }
    2725             /* Scroll Left (in ltr mode) or Scroll Right (in rtl mode) */
    2726             else {
    2727                 // position is upper left corner of the visibleRect shifted
    2728                 // left by the visibleRect.width if ltr, or upper right shifted
    2729                 // right by the visibleRect.width otherwise
    2730                 int x = visibleRect.x + (leftToRight
    2731                                          ? -visibleRect.width
    2732                                          : visibleRect.width - 1 + visibleRect.width);
    2733                 int first = locationToIndex(new Point(x, visibleRect.y));
    2734
    2735                 if (first != -1) {
    2736                     Rectangle firstRect = getCellBounds(first,first);
    2737                     if (firstRect != null) {
    2738                         // the right of the first cell
    2739                         int firstRight = firstRect.x + firstRect.width;
    2740
    2741                         if (leftToRight) {
    2742                             if ((firstRect.x < visibleRect.x - visibleRect.width)
    2743                                     && (firstRight < visibleRect.x)) {
    2744                                 inc = visibleRect.x - firstRight;
    2745                             } else {
    2746                                 inc = visibleRect.x - firstRect.x;
    2747                             }
    2748                         } else {
    2749                             int visibleRight = visibleRect.x + visibleRect.width;
    2750
    2751                             if ((firstRight > visibleRight + visibleRect.width)
    2752                                     && (firstRect.x > visibleRight)) {
    2753                                 inc = firstRect.x - visibleRight;
    2754                             } else {
    2755                                 inc = firstRight - visibleRight;
    2756                             }
    2757                         }
    2758                     }
    2759                 }
    2760             }
    2761             return inc;
    2762         }
    2763         return visibleRect.width;
    2764     }
    2765
    2766
    2767     /**
    2768      * Returns {@code true} if this {@code JList} is displayed in a
    2769      * {@code JViewport} and the viewport is wider than the list's
    2770      * preferred width, or if the layout orientation is {@code HORIZONTAL_WRAP}
    2771      * and {@code visibleRowCount <= 0}; otherwise returns {@code false}.
    2772      * <p>
    2773      * If {@code false}, then don't track the viewport's width. This allows
    2774      * horizontal scrolling if the {@code JViewport} is itself embedded in a
    2775      * {@code JScrollPane}.
    2776      *
    2777      * @return whether or not an enclosing viewport should force the list's
    2778      *         width to match its own
    2779      * @see Scrollable#getScrollableTracksViewportWidth
    2780      */
    2781     @BeanProperty(bound = false)
    2782     public boolean getScrollableTracksViewportWidth() {
    2783         if (getLayoutOrientation() == HORIZONTAL_WRAP &&
    2784                                       getVisibleRowCount() <= 0) {
    2785             return true;
    2786         }
    2787         Container parent = SwingUtilities.getUnwrappedParent(this);
    2788         if (parent instanceof JViewport) {
    2789             return parent.getWidth() > getPreferredSize().width;
    2790         }
    2791         return false;
    2792     }
    2793
    2794     /**
    2795      * Returns {@code true} if this {@code JList} is displayed in a
    2796      * {@code JViewport} and the viewport is taller than the list's
    2797      * preferred height, or if the layout orientation is {@code VERTICAL_WRAP}
    2798      * and {@code visibleRowCount <= 0}; otherwise returns {@code false}.
    2799      * <p>
    2800      * If {@code false}, then don't track the viewport's height. This allows
    2801      * vertical scrolling if the {@code JViewport} is itself embedded in a
    2802      * {@code JScrollPane}.
    2803      *
    2804      * @return whether or not an enclosing viewport should force the list's
    2805      *         height to match its own
    2806      * @see Scrollable#getScrollableTracksViewportHeight
    2807      */
    2808     @BeanProperty(bound = false)
    2809     public boolean getScrollableTracksViewportHeight() {
    2810         if (getLayoutOrientation() == VERTICAL_WRAP &&
    2811                      getVisibleRowCount() <= 0) {
    2812             return true;
    2813         }
    2814         Container parent = SwingUtilities.getUnwrappedParent(this);
    2815         if (parent instanceof JViewport) {
    2816             return parent.getHeight() > getPreferredSize().height;
    2817         }
    2818         return false;
    2819     }
    2820
    2821
    2822     /*
    2823      * See {@code readObject} and {@code writeObject} in {@code JComponent}
    2824      * for more information about serialization in Swing.
    2825      */
    2826     private void writeObject(ObjectOutputStream s) throws IOException {
    2827         s.defaultWriteObject();
    2828         if (getUIClassID().equals(uiClassID)) {
    2829             byte count = JComponent.getWriteObjCounter(this);
    2830             JComponent.setWriteObjCounter(this, --count);
    2831             if (count == 0 && ui != null) {
    2832                 ui.installUI(this);
    2833             }
    2834         }
    2835     }
    2836
    2837
    2838     /**
    2839      * Returns a {@code String} representation of this {@code JList}.
    2840      * This method is intended to be used only for debugging purposes,
    2841      * and the content and format of the returned {@code String} may vary
    2842      * between implementations. The returned {@code String} may be empty,
    2843      * but may not be {@code null}.
    2844      *
    2845      * @return  a {@code String} representation of this {@code JList}.
    2846      */
    2847     protected String paramString() {
    2848         String selectionForegroundString = (selectionForeground != null ?
    2849                                             selectionForeground.toString() :
    2850                                             "");
    2851         String selectionBackgroundString = (selectionBackground != null ?
    2852                                             selectionBackground.toString() :
    2853                                             "");
    2854
    2855         return super.paramString() +
    2856         ",fixedCellHeight=" + fixedCellHeight +
    2857         ",fixedCellWidth=" + fixedCellWidth +
    2858         ",horizontalScrollIncrement=" + horizontalScrollIncrement +
    2859         ",selectionBackground=" + selectionBackgroundString +
    2860         ",selectionForeground=" + selectionForegroundString +
    2861         ",visibleRowCount=" + visibleRowCount +
    2862         ",layoutOrientation=" + layoutOrientation;
    2863     }
    2864
    2865
    2866     /**
    2867      * --- Accessibility Support ---
    2868      */
    2869
    2870     /**
    2871      * Gets the {@code AccessibleContext} associated with this {@code JList}.
    2872      * For {@code JList}, the {@code AccessibleContext} takes the form of an
    2873      * {@code AccessibleJList}.
    2874      * <p>
    2875      * A new {@code AccessibleJList} instance is created if necessary.
    2876      *
    2877      * @return an {@code AccessibleJList} that serves as the
    2878      *         {@code AccessibleContext} of this {@code JList}
    2879      */
    2880     @BeanProperty(bound = false)
    2881     public AccessibleContext getAccessibleContext() {
    2882         if (accessibleContext == null) {
    2883             accessibleContext = new AccessibleJList();
    2884         }
    2885         return accessibleContext;
    2886     }
    2887
    2888     /**
    2889      * This class implements accessibility support for the
    2890      * {@code JList} class. It provides an implementation of the
    2891      * Java Accessibility API appropriate to list user-interface
    2892      * elements.
    2893      * <p>
    2894      * <strong>Warning:</strong>
    2895      * Serialized objects of this class will not be compatible with
    2896      * future Swing releases. The current serialization support is
    2897      * appropriate for short term storage or RMI between applications running
    2898      * the same version of Swing.  As of 1.4, support for long term storage
    2899      * of all JavaBeans&trade;
    2900      * has been added to the <code>java.beans</code> package.
    2901      * Please see {@link java.beans.XMLEncoder}.
    2902      */
    2903     @SuppressWarnings("serial") // Same-version serialization only
    2904     protected class AccessibleJList extends AccessibleJComponent
    2905         implements AccessibleSelection, PropertyChangeListener,
    2906         ListSelectionListener, ListDataListener {
    2907
    2908         int leadSelectionIndex;
    2909
    2910         /**
    2911          * Constructs an {@code AccessibleJList}.
    2912          */
    2913         public AccessibleJList() {
    2914             super();
    2915             JList.this.addPropertyChangeListener(this);
    2916             JList.this.getSelectionModel().addListSelectionListener(this);
    2917             JList.this.getModel().addListDataListener(this);
    2918             leadSelectionIndex = JList.this.getLeadSelectionIndex();
    2919         }
    2920
    2921         /**
    2922          * Property Change Listener change method. Used to track changes
    2923          * to the DataModel and ListSelectionModel, in order to re-set
    2924          * listeners to those for reporting changes there via the Accessibility
    2925          * PropertyChange mechanism.
    2926          *
    2927          * @param e PropertyChangeEvent
    2928          */
    2929         public void propertyChange(PropertyChangeEvent e) {
    2930             String name = e.getPropertyName();
    2931             Object oldValue = e.getOldValue();
    2932             Object newValue = e.getNewValue();
    2933
    2934                 // re-set listData listeners
    2935             if (name.compareTo("model") == 0) {
    2936
    2937                 if (oldValue != null && oldValue instanceof ListModel) {
    2938                     ((ListModel) oldValue).removeListDataListener(this);
    2939                 }
    2940                 if (newValue != null && newValue instanceof ListModel) {
    2941                     ((ListModel) newValue).addListDataListener(this);
    2942                 }
    2943
    2944                 // re-set listSelectionModel listeners
    2945             } else if (name.compareTo("selectionModel") == 0) {
    2946
    2947                 if (oldValue != null && oldValue instanceof ListSelectionModel) {
    2948                     ((ListSelectionModel) oldValue).removeListSelectionListener(this);
    2949                 }
    2950                 if (newValue != null && newValue instanceof ListSelectionModel) {
    2951                     ((ListSelectionModel) newValue).addListSelectionListener(this);
    2952                 }
    2953
    2954                 firePropertyChange(
    2955                     AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
    2956                     Boolean.valueOf(false), Boolean.valueOf(true));
    2957             }
    2958         }
    2959
    2960         /**
    2961          * List Selection Listener value change method. Used to fire
    2962          * the property change
    2963          *
    2964          * @param e ListSelectionEvent
    2965          *
    2966          */
    2967         public void valueChanged(ListSelectionEvent e) {
    2968             int oldLeadSelectionIndex = leadSelectionIndex;
    2969             leadSelectionIndex = JList.this.getLeadSelectionIndex();
    2970             if (oldLeadSelectionIndex != leadSelectionIndex) {
    2971                 Accessible oldLS, newLS;
    2972                 oldLS = (oldLeadSelectionIndex >= 0)
    2973                         ? getAccessibleChild(oldLeadSelectionIndex)
    2974                         : null;
    2975                 newLS = (leadSelectionIndex >= 0)
    2976                         ? getAccessibleChild(leadSelectionIndex)
    2977                         : null;
    2978                 firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
    2979                                    oldLS, newLS);
    2980             }
    2981
    2982             firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
    2983                                Boolean.valueOf(false), Boolean.valueOf(true));
    2984             firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
    2985                                Boolean.valueOf(false), Boolean.valueOf(true));
    2986
    2987             // Process the State changes for Multiselectable
    2988             AccessibleStateSet s = getAccessibleStateSet();
    2989             ListSelectionModel lsm = JList.this.getSelectionModel();
    2990             if (lsm.getSelectionMode() != ListSelectionModel.SINGLE_SELECTION) {
    2991                 if (!s.contains(AccessibleState.MULTISELECTABLE)) {
    2992                     s.add(AccessibleState.MULTISELECTABLE);
    2993                     firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
    2994                                        null, AccessibleState.MULTISELECTABLE);
    2995                 }
    2996             } else {
    2997                 if (s.contains(AccessibleState.MULTISELECTABLE)) {
    2998                     s.remove(AccessibleState.MULTISELECTABLE);
    2999                     firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
    3000                                        AccessibleState.MULTISELECTABLE, null);
    3001                 }
    3002             }
    3003         }
    3004
    3005         /**
    3006          * List Data Listener interval added method. Used to fire the visible data property change
    3007          *
    3008          * @param e ListDataEvent
    3009          *
    3010          */
    3011         public void intervalAdded(ListDataEvent e) {
    3012             firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
    3013                                Boolean.valueOf(false), Boolean.valueOf(true));
    3014         }
    3015
    3016         /**
    3017          * List Data Listener interval removed method. Used to fire the visible data property change
    3018          *
    3019          * @param e ListDataEvent
    3020          *
    3021          */
    3022         public void intervalRemoved(ListDataEvent e) {
    3023             firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
    3024                                Boolean.valueOf(false), Boolean.valueOf(true));
    3025         }
    3026
    3027         /**
    3028          * List Data Listener contents changed method. Used to fire the visible data property change
    3029          *
    3030          * @param e ListDataEvent
    3031          *
    3032          */
    3033          public void contentsChanged(ListDataEvent e) {
    3034              firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
    3035                                 Boolean.valueOf(false), Boolean.valueOf(true));
    3036          }
    3037
    3038     // AccessibleContext methods
    3039
    3040         /**
    3041          * Get the state set of this object.
    3042          *
    3043          * @return an instance of AccessibleState containing the current state
    3044          * of the object
    3045          * @see AccessibleState
    3046          */
    3047         public AccessibleStateSet getAccessibleStateSet() {
    3048             AccessibleStateSet states = super.getAccessibleStateSet();
    3049             if (selectionModel.getSelectionMode() !=
    3050                 ListSelectionModel.SINGLE_SELECTION) {
    3051                 states.add(AccessibleState.MULTISELECTABLE);
    3052             }
    3053             return states;
    3054         }
    3055
    3056         /**
    3057          * Get the role of this object.
    3058          *
    3059          * @return an instance of AccessibleRole describing the role of the
    3060          * object
    3061          * @see AccessibleRole
    3062          */
    3063         public AccessibleRole getAccessibleRole() {
    3064             return AccessibleRole.LIST;
    3065         }
    3066
    3067         /**
    3068          * Returns the <code>Accessible</code> child contained at
    3069          * the local coordinate <code>Point</code>, if one exists.
    3070          * Otherwise returns <code>null</code>.
    3071          *
    3072          * @return the <code>Accessible</code> at the specified
    3073          *    location, if it exists
    3074          */
    3075         public Accessible getAccessibleAt(Point p) {
    3076             int i = locationToIndex(p);
    3077             if (i >= 0) {
    3078                 return new AccessibleJListChild(JList.this, i);
    3079             } else {
    3080                 return null;
    3081             }
    3082         }
    3083
    3084         /**
    3085          * Returns the number of accessible children in the object.  If all
    3086          * of the children of this object implement Accessible, than this
    3087          * method should return the number of children of this object.
    3088          *
    3089          * @return the number of accessible children in the object.
    3090          */
    3091         public int getAccessibleChildrenCount() {
    3092             return getModel().getSize();
    3093         }
    3094
    3095         /**
    3096          * Return the nth Accessible child of the object.
    3097          *
    3098          * @param i zero-based index of child
    3099          * @return the nth Accessible child of the object
    3100          */
    3101         public Accessible getAccessibleChild(int i) {
    3102             if (i >= getModel().getSize()) {
    3103                 return null;
    3104             } else {
    3105                 return new AccessibleJListChild(JList.this, i);
    3106             }
    3107         }
    3108
    3109         /**
    3110          * Get the AccessibleSelection associated with this object.  In the
    3111          * implementation of the Java Accessibility API for this class,
    3112          * return this object, which is responsible for implementing the
    3113          * AccessibleSelection interface on behalf of itself.
    3114          *
    3115          * @return this object
    3116          */
    3117         public AccessibleSelection getAccessibleSelection() {
    3118             return this;
    3119         }
    3120
    3121
    3122     // AccessibleSelection methods
    3123
    3124         /**
    3125          * Returns the number of items currently selected.
    3126          * If no items are selected, the return value will be 0.
    3127          *
    3128          * @return the number of items currently selected.
    3129          */
    3130          public int getAccessibleSelectionCount() {
    3131              return JList.this.getSelectedIndices().length;
    3132          }
    3133
    3134         /**
    3135          * Returns an Accessible representing the specified selected item
    3136          * in the object.  If there isn't a selection, or there are
    3137          * fewer items selected than the integer passed in, the return
    3138          * value will be <code>null</code>.
    3139          *
    3140          * @param i the zero-based index of selected items
    3141          * @return an Accessible containing the selected item
    3142          */
    3143          public Accessible getAccessibleSelection(int i) {
    3144              int len = getAccessibleSelectionCount();
    3145              if (i < 0 || i >= len) {
    3146                  return null;
    3147              } else {
    3148                  return getAccessibleChild(JList.this.getSelectedIndices()[i]);
    3149              }
    3150          }
    3151
    3152         /**
    3153          * Returns true if the current child of this object is selected.
    3154          *
    3155          * @param i the zero-based index of the child in this Accessible
    3156          * object.
    3157          * @see AccessibleContext#getAccessibleChild
    3158          */
    3159         public boolean isAccessibleChildSelected(int i) {
    3160             return isSelectedIndex(i);
    3161         }
    3162
    3163         /**
    3164          * Adds the specified selected item in the object to the object's
    3165          * selection.  If the object supports multiple selections,
    3166          * the specified item is added to any existing selection, otherwise
    3167          * it replaces any existing selection in the object.  If the
    3168          * specified item is already selected, this method has no effect.
    3169          *
    3170          * @param i the zero-based index of selectable items
    3171          */
    3172          public void addAccessibleSelection(int i) {
    3173              JList.this.addSelectionInterval(i, i);
    3174          }
    3175
    3176         /**
    3177          * Removes the specified selected item in the object from the object's
    3178          * selection.  If the specified item isn't currently selected, this
    3179          * method has no effect.
    3180          *
    3181          * @param i the zero-based index of selectable items
    3182          */
    3183          public void removeAccessibleSelection(int i) {
    3184              JList.this.removeSelectionInterval(i, i);
    3185          }
    3186
    3187         /**
    3188          * Clears the selection in the object, so that nothing in the
    3189          * object is selected.
    3190          */
    3191          public void clearAccessibleSelection() {
    3192              JList.this.clearSelection();
    3193          }
    3194
    3195         /**
    3196          * Causes every selected item in the object to be selected
    3197          * if the object supports multiple selections.
    3198          */
    3199          public void selectAllAccessibleSelection() {
    3200              JList.this.addSelectionInterval(0, getAccessibleChildrenCount() -1);
    3201          }
    3202
    3203           /**
    3204            * This class implements accessibility support appropriate
    3205            * for list children.
    3206            */
    3207         protected class AccessibleJListChild extends AccessibleContext
    3208                 implements Accessible, AccessibleComponent, AccessibleAction {
    3209             private JList<E>     parent = null;
    3210             int indexInParent;
    3211             private Component component = null;
    3212             private AccessibleContext accessibleContext = null;
    3213             private ListModel<E> listModel;
    3214             private ListCellRenderer<? super E> cellRenderer = null;
    3215
    3216             /**
    3217              * Constructs an {@code AccessibleJListChild}.
    3218              * @param parent the parent
    3219              * @param indexInParent the index in the parent
    3220              */
    3221             public AccessibleJListChild(JList<E> parent, int indexInParent) {
    3222                 this.parent = parent;
    3223                 this.setAccessibleParent(parent);
    3224                 this.indexInParent = indexInParent;
    3225                 if (parent != null) {
    3226                     listModel = parent.getModel();
    3227                     cellRenderer = parent.getCellRenderer();
    3228                 }
    3229             }
    3230
    3231             private Component getCurrentComponent() {
    3232                 return getComponentAtIndex(indexInParent);
    3233             }
    3234
    3235             AccessibleContext getCurrentAccessibleContext() {
    3236                 Component c = getComponentAtIndex(indexInParent);
    3237                 if (c instanceof Accessible) {
    3238                     return c.getAccessibleContext();
    3239                 } else {
    3240                     return null;
    3241                 }
    3242             }
    3243
    3244             private Component getComponentAtIndex(int index) {
    3245                 if (index < 0 || index >= listModel.getSize()) {
    3246                     return null;
    3247                 }
    3248                 if ((parent != null)
    3249                         && (listModel != null)
    3250                         && cellRenderer != null) {
    3251                     E value = listModel.getElementAt(index);
    3252                     boolean isSelected = parent.isSelectedIndex(index);
    3253                     boolean isFocussed = parent.isFocusOwner()
    3254                             && (index == parent.getLeadSelectionIndex());
    3255                     return cellRenderer.getListCellRendererComponent(
    3256                             parent,
    3257                             value,
    3258                             index,
    3259                             isSelected,
    3260                             isFocussed);
    3261                 } else {
    3262                     return null;
    3263                 }
    3264             }
    3265
    3266
    3267             // Accessible Methods
    3268            /**
    3269             * Get the AccessibleContext for this object. In the
    3270             * implementation of the Java Accessibility API for this class,
    3271             * returns this object, which is its own AccessibleContext.
    3272             *
    3273             * @return this object
    3274             */
    3275             public AccessibleContext getAccessibleContext() {
    3276                 return this;
    3277             }
    3278
    3279
    3280             // AccessibleContext methods
    3281
    3282             public String getAccessibleName() {
    3283                 AccessibleContext ac = getCurrentAccessibleContext();
    3284                 if (ac != null) {
    3285                     return ac.getAccessibleName();
    3286                 } else {
    3287                     return null;
    3288                 }
    3289             }
    3290
    3291             public void setAccessibleName(String s) {
    3292                 AccessibleContext ac = getCurrentAccessibleContext();
    3293                 if (ac != null) {
    3294                     ac.setAccessibleName(s);
    3295                 }
    3296             }
    3297
    3298             public String getAccessibleDescription() {
    3299                 AccessibleContext ac = getCurrentAccessibleContext();
    3300                 if (ac != null) {
    3301                     return ac.getAccessibleDescription();
    3302                 } else {
    3303                     return null;
    3304                 }
    3305             }
    3306
    3307             public void setAccessibleDescription(String s) {
    3308                 AccessibleContext ac = getCurrentAccessibleContext();
    3309                 if (ac != null) {
    3310                     ac.setAccessibleDescription(s);
    3311                 }
    3312             }
    3313
    3314             public AccessibleRole getAccessibleRole() {
    3315                 AccessibleContext ac = getCurrentAccessibleContext();
    3316                 if (ac != null) {
    3317                     return ac.getAccessibleRole();
    3318                 } else {
    3319                     return null;
    3320                 }
    3321             }
    3322
    3323             public AccessibleStateSet getAccessibleStateSet() {
    3324                 AccessibleContext ac = getCurrentAccessibleContext();
    3325                 AccessibleStateSet s;
    3326                 if (ac != null) {
    3327                     s = ac.getAccessibleStateSet();
    3328                 } else {
    3329                     s = new AccessibleStateSet();
    3330                 }
    3331
    3332                 s.add(AccessibleState.SELECTABLE);
    3333                 if (parent.isFocusOwner()
    3334                     && (indexInParent == parent.getLeadSelectionIndex())) {
    3335                     s.add(AccessibleState.ACTIVE);
    3336                 }
    3337                 if (parent.isSelectedIndex(indexInParent)) {
    3338                     s.add(AccessibleState.SELECTED);
    3339                 }
    3340                 if (this.isShowing()) {
    3341                     s.add(AccessibleState.SHOWING);
    3342                 } else if (s.contains(AccessibleState.SHOWING)) {
    3343                     s.remove(AccessibleState.SHOWING);
    3344                 }
    3345                 if (this.isVisible()) {
    3346                     s.add(AccessibleState.VISIBLE);
    3347                 } else if (s.contains(AccessibleState.VISIBLE)) {
    3348                     s.remove(AccessibleState.VISIBLE);
    3349                 }
    3350                 s.add(AccessibleState.TRANSIENT); // cell-rendered
    3351                 return s;
    3352             }
    3353
    3354             public int getAccessibleIndexInParent() {
    3355                 return indexInParent;
    3356             }
    3357
    3358             public int getAccessibleChildrenCount() {
    3359                 AccessibleContext ac = getCurrentAccessibleContext();
    3360                 if (ac != null) {
    3361                     return ac.getAccessibleChildrenCount();
    3362                 } else {
    3363                     return 0;
    3364                 }
    3365             }
    3366
    3367             public Accessible getAccessibleChild(int i) {
    3368                 AccessibleContext ac = getCurrentAccessibleContext();
    3369                 if (ac != null) {
    3370                     Accessible accessibleChild = ac.getAccessibleChild(i);
    3371                     ac.setAccessibleParent(this);
    3372                     return accessibleChild;
    3373                 } else {
    3374                     return null;
    3375                 }
    3376             }
    3377
    3378             public Locale getLocale() {
    3379                 AccessibleContext ac = getCurrentAccessibleContext();
    3380                 if (ac != null) {
    3381                     return ac.getLocale();
    3382                 } else {
    3383                     return null;
    3384                 }
    3385             }
    3386
    3387             public void addPropertyChangeListener(PropertyChangeListener l) {
    3388                 AccessibleContext ac = getCurrentAccessibleContext();
    3389                 if (ac != null) {
    3390                     ac.addPropertyChangeListener(l);
    3391                 }
    3392             }
    3393
    3394             public void removePropertyChangeListener(PropertyChangeListener l) {
    3395                 AccessibleContext ac = getCurrentAccessibleContext();
    3396                 if (ac != null) {
    3397                     ac.removePropertyChangeListener(l);
    3398                 }
    3399             }
    3400
    3401            /**
    3402             * Get the AccessibleComponent associated with this object.  In the
    3403             * implementation of the Java Accessibility API for this class,
    3404             * return this object, which is responsible for implementing the
    3405             * AccessibleComponent interface on behalf of itself.
    3406             *
    3407             * @return this object
    3408             */
    3409             public AccessibleComponent getAccessibleComponent() {
    3410                 return this; // to override getBounds()
    3411             }
    3412
    3413             public AccessibleSelection getAccessibleSelection() {
    3414                 AccessibleContext ac = getCurrentAccessibleContext();
    3415                 return ac != null ? ac.getAccessibleSelection() : null;
    3416             }
    3417
    3418             public AccessibleText getAccessibleText() {
    3419                 AccessibleContext ac = getCurrentAccessibleContext();
    3420                 return ac != null ? ac.getAccessibleText() : null;
    3421             }
    3422
    3423             public AccessibleValue getAccessibleValue() {
    3424                 AccessibleContext ac = getCurrentAccessibleContext();
    3425                 return ac != null ? ac.getAccessibleValue() : null;
    3426             }
    3427
    3428
    3429             // AccessibleComponent methods
    3430
    3431             public Color getBackground() {
    3432                 AccessibleContext ac = getCurrentAccessibleContext();
    3433                 if (ac instanceof AccessibleComponent) {
    3434                     return ((AccessibleComponent) ac).getBackground();
    3435                 } else {
    3436                     Component c = getCurrentComponent();
    3437                     if (c != null) {
    3438                         return c.getBackground();
    3439                     } else {
    3440                         return null;
    3441                     }
    3442                 }
    3443             }
    3444
    3445             public void setBackground(Color c) {
    3446                 AccessibleContext ac = getCurrentAccessibleContext();
    3447                 if (ac instanceof AccessibleComponent) {
    3448                     ((AccessibleComponent) ac).setBackground(c);
    3449                 } else {
    3450                     Component cp = getCurrentComponent();
    3451                     if (cp != null) {
    3452                         cp.setBackground(c);
    3453                     }
    3454                 }
    3455             }
    3456
    3457             public Color getForeground() {
    3458                 AccessibleContext ac = getCurrentAccessibleContext();
    3459                 if (ac instanceof AccessibleComponent) {
    3460                     return ((AccessibleComponent) ac).getForeground();
    3461                 } else {
    3462                     Component c = getCurrentComponent();
    3463                     if (c != null) {
    3464                         return c.getForeground();
    3465                     } else {
    3466                         return null;
    3467                     }
    3468                 }
    3469             }
    3470
    3471             public void setForeground(Color c) {
    3472                 AccessibleContext ac = getCurrentAccessibleContext();
    3473                 if (ac instanceof AccessibleComponent) {
    3474                     ((AccessibleComponent) ac).setForeground(c);
    3475                 } else {
    3476                     Component cp = getCurrentComponent();
    3477                     if (cp != null) {
    3478                         cp.setForeground(c);
    3479                     }
    3480                 }
    3481             }
    3482
    3483             public Cursor getCursor() {
    3484                 AccessibleContext ac = getCurrentAccessibleContext();
    3485                 if (ac instanceof AccessibleComponent) {
    3486                     return ((AccessibleComponent) ac).getCursor();
    3487                 } else {
    3488                     Component c = getCurrentComponent();
    3489                     if (c != null) {
    3490                         return c.getCursor();
    3491                     } else {
    3492                         Accessible ap = getAccessibleParent();
    3493                         if (ap instanceof AccessibleComponent) {
    3494                             return ((AccessibleComponent) ap).getCursor();
    3495                         } else {
    3496                             return null;
    3497                         }
    3498                     }
    3499                 }
    3500             }
    3501
    3502             public void setCursor(Cursor c) {
    3503                 AccessibleContext ac = getCurrentAccessibleContext();
    3504                 if (ac instanceof AccessibleComponent) {
    3505                     ((AccessibleComponent) ac).setCursor(c);
    3506                 } else {
    3507                     Component cp = getCurrentComponent();
    3508                     if (cp != null) {
    3509                         cp.setCursor(c);
    3510                     }
    3511                 }
    3512             }
    3513
    3514             public Font getFont() {
    3515                 AccessibleContext ac = getCurrentAccessibleContext();
    3516                 if (ac instanceof AccessibleComponent) {
    3517                     return ((AccessibleComponent) ac).getFont();
    3518                 } else {
    3519                     Component c = getCurrentComponent();
    3520                     if (c != null) {
    3521                         return c.getFont();
    3522                     } else {
    3523                         return null;
    3524                     }
    3525                 }
    3526             }
    3527
    3528             public void setFont(Font f) {
    3529                 AccessibleContext ac = getCurrentAccessibleContext();
    3530                 if (ac instanceof AccessibleComponent) {
    3531                     ((AccessibleComponent) ac).setFont(f);
    3532                 } else {
    3533                     Component c = getCurrentComponent();
    3534                     if (c != null) {
    3535                         c.setFont(f);
    3536                     }
    3537                 }
    3538             }
    3539
    3540             public FontMetrics getFontMetrics(Font f) {
    3541                 AccessibleContext ac = getCurrentAccessibleContext();
    3542                 if (ac instanceof AccessibleComponent) {
    3543                     return ((AccessibleComponent) ac).getFontMetrics(f);
    3544                 } else {
    3545                     Component c = getCurrentComponent();
    3546                     if (c != null) {
    3547                         return c.getFontMetrics(f);
    3548                     } else {
    3549                         return null;
    3550                     }
    3551                 }
    3552             }
    3553
    3554             public boolean isEnabled() {
    3555                 AccessibleContext ac = getCurrentAccessibleContext();
    3556                 if (ac instanceof AccessibleComponent) {
    3557                     return ((AccessibleComponent) ac).isEnabled();
    3558                 } else {
    3559                     Component c = getCurrentComponent();
    3560                     if (c != null) {
    3561                         return c.isEnabled();
    3562                     } else {
    3563                         return false;
    3564                     }
    3565                 }
    3566             }
    3567
    3568             public void setEnabled(boolean b) {
    3569                 AccessibleContext ac = getCurrentAccessibleContext();
    3570                 if (ac instanceof AccessibleComponent) {
    3571                     ((AccessibleComponent) ac).setEnabled(b);
    3572                 } else {
    3573                     Component c = getCurrentComponent();
    3574                     if (c != null) {
    3575                         c.setEnabled(b);
    3576                     }
    3577                 }
    3578             }
    3579
    3580             public boolean isVisible() {
    3581                 int fi = parent.getFirstVisibleIndex();
    3582                 int li = parent.getLastVisibleIndex();
    3583                 // The UI incorrectly returns a -1 for the last
    3584                 // visible index if the list is smaller than the
    3585                 // viewport size.
    3586                 if (li == -1) {
    3587                     li = parent.getModel().getSize() - 1;
    3588                 }
    3589                 return ((indexInParent >= fi)
    3590                         && (indexInParent <= li));
    3591             }
    3592
    3593             public void setVisible(boolean b) {
    3594             }
    3595
    3596             public boolean isShowing() {
    3597                 return (parent.isShowing() && isVisible());
    3598             }
    3599
    3600             public boolean contains(Point p) {
    3601                 AccessibleContext ac = getCurrentAccessibleContext();
    3602                 if (ac instanceof AccessibleComponent) {
    3603                     Rectangle r = ((AccessibleComponent) ac).getBounds();
    3604                     return r.contains(p);
    3605                 } else {
    3606                     Component c = getCurrentComponent();
    3607                     if (c != null) {
    3608                         Rectangle r = c.getBounds();
    3609                         return r.contains(p);
    3610                     } else {
    3611                         return getBounds().contains(p);
    3612                     }
    3613                 }
    3614             }
    3615
    3616             public Point getLocationOnScreen() {
    3617                 if (parent != null) {
    3618                     Point listLocation;
    3619                     try {
    3620                         listLocation = parent.getLocationOnScreen();
    3621                     } catch (IllegalComponentStateException e) {
    3622                         // This can happen if the component isn't visisble
    3623                         return null;
    3624                     }
    3625                     Point componentLocation = parent.indexToLocation(indexInParent);
    3626                     if (componentLocation != null) {
    3627                         componentLocation.translate(listLocation.x, listLocation.y);
    3628                         return componentLocation;
    3629                     } else {
    3630                         return null;
    3631                     }
    3632                 } else {
    3633                     return null;
    3634                 }
    3635             }
    3636
    3637             public Point getLocation() {
    3638                 if (parent != null) {
    3639                     return parent.indexToLocation(indexInParent);
    3640                 } else {
    3641                     return null;
    3642                 }
    3643             }
    3644
    3645             public void setLocation(Point p) {
    3646                 if ((parent != null)  && (parent.contains(p))) {
    3647                     ensureIndexIsVisible(indexInParent);
    3648                 }
    3649             }
    3650
    3651             public Rectangle getBounds() {
    3652                 if (parent != null) {
    3653                     return parent.getCellBounds(indexInParent,indexInParent);
    3654                 } else {
    3655                     return null;
    3656                 }
    3657             }
    3658
    3659             public void setBounds(Rectangle r) {
    3660                 AccessibleContext ac = getCurrentAccessibleContext();
    3661                 if (ac instanceof AccessibleComponent) {
    3662                     ((AccessibleComponent) ac).setBounds(r);
    3663                 }
    3664             }
    3665
    3666             public Dimension getSize() {
    3667                 Rectangle cellBounds = this.getBounds();
    3668                 if (cellBounds != null) {
    3669                     return cellBounds.getSize();
    3670                 } else {
    3671                     return null;
    3672                 }
    3673             }
    3674
    3675             public void setSize (Dimension d) {
    3676                 AccessibleContext ac = getCurrentAccessibleContext();
    3677                 if (ac instanceof AccessibleComponent) {
    3678                     ((AccessibleComponent) ac).setSize(d);
    3679                 } else {
    3680                     Component c = getCurrentComponent();
    3681                     if (c != null) {
    3682                         c.setSize(d);
    3683                     }
    3684                 }
    3685             }
    3686
    3687             public Accessible getAccessibleAt(Point p) {
    3688                 AccessibleContext ac = getCurrentAccessibleContext();
    3689                 if (ac instanceof AccessibleComponent) {
    3690                     return ((AccessibleComponent) ac).getAccessibleAt(p);
    3691                 } else {
    3692                     return null;
    3693                 }
    3694             }
    3695
    3696             @SuppressWarnings("deprecation")
    3697             public boolean isFocusTraversable() {
    3698                 AccessibleContext ac = getCurrentAccessibleContext();
    3699                 if (ac instanceof AccessibleComponent) {
    3700                     return ((AccessibleComponent) ac).isFocusTraversable();
    3701                 } else {
    3702                     Component c = getCurrentComponent();
    3703                     if (c != null) {
    3704                         return c.isFocusTraversable();
    3705                     } else {
    3706                         return false;
    3707                     }
    3708                 }
    3709             }
    3710
    3711             public void requestFocus() {
    3712                 AccessibleContext ac = getCurrentAccessibleContext();
    3713                 if (ac instanceof AccessibleComponent) {
    3714                     ((AccessibleComponent) ac).requestFocus();
    3715                 } else {
    3716                     Component c = getCurrentComponent();
    3717                     if (c != null) {
    3718                         c.requestFocus();
    3719                     }
    3720                 }
    3721             }
    3722
    3723             public void addFocusListener(FocusListener l) {
    3724                 AccessibleContext ac = getCurrentAccessibleContext();
    3725                 if (ac instanceof AccessibleComponent) {
    3726                     ((AccessibleComponent) ac).addFocusListener(l);
    3727                 } else {
    3728                     Component c = getCurrentComponent();
    3729                     if (c != null) {
    3730                         c.addFocusListener(l);
    3731                     }
    3732                 }
    3733             }
    3734
    3735             public void removeFocusListener(FocusListener l) {
    3736                 AccessibleContext ac = getCurrentAccessibleContext();
    3737                 if (ac instanceof AccessibleComponent) {
    3738                     ((AccessibleComponent) ac).removeFocusListener(l);
    3739                 } else {
    3740                     Component c = getCurrentComponent();
    3741                     if (c != null) {
    3742                         c.removeFocusListener(l);
    3743                     }
    3744                 }
    3745             }
    3746
    3747             // TIGER - 4733624
    3748             /**
    3749              * Returns the icon for the element renderer, as the only item
    3750              * of an array of <code>AccessibleIcon</code>s or a <code>null</code> array
    3751              * if the renderer component contains no icons.
    3752              *
    3753              * @return an array containing the accessible icon
    3754              *         or a <code>null</code> array if none
    3755              * @since 1.3
    3756              */
    3757             public AccessibleIcon [] getAccessibleIcon() {
    3758                 AccessibleContext ac = getCurrentAccessibleContext();
    3759                 if (ac != null) {
    3760                     return ac.getAccessibleIcon();
    3761                 } else {
    3762                     return null;
    3763                 }
    3764             }
    3765
    3766             /**
    3767              * {@inheritDoc}
    3768              * @implSpec Returns the AccessibleAction for this AccessibleJListChild
    3769              * as follows:  First getListCellRendererComponent of the ListCellRenderer
    3770              * for the component at the "index in parent" of this child is called.
    3771              * Then its AccessibleContext is fetched and that AccessibleContext's
    3772              * AccessibleAction is returned.  Note that if an AccessibleAction
    3773              * is not found using this process then this object with its implementation
    3774              * of the AccessibleAction interface is returned.
    3775              * @since 9
    3776              */
    3777             @Override
    3778             public AccessibleAction getAccessibleAction() {
    3779                 AccessibleContext ac = getCurrentAccessibleContext();
    3780                 if (ac == null) {
    3781                     return null;
    3782                 } else {
    3783                     AccessibleAction aa = ac.getAccessibleAction();
    3784                     if (aa != null) {
    3785                         return aa;
    3786                     } else {
    3787                         return this;
    3788                     }
    3789                 }
    3790             }
    3791
    3792             /**
    3793              * {@inheritDoc}
    3794              * @implSpec If i == 0 selects this AccessibleJListChild by calling
    3795              * JList.this.setSelectedIndex(indexInParent) and then returns true;
    3796              * otherwise returns false.
    3797              * @since 9
    3798              */
    3799             @Override
    3800             public boolean doAccessibleAction(int i) {
    3801                 if (i == 0) {
    3802                     JList.this.setSelectedIndex(indexInParent);
    3803                     return true;
    3804                 } else {
    3805                     return false;
    3806                 }
    3807             }
    3808
    3809             /**
    3810              * {@inheritDoc}
    3811              * @implSpec If i == 0 returns the action description fetched from
    3812              * UIManager.getString("AbstractButton.clickText");
    3813              * otherwise returns null.
    3814              * @since 9
    3815              */
    3816             @Override
    3817             public String getAccessibleActionDescription(int i) {
    3818                 if (i == 0) {
    3819                     return UIManager.getString("AbstractButton.clickText");
    3820                 } else {
    3821                     return null;
    3822                 }
    3823             }
    3824
    3825             /**
    3826              * {@inheritDoc}
    3827              * @implSpec Returns 1, i.e. there is only one action.
    3828              * @since 9
    3829              */
    3830             @Override
    3831             public int getAccessibleActionCount() {
    3832                 return 1;
    3833             }
    3834
    3835         } // inner class AccessibleJListChild
    3836
    3837     } // inner class AccessibleJList
    3838 }
    --- EOF ---