0001 /*
0002 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.swing;
0027
0028 import java.awt.event.*;
0029 import java.awt.*;
0030
0031 import java.util.Vector;
0032 import java.util.Locale;
0033
0034 import java.beans.*;
0035
0036 import javax.swing.event.*;
0037 import javax.accessibility.*;
0038 import javax.swing.plaf.*;
0039 import javax.swing.text.Position;
0040
0041 import java.io.ObjectOutputStream;
0042 import java.io.ObjectInputStream;
0043 import java.io.IOException;
0044 import java.io.Serializable;
0045
0046 import sun.swing.SwingUtilities2;
0047 import sun.swing.SwingUtilities2.Section;
0048 import static sun.swing.SwingUtilities2.Section.*;
0049
0050 /**
0051 * A component that displays a list of objects and allows the user to select
0052 * one or more items. A separate model, {@code ListModel}, maintains the
0053 * contents of the list.
0054 * <p>
0055 * It's easy to display an array or Vector of objects, using the {@code JList}
0056 * constructor that automatically builds a read-only {@code ListModel} instance
0057 * for you:
0058 * <pre>
0059 * // Create a JList that displays strings from an array
0060 *
0061 * String[] data = {"one", "two", "three", "four"};
0062 * JList myList = new JList(data);
0063 *
0064 * // Create a JList that displays the superclasses of JList.class, by
0065 * // creating it with a Vector populated with this data
0066 *
0067 * Vector superClasses = new Vector();
0068 * Class rootClass = javax.swing.JList.class;
0069 * for(Class cls = rootClass; cls != null; cls = cls.getSuperclass()) {
0070 * superClasses.addElement(cls);
0071 * }
0072 * JList myList = new JList(superClasses);
0073 *
0074 * // The automatically created model is stored in JList's "model"
0075 * // property, which you can retrieve
0076 *
0077 * ListModel model = myList.getModel();
0078 * for(int i = 0; i < model.getSize(); i++) {
0079 * System.out.println(model.getElementAt(i));
0080 * }
0081 * </pre>
0082 * <p>
0083 * A {@code ListModel} can be supplied directly to a {@code JList} by way of a
0084 * constructor or the {@code setModel} method. The contents need not be static -
0085 * the number of items, and the values of items can change over time. A correct
0086 * {@code ListModel} implementation notifies the set of
0087 * {@code javax.swing.event.ListDataListener}s that have been added to it, each
0088 * time a change occurs. These changes are characterized by a
0089 * {@code javax.swing.event.ListDataEvent}, which identifies the range of list
0090 * indices that have been modified, added, or removed. {@code JList}'s
0091 * {@code ListUI} is responsible for keeping the visual representation up to
0092 * date with changes, by listening to the model.
0093 * <p>
0094 * Simple, dynamic-content, {@code JList} applications can use the
0095 * {@code DefaultListModel} class to maintain list elements. This class
0096 * implements the {@code ListModel} interface and also provides a
0097 * <code>java.util.Vector</code>-like API. Applications that need a more
0098 * custom <code>ListModel</code> implementation may instead wish to subclass
0099 * {@code AbstractListModel}, which provides basic support for managing and
0100 * notifying listeners. For example, a read-only implementation of
0101 * {@code AbstractListModel}:
0102 * <pre>
0103 * // This list model has about 2^16 elements. Enjoy scrolling.
0104 *
0105 * ListModel bigData = new AbstractListModel() {
0106 * public int getSize() { return Short.MAX_VALUE; }
0107 * public Object getElementAt(int index) { return "Index " + index; }
0108 * };
0109 * </pre>
0110 * <p>
0111 * The selection state of a {@code JList} is managed by another separate
0112 * model, an instance of {@code ListSelectionModel}. {@code JList} is
0113 * initialized with a selection model on construction, and also contains
0114 * methods to query or set this selection model. Additionally, {@code JList}
0115 * provides convenient methods for easily managing the selection. These methods,
0116 * such as {@code setSelectedIndex} and {@code getSelectedValue}, are cover
0117 * methods that take care of the details of interacting with the selection
0118 * model. By default, {@code JList}'s selection model is configured to allow any
0119 * combination of items to be selected at a time; selection mode
0120 * {@code MULTIPLE_INTERVAL_SELECTION}. The selection mode can be changed
0121 * on the selection model directly, or via {@code JList}'s cover method.
0122 * Responsibility for updating the selection model in response to user gestures
0123 * lies with the list's {@code ListUI}.
0124 * <p>
0125 * A correct {@code ListSelectionModel} implementation notifies the set of
0126 * {@code javax.swing.event.ListSelectionListener}s that have been added to it
0127 * each time a change to the selection occurs. These changes are characterized
0128 * by a {@code javax.swing.event.ListSelectionEvent}, which identifies the range
0129 * of the selection change.
0130 * <p>
0131 * The preferred way to listen for changes in list selection is to add
0132 * {@code ListSelectionListener}s directly to the {@code JList}. {@code JList}
0133 * then takes care of listening to the the selection model and notifying your
0134 * listeners of change.
0135 * <p>
0136 * Responsibility for listening to selection changes in order to keep the list's
0137 * visual representation up to date lies with the list's {@code ListUI}.
0138 * <p>
0139 * <a name="renderer">
0140 * Painting of cells in a {@code JList} is handled by a delegate called a
0141 * cell renderer, installed on the list as the {@code cellRenderer} property.
0142 * The renderer provides a {@code java.awt.Component} that is used
0143 * like a "rubber stamp" to paint the cells. Each time a cell needs to be
0144 * painted, the list's {@code ListUI} asks the cell renderer for the component,
0145 * moves it into place, and has it paint the contents of the cell by way of its
0146 * {@code paint} method. A default cell renderer, which uses a {@code JLabel}
0147 * component to render, is installed by the lists's {@code ListUI}. You can
0148 * substitute your own renderer using code like this:
0149 * <pre>
0150 * // Display an icon and a string for each object in the list.
0151 *
0152 * class MyCellRenderer extends JLabel implements ListCellRenderer {
0153 * final static ImageIcon longIcon = new ImageIcon("long.gif");
0154 * final static ImageIcon shortIcon = new ImageIcon("short.gif");
0155 *
0156 * // This is the only method defined by ListCellRenderer.
0157 * // We just reconfigure the JLabel each time we're called.
0158 *
0159 * public Component getListCellRendererComponent(
0160 * JList list, // the list
0161 * Object value, // value to display
0162 * int index, // cell index
0163 * boolean isSelected, // is the cell selected
0164 * boolean cellHasFocus) // does the cell have focus
0165 * {
0166 * String s = value.toString();
0167 * setText(s);
0168 * setIcon((s.length() > 10) ? longIcon : shortIcon);
0169 * if (isSelected) {
0170 * setBackground(list.getSelectionBackground());
0171 * setForeground(list.getSelectionForeground());
0172 * } else {
0173 * setBackground(list.getBackground());
0174 * setForeground(list.getForeground());
0175 * }
0176 * setEnabled(list.isEnabled());
0177 * setFont(list.getFont());
0178 * setOpaque(true);
0179 * return this;
0180 * }
0181 * }
0182 *
0183 * myList.setCellRenderer(new MyCellRenderer());
0184 * </pre>
0185 * <p>
0186 * Another job for the cell renderer is in helping to determine sizing
0187 * information for the list. By default, the list's {@code ListUI} determines
0188 * the size of cells by asking the cell renderer for its preferred
0189 * size for each list item. This can be expensive for large lists of items.
0190 * To avoid these calculations, you can set a {@code fixedCellWidth} and
0191 * {@code fixedCellHeight} on the list, or have these values calculated
0192 * automatically based on a single prototype value:
0193 * <a name="prototype_example">
0194 * <pre>
0195 * JList bigDataList = new JList(bigData);
0196 *
0197 * // We don't want the JList implementation to compute the width
0198 * // or height of all of the list cells, so we give it a string
0199 * // that's as big as we'll need for any cell. It uses this to
0200 * // compute values for the fixedCellWidth and fixedCellHeight
0201 * // properties.
0202 *
0203 * bigDataList.setPrototypeCellValue("Index 1234567890");
0204 * </pre>
0205 * <p>
0206 * {@code JList} doesn't implement scrolling directly. To create a list that
0207 * scrolls, make it the viewport view of a {@code JScrollPane}. For example:
0208 * <pre>
0209 * JScrollPane scrollPane = new JScrollPane(myList);
0210 *
0211 * // Or in two steps:
0212 * JScrollPane scrollPane = new JScrollPane();
0213 * scrollPane.getViewport().setView(myList);
0214 * </pre>
0215 * <p>
0216 * {@code JList} doesn't provide any special handling of double or triple
0217 * (or N) mouse clicks, but it's easy to add a {@code MouseListener} if you
0218 * wish to take action on these events. Use the {@code locationToIndex}
0219 * method to determine what cell was clicked. For example:
0220 * <pre>
0221 * MouseListener mouseListener = new MouseAdapter() {
0222 * public void mouseClicked(MouseEvent e) {
0223 * if (e.getClickCount() == 2) {
0224 * int index = list.locationToIndex(e.getPoint());
0225 * System.out.println("Double clicked on Item " + index);
0226 * }
0227 * }
0228 * };
0229 * list.addMouseListener(mouseListener);
0230 * </pre>
0231 * <p>
0232 * <strong>Warning:</strong> Swing is not thread safe. For more
0233 * information see <a
0234 * href="package-summary.html#threading">Swing's Threading
0235 * Policy</a>.
0236 * <p>
0237 * <strong>Warning:</strong>
0238 * Serialized objects of this class will not be compatible with
0239 * future Swing releases. The current serialization support is
0240 * appropriate for short term storage or RMI between applications running
0241 * the same version of Swing. As of 1.4, support for long term storage
0242 * of all JavaBeans<sup><font size="-2">TM</font></sup>
0243 * has been added to the <code>java.beans</code> package.
0244 * Please see {@link java.beans.XMLEncoder}.
0245 * <p>
0246 * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/list.html">How to Use Lists</a>
0247 * in <a href="http://java.sun.com/Series/Tutorial/index.html"><em>The Java Tutorial</em></a>
0248 * for further documentation.
0249 * Also see the article <a href="http://java.sun.com/products/jfc/tsc/tech_topics/jlist_1/jlist.html">Advanced JList Programming</a>
0250 * in <a href="http://java.sun.com/products/jfc/tsc"><em>The Swing Connection</em></a>.
0251 * <p>
0252 * @see ListModel
0253 * @see AbstractListModel
0254 * @see DefaultListModel
0255 * @see ListSelectionModel
0256 * @see DefaultListSelectionModel
0257 * @see ListCellRenderer
0258 * @see DefaultListCellRenderer
0259 *
0260 * @beaninfo
0261 * attribute: isContainer false
0262 * description: A component which allows for the selection of one or more objects from a list.
0263 *
0264 * @version 1.142 05/05/07
0265 * @author Hans Muller
0266 */
0267 public class JList extends JComponent implements Scrollable, Accessible {
0268 /**
0269 * @see #getUIClassID
0270 * @see #readObject
0271 */
0272 private static final String uiClassID = "ListUI";
0273
0274 /**
0275 * Indicates a vertical layout of cells, in a single column;
0276 * the default layout.
0277 * @see #setLayoutOrientation
0278 * @since 1.4
0279 */
0280 public static final int VERTICAL = 0;
0281
0282 /**
0283 * Indicates a "newspaper style" layout with cells flowing vertically
0284 * then horizontally.
0285 * @see #setLayoutOrientation
0286 * @since 1.4
0287 */
0288 public static final int VERTICAL_WRAP = 1;
0289
0290 /**
0291 * Indicates a "newspaper style" layout with cells flowing horizontally
0292 * then vertically.
0293 * @see #setLayoutOrientation
0294 * @since 1.4
0295 */
0296 public static final int HORIZONTAL_WRAP = 2;
0297
0298 private int fixedCellWidth = -1;
0299 private int fixedCellHeight = -1;
0300 private int horizontalScrollIncrement = -1;
0301 private Object prototypeCellValue;
0302 private int visibleRowCount = 8;
0303 private Color selectionForeground;
0304 private Color selectionBackground;
0305 private boolean dragEnabled;
0306
0307 private ListSelectionModel selectionModel;
0308 private ListModel dataModel;
0309 private ListCellRenderer cellRenderer;
0310 private ListSelectionListener selectionListener;
0311
0312 /**
0313 * How to lay out the cells; defaults to <code>VERTICAL</code>.
0314 */
0315 private int layoutOrientation;
0316
0317 /**
0318 * The drop mode for this component.
0319 */
0320 private DropMode dropMode = DropMode.USE_SELECTION;
0321
0322 /**
0323 * The drop location.
0324 */
0325 private transient DropLocation dropLocation;
0326
0327 /**
0328 * A subclass of <code>TransferHandler.DropLocation</code> representing
0329 * a drop location for a <code>JList</code>.
0330 *
0331 * @see #getDropLocation
0332 * @since 1.6
0333 */
0334 public static final class DropLocation extends
0335 TransferHandler.DropLocation {
0336 private final int index;
0337 private final boolean isInsert;
0338
0339 private DropLocation(Point p, int index, boolean isInsert) {
0340 super (p);
0341 this .index = index;
0342 this .isInsert = isInsert;
0343 }
0344
0345 /**
0346 * Returns the index where dropped data should be placed in the
0347 * list. Interpretation of the value depends on the drop mode set on
0348 * the associated component. If the drop mode is either
0349 * <code>DropMode.USE_SELECTION</code> or <code>DropMode.ON</code>,
0350 * the return value is an index of a row in the list. If the drop mode is
0351 * <code>DropMode.INSERT</code>, the return value refers to the index
0352 * where the data should be inserted. If the drop mode is
0353 * <code>DropMode.ON_OR_INSERT</code>, the value of
0354 * <code>isInsert()</code> indicates whether the index is an index
0355 * of a row, or an insert index.
0356 * <p>
0357 * <code>-1</code> indicates that the drop occurred over empty space,
0358 * and no index could be calculated.
0359 *
0360 * @return the drop index
0361 */
0362 public int getIndex() {
0363 return index;
0364 }
0365
0366 /**
0367 * Returns whether or not this location represents an insert
0368 * location.
0369 *
0370 * @return whether or not this is an insert location
0371 */
0372 public boolean isInsert() {
0373 return isInsert;
0374 }
0375
0376 /**
0377 * Returns a string representation of this drop location.
0378 * This method is intended to be used for debugging purposes,
0379 * and the content and format of the returned string may vary
0380 * between implementations.
0381 *
0382 * @return a string representation of this drop location
0383 */
0384 public String toString() {
0385 return getClass().getName() + "[dropPoint="
0386 + getDropPoint() + "," + "index=" + index + ","
0387 + "insert=" + isInsert + "]";
0388 }
0389 }
0390
0391 /**
0392 * Constructs a {@code JList} that displays elements from the specified,
0393 * {@code non-null}, model. All {@code JList} constructors delegate to
0394 * this one.
0395 * <p>
0396 * This constructor registers the list with the {@code ToolTipManager},
0397 * allowing for tooltips to be provided by the cell renderers.
0398 *
0399 * @param dataModel the model for the list
0400 * @exception IllegalArgumentException if the model is {@code null}
0401 */
0402 public JList(ListModel dataModel) {
0403 if (dataModel == null) {
0404 throw new IllegalArgumentException(
0405 "dataModel must be non null");
0406 }
0407
0408 // Register with the ToolTipManager so that tooltips from the
0409 // renderer show through.
0410 ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
0411 toolTipManager.registerComponent(this );
0412
0413 layoutOrientation = VERTICAL;
0414
0415 this .dataModel = dataModel;
0416 selectionModel = createSelectionModel();
0417 setAutoscrolls(true);
0418 setOpaque(true);
0419 updateUI();
0420 }
0421
0422 /**
0423 * Constructs a <code>JList</code> that displays the elements in
0424 * the specified array. This constructor creates a read-only model
0425 * for the given array, and then delegates to the constructor that
0426 * takes a {@code ListModel}.
0427 * <p>
0428 * Attempts to pass a {@code null} value to this method results in
0429 * undefined behavior and, most likely, exceptions. The created model
0430 * references the given array directly. Attempts to modify the array
0431 * after constructing the list results in undefined behavior.
0432 *
0433 * @param listData the array of Objects to be loaded into the data model,
0434 * {@code non-null}
0435 */
0436 public JList(final Object[] listData) {
0437 this (new AbstractListModel() {
0438 public int getSize() {
0439 return listData.length;
0440 }
0441
0442 public Object getElementAt(int i) {
0443 return listData[i];
0444 }
0445 });
0446 }
0447
0448 /**
0449 * Constructs a <code>JList</code> that displays the elements in
0450 * the specified <code>Vector</code>. This constructor creates a read-only
0451 * model for the given {@code Vector}, and then delegates to the constructor
0452 * that takes a {@code ListModel}.
0453 * <p>
0454 * Attempts to pass a {@code null} value to this method results in
0455 * undefined behavior and, most likely, exceptions. The created model
0456 * references the given {@code Vector} directly. Attempts to modify the
0457 * {@code Vector} after constructing the list results in undefined behavior.
0458 *
0459 * @param listData the <code>Vector</code> to be loaded into the
0460 * data model, {@code non-null}
0461 */
0462 public JList(final Vector<?> listData) {
0463 this (new AbstractListModel() {
0464 public int getSize() {
0465 return listData.size();
0466 }
0467
0468 public Object getElementAt(int i) {
0469 return listData.elementAt(i);
0470 }
0471 });
0472 }
0473
0474 /**
0475 * Constructs a <code>JList</code> with an empty, read-only, model.
0476 */
0477 public JList() {
0478 this (new AbstractListModel() {
0479 public int getSize() {
0480 return 0;
0481 }
0482
0483 public Object getElementAt(int i) {
0484 return "No Data Model";
0485 }
0486 });
0487 }
0488
0489 /**
0490 * Returns the {@code ListUI}, the look and feel object that
0491 * renders this component.
0492 *
0493 * @return the <code>ListUI</code> object that renders this component
0494 */
0495 public ListUI getUI() {
0496 return (ListUI) ui;
0497 }
0498
0499 /**
0500 * Sets the {@code ListUI}, the look and feel object that
0501 * renders this component.
0502 *
0503 * @param ui the <code>ListUI</code> object
0504 * @see UIDefaults#getUI
0505 * @beaninfo
0506 * bound: true
0507 * hidden: true
0508 * attribute: visualUpdate true
0509 * description: The UI object that implements the Component's LookAndFeel.
0510 */
0511 public void setUI(ListUI ui) {
0512 super .setUI(ui);
0513 }
0514
0515 /**
0516 * Resets the {@code ListUI} property by setting it to the value provided
0517 * by the current look and feel. If the current cell renderer was installed
0518 * by the developer (rather than the look and feel itself), this also causes
0519 * the cell renderer and its children to be updated, by calling
0520 * {@code SwingUtilities.updateComponentTreeUI} on it.
0521 *
0522 * @see UIManager#getUI
0523 * @see SwingUtilities#updateComponentTreeUI
0524 */
0525 public void updateUI() {
0526 setUI((ListUI) UIManager.getUI(this ));
0527
0528 ListCellRenderer renderer = getCellRenderer();
0529 if (renderer instanceof Component) {
0530 SwingUtilities.updateComponentTreeUI((Component) renderer);
0531 }
0532 }
0533
0534 /**
0535 * Returns {@code "ListUI"}, the <code>UIDefaults</code> key used to look
0536 * up the name of the {@code javax.swing.plaf.ListUI} class that defines
0537 * the look and feel for this component.
0538 *
0539 * @return the string "ListUI"
0540 * @see JComponent#getUIClassID
0541 * @see UIDefaults#getUI
0542 */
0543 public String getUIClassID() {
0544 return uiClassID;
0545 }
0546
0547 /* -----private-----
0548 * This method is called by setPrototypeCellValue and setCellRenderer
0549 * to update the fixedCellWidth and fixedCellHeight properties from the
0550 * current value of prototypeCellValue (if it's non null).
0551 * <p>
0552 * This method sets fixedCellWidth and fixedCellHeight but does <b>not</b>
0553 * generate PropertyChangeEvents for them.
0554 *
0555 * @see #setPrototypeCellValue
0556 * @see #setCellRenderer
0557 */
0558 private void updateFixedCellSize() {
0559 ListCellRenderer cr = getCellRenderer();
0560 Object value = getPrototypeCellValue();
0561
0562 if ((cr != null) && (value != null)) {
0563 Component c = cr.getListCellRendererComponent(this , value,
0564 0, false, false);
0565
0566 /* The ListUI implementation will add Component c to its private
0567 * CellRendererPane however we can't assume that's already
0568 * been done here. So we temporarily set the one "inherited"
0569 * property that may affect the renderer components preferred size:
0570 * its font.
0571 */
0572 Font f = c.getFont();
0573 c.setFont(getFont());
0574
0575 Dimension d = c.getPreferredSize();
0576 fixedCellWidth = d.width;
0577 fixedCellHeight = d.height;
0578
0579 c.setFont(f);
0580 }
0581 }
0582
0583 /**
0584 * Returns the "prototypical" cell value -- a value used to calculate a
0585 * fixed width and height for cells. This can be {@code null} if there
0586 * is no such value.
0587 *
0588 * @return the value of the {@code prototypeCellValue} property
0589 * @see #setPrototypeCellValue
0590 */
0591 public Object getPrototypeCellValue() {
0592 return prototypeCellValue;
0593 }
0594
0595 /**
0596 * Sets the {@code prototypeCellValue} property, and then (if the new value
0597 * is {@code non-null}), computes the {@code fixedCellWidth} and
0598 * {@code fixedCellHeight} properties by requesting the cell renderer
0599 * component for the given value (and index 0) from the cell renderer, and
0600 * using that component's preferred size.
0601 * <p>
0602 * This method is useful when the list is too long to allow the
0603 * {@code ListUI} to compute the width/height of each cell, and there is a
0604 * single cell value that is known to occupy as much space as any of the
0605 * others, a so-called prototype.
0606 * <p>
0607 * While all three of the {@code prototypeCellValue},
0608 * {@code fixedCellHeight}, and {@code fixedCellWidth} properties may be
0609 * modified by this method, {@code PropertyChangeEvent} notifications are
0610 * only sent when the {@code prototypeCellValue} property changes.
0611 * <p>
0612 * To see an example which sets this property, see the
0613 * <a href="#prototype_example">class description</a> above.
0614 * <p>
0615 * The default value of this property is <code>null</code>.
0616 * <p>
0617 * This is a JavaBeans bound property.
0618 *
0619 * @param prototypeCellValue the value on which to base
0620 * <code>fixedCellWidth</code> and
0621 * <code>fixedCellHeight</code>
0622 * @see #getPrototypeCellValue
0623 * @see #setFixedCellWidth
0624 * @see #setFixedCellHeight
0625 * @see JComponent#addPropertyChangeListener
0626 * @beaninfo
0627 * bound: true
0628 * attribute: visualUpdate true
0629 * description: The cell prototype value, used to compute cell width and height.
0630 */
0631 public void setPrototypeCellValue(Object prototypeCellValue) {
0632 Object oldValue = this .prototypeCellValue;
0633 this .prototypeCellValue = prototypeCellValue;
0634
0635 /* If the prototypeCellValue has changed and is non-null,
0636 * then recompute fixedCellWidth and fixedCellHeight.
0637 */
0638
0639 if ((prototypeCellValue != null)
0640 && !prototypeCellValue.equals(oldValue)) {
0641 updateFixedCellSize();
0642 }
0643
0644 firePropertyChange("prototypeCellValue", oldValue,
0645 prototypeCellValue);
0646 }
0647
0648 /**
0649 * Returns the value of the {@code fixedCellWidth} property.
0650 *
0651 * @return the fixed cell width
0652 * @see #setFixedCellWidth
0653 */
0654 public int getFixedCellWidth() {
0655 return fixedCellWidth;
0656 }
0657
0658 /**
0659 * Sets a fixed value to be used for the width of every cell in the list.
0660 * If {@code width} is -1, cell widths are computed in the {@code ListUI}
0661 * by applying <code>getPreferredSize</code> to the cell renderer component
0662 * for each list element.
0663 * <p>
0664 * The default value of this property is {@code -1}.
0665 * <p>
0666 * This is a JavaBeans bound property.
0667 *
0668 * @param width the width to be used for all cells in the list
0669 * @see #setPrototypeCellValue
0670 * @see #setFixedCellWidth
0671 * @see JComponent#addPropertyChangeListener
0672 * @beaninfo
0673 * bound: true
0674 * attribute: visualUpdate true
0675 * description: Defines a fixed cell width when greater than zero.
0676 */
0677 public void setFixedCellWidth(int width) {
0678 int oldValue = fixedCellWidth;
0679 fixedCellWidth = width;
0680 firePropertyChange("fixedCellWidth", oldValue, fixedCellWidth);
0681 }
0682
0683 /**
0684 * Returns the value of the {@code fixedCellHeight} property.
0685 *
0686 * @return the fixed cell height
0687 * @see #setFixedCellHeight
0688 */
0689 public int getFixedCellHeight() {
0690 return fixedCellHeight;
0691 }
0692
0693 /**
0694 * Sets a fixed value to be used for the height of every cell in the list.
0695 * If {@code height} is -1, cell heights are computed in the {@code ListUI}
0696 * by applying <code>getPreferredSize</code> to the cell renderer component
0697 * for each list element.
0698 * <p>
0699 * The default value of this property is {@code -1}.
0700 * <p>
0701 * This is a JavaBeans bound property.
0702 *
0703 * @param height the height to be used for for all cells in the list
0704 * @see #setPrototypeCellValue
0705 * @see #setFixedCellWidth
0706 * @see JComponent#addPropertyChangeListener
0707 * @beaninfo
0708 * bound: true
0709 * attribute: visualUpdate true
0710 * description: Defines a fixed cell height when greater than zero.
0711 */
0712 public void setFixedCellHeight(int height) {
0713 int oldValue = fixedCellHeight;
0714 fixedCellHeight = height;
0715 firePropertyChange("fixedCellHeight", oldValue, fixedCellHeight);
0716 }
0717
0718 /**
0719 * Returns the object responsible for painting list items.
0720 *
0721 * @return the value of the {@code cellRenderer} property
0722 * @see #setCellRenderer
0723 */
0724 public ListCellRenderer getCellRenderer() {
0725 return cellRenderer;
0726 }
0727
0728 /**
0729 * Sets the delegate that is used to paint each cell in the list.
0730 * The job of a cell renderer is discussed in detail in the
0731 * <a href="#renderer">class level documentation</a>.
0732 * <p>
0733 * If the {@code prototypeCellValue} property is {@code non-null},
0734 * setting the cell renderer also causes the {@code fixedCellWidth} and
0735 * {@code fixedCellHeight} properties to be re-calculated. Only one
0736 * <code>PropertyChangeEvent</code> is generated however -
0737 * for the <code>cellRenderer</code> property.
0738 * <p>
0739 * The default value of this property is provided by the {@code ListUI}
0740 * delegate, i.e. by the look and feel implementation.
0741 * <p>
0742 * This is a JavaBeans bound property.
0743 *
0744 * @param cellRenderer the <code>ListCellRenderer</code>
0745 * that paints list cells
0746 * @see #getCellRenderer
0747 * @beaninfo
0748 * bound: true
0749 * attribute: visualUpdate true
0750 * description: The component used to draw the cells.
0751 */
0752 public void setCellRenderer(ListCellRenderer cellRenderer) {
0753 ListCellRenderer oldValue = this .cellRenderer;
0754 this .cellRenderer = cellRenderer;
0755
0756 /* If the cellRenderer has changed and prototypeCellValue
0757 * was set, then recompute fixedCellWidth and fixedCellHeight.
0758 */
0759 if ((cellRenderer != null) && !cellRenderer.equals(oldValue)) {
0760 updateFixedCellSize();
0761 }
0762
0763 firePropertyChange("cellRenderer", oldValue, cellRenderer);
0764 }
0765
0766 /**
0767 * Returns the color used to draw the foreground of selected items.
0768 * {@code DefaultListCellRenderer} uses this color to draw the foreground
0769 * of items in the selected state, as do the renderers installed by most
0770 * {@code ListUI} implementations.
0771 *
0772 * @return the color to draw the foreground of selected items
0773 * @see #setSelectionForeground
0774 * @see DefaultListCellRenderer
0775 */
0776 public Color getSelectionForeground() {
0777 return selectionForeground;
0778 }
0779
0780 /**
0781 * Sets the color used to draw the foreground of selected items, which
0782 * cell renderers can use to render text and graphics.
0783 * {@code DefaultListCellRenderer} uses this color to draw the foreground
0784 * of items in the selected state, as do the renderers installed by most
0785 * {@code ListUI} implementations.
0786 * <p>
0787 * The default value of this property is defined by the look and feel
0788 * implementation.
0789 * <p>
0790 * This is a JavaBeans bound property.
0791 *
0792 * @param selectionForeground the {@code Color} to use in the foreground
0793 * for selected list items
0794 * @see #getSelectionForeground
0795 * @see #setSelectionBackground
0796 * @see #setForeground
0797 * @see #setBackground
0798 * @see #setFont
0799 * @see DefaultListCellRenderer
0800 * @beaninfo
0801 * bound: true
0802 * attribute: visualUpdate true
0803 * description: The foreground color of selected cells.
0804 */
0805 public void setSelectionForeground(Color selectionForeground) {
0806 Color oldValue = this .selectionForeground;
0807 this .selectionForeground = selectionForeground;
0808 firePropertyChange("selectionForeground", oldValue,
0809 selectionForeground);
0810 }
0811
0812 /**
0813 * Returns the color used to draw the background of selected items.
0814 * {@code DefaultListCellRenderer} uses this color to draw the background
0815 * of items in the selected state, as do the renderers installed by most
0816 * {@code ListUI} implementations.
0817 *
0818 * @return the color to draw the background of selected items
0819 * @see #setSelectionBackground
0820 * @see DefaultListCellRenderer
0821 */
0822 public Color getSelectionBackground() {
0823 return selectionBackground;
0824 }
0825
0826 /**
0827 * Sets the color used to draw the background of selected items, which
0828 * cell renderers can use fill selected cells.
0829 * {@code DefaultListCellRenderer} uses this color to fill the background
0830 * of items in the selected state, as do the renderers installed by most
0831 * {@code ListUI} implementations.
0832 * <p>
0833 * The default value of this property is defined by the look
0834 * and feel implementation.
0835 * <p>
0836 * This is a JavaBeans bound property.
0837 *
0838 * @param selectionBackground the {@code Color} to use for the
0839 * background of selected cells
0840 * @see #getSelectionBackground
0841 * @see #setSelectionForeground
0842 * @see #setForeground
0843 * @see #setBackground
0844 * @see #setFont
0845 * @see DefaultListCellRenderer
0846 * @beaninfo
0847 * bound: true
0848 * attribute: visualUpdate true
0849 * description: The background color of selected cells.
0850 */
0851 public void setSelectionBackground(Color selectionBackground) {
0852 Color oldValue = this .selectionBackground;
0853 this .selectionBackground = selectionBackground;
0854 firePropertyChange("selectionBackground", oldValue,
0855 selectionBackground);
0856 }
0857
0858 /**
0859 * Returns the value of the {@code visibleRowCount} property. See the
0860 * documentation for {@link #setVisibleRowCount} for details on how to
0861 * interpret this value.
0862 *
0863 * @return the value of the {@code visibleRowCount} property.
0864 * @see #setVisibleRowCount
0865 */
0866 public int getVisibleRowCount() {
0867 return visibleRowCount;
0868 }
0869
0870 /**
0871 * Sets the {@code visibleRowCount} property, which has different meanings
0872 * depending on the layout orientation: For a {@code VERTICAL} layout
0873 * orientation, this sets the preferred number of rows to display without
0874 * requiring scrolling; for other orientations, it affects the wrapping of
0875 * cells.
0876 * <p>
0877 * In {@code VERTICAL} orientation:<br>
0878 * Setting this property affects the return value of the
0879 * {@link #getPreferredScrollableViewportSize} method, which is used to
0880 * calculate the preferred size of an enclosing viewport. See that method's
0881 * documentation for more details.
0882 * <p>
0883 * In {@code HORIZONTAL_WRAP} and {@code VERTICAL_WRAP} orientations:<br>
0884 * This affects how cells are wrapped. See the documentation of
0885 * {@link #setLayoutOrientation} for more details.
0886 * <p>
0887 * The default value of this property is {@code 8}.
0888 * <p>
0889 * Calling this method with a negative value results in the property
0890 * being set to {@code 0}.
0891 * <p>
0892 * This is a JavaBeans bound property.
0893 *
0894 * @param visibleRowCount an integer specifying the preferred number of
0895 * rows to display without requiring scrolling
0896 * @see #getVisibleRowCount
0897 * @see #getPreferredScrollableViewportSize
0898 * @see #setLayoutOrientation
0899 * @see JComponent#getVisibleRect
0900 * @see JViewport
0901 * @beaninfo
0902 * bound: true
0903 * attribute: visualUpdate true
0904 * description: The preferred number of rows to display without
0905 * requiring scrolling
0906 */
0907 public void setVisibleRowCount(int visibleRowCount) {
0908 int oldValue = this .visibleRowCount;
0909 this .visibleRowCount = Math.max(0, visibleRowCount);
0910 firePropertyChange("visibleRowCount", oldValue, visibleRowCount);
0911 }
0912
0913 /**
0914 * Returns the layout orientation property for the list: {@code VERTICAL}
0915 * if the layout is a single column of cells, {@code VERTICAL_WRAP} if the
0916 * layout is "newspaper style" with the content flowing vertically then
0917 * horizontally, or {@code HORIZONTAL_WRAP} if the layout is "newspaper
0918 * style" with the content flowing horizontally then vertically.
0919 *
0920 * @return the value of the {@code layoutOrientation} property
0921 * @see #setLayoutOrientation
0922 * @since 1.4
0923 */
0924 public int getLayoutOrientation() {
0925 return layoutOrientation;
0926 }
0927
0928 /**
0929 * Defines the way list cells are layed out. Consider a {@code JList}
0930 * with five cells. Cells can be layed out in one of the following ways:
0931 * <p>
0932 * <pre>
0933 * VERTICAL: 0
0934 * 1
0935 * 2
0936 * 3
0937 * 4
0938 *
0939 * HORIZONTAL_WRAP: 0 1 2
0940 * 3 4
0941 *
0942 * VERTICAL_WRAP: 0 3
0943 * 1 4
0944 * 2
0945 * </pre>
0946 * <p>
0947 * A description of these layouts follows:
0948 *
0949 * <table border="1"
0950 * summary="Describes layouts VERTICAL, HORIZONTAL_WRAP, and VERTICAL_WRAP">
0951 * <tr><th><p align="left">Value</p></th><th><p align="left">Description</p></th></tr>
0952 * <tr><td><code>VERTICAL</code>
0953 * <td>Cells are layed out vertically in a single column.
0954 * <tr><td><code>HORIZONTAL_WRAP</code>
0955 * <td>Cells are layed out horizontally, wrapping to a new row as
0956 * necessary. If the {@code visibleRowCount} property is less than
0957 * or equal to zero, wrapping is determined by the width of the
0958 * list; otherwise wrapping is done in such a way as to ensure
0959 * {@code visibleRowCount} rows in the list.
0960 * <tr><td><code>VERTICAL_WRAP</code>
0961 * <td>Cells are layed out vertically, wrapping to a new column as
0962 * necessary. If the {@code visibleRowCount} property is less than
0963 * or equal to zero, wrapping is determined by the height of the
0964 * list; otherwise wrapping is done at {@code visibleRowCount} rows.
0965 * </table>
0966 * <p>
0967 * The default value of this property is <code>VERTICAL</code>.
0968 *
0969 * @param layoutOrientation the new layout orientation, one of:
0970 * {@code VERTICAL}, {@code HORIZONTAL_WRAP} or {@code VERTICAL_WRAP}
0971 * @see #getLayoutOrientation
0972 * @see #setVisibleRowCount
0973 * @see #getScrollableTracksViewportHeight
0974 * @see #getScrollableTracksViewportWidth
0975 * @throws IllegalArgumentException if {@code layoutOrientation} isn't one of the
0976 * allowable values
0977 * @since 1.4
0978 * @beaninfo
0979 * bound: true
0980 * attribute: visualUpdate true
0981 * description: Defines the way list cells are layed out.
0982 * enum: VERTICAL JList.VERTICAL
0983 * HORIZONTAL_WRAP JList.HORIZONTAL_WRAP
0984 * VERTICAL_WRAP JList.VERTICAL_WRAP
0985 */
0986 public void setLayoutOrientation(int layoutOrientation) {
0987 int oldValue = this .layoutOrientation;
0988 switch (layoutOrientation) {
0989 case VERTICAL:
0990 case VERTICAL_WRAP:
0991 case HORIZONTAL_WRAP:
0992 this .layoutOrientation = layoutOrientation;
0993 firePropertyChange("layoutOrientation", oldValue,
0994 layoutOrientation);
0995 break;
0996 default:
0997 throw new IllegalArgumentException(
0998 "layoutOrientation must be one of: VERTICAL, HORIZONTAL_WRAP or VERTICAL_WRAP");
0999 }
1000 }
1001
1002 /**
1003 * Returns the smallest list index that is currently visible.
1004 * In a left-to-right {@code componentOrientation}, the first visible
1005 * cell is found closest to the list's upper-left corner. In right-to-left
1006 * orientation, it is found closest to the upper-right corner.
1007 * If nothing is visible or the list is empty, {@code -1} is returned.
1008 * Note that the returned cell may only be partially visible.
1009 *
1010 * @return the index of the first visible cell
1011 * @see #getLastVisibleIndex
1012 * @see JComponent#getVisibleRect
1013 */
1014 public int getFirstVisibleIndex() {
1015 Rectangle r = getVisibleRect();
1016 int first;
1017 if (this .getComponentOrientation().isLeftToRight()) {
1018 first = locationToIndex(r.getLocation());
1019 } else {
1020 first = locationToIndex(new Point((r.x + r.width) - 1, r.y));
1021 }
1022 if (first != -1) {
1023 Rectangle bounds = getCellBounds(first, first);
1024 if (bounds != null) {
1025 SwingUtilities.computeIntersection(r.x, r.y, r.width,
1026 r.height, bounds);
1027 if (bounds.width == 0 || bounds.height == 0) {
1028 first = -1;
1029 }
1030 }
1031 }
1032 return first;
1033 }
1034
1035 /**
1036 * Returns the largest list index that is currently visible.
1037 * If nothing is visible or the list is empty, {@code -1} is returned.
1038 * Note that the returned cell may only be partially visible.
1039 *
1040 * @return the index of the last visible cell
1041 * @see #getFirstVisibleIndex
1042 * @see JComponent#getVisibleRect
1043 */
1044 public int getLastVisibleIndex() {
1045 boolean leftToRight = this .getComponentOrientation()
1046 .isLeftToRight();
1047 Rectangle r = getVisibleRect();
1048 Point lastPoint;
1049 if (leftToRight) {
1050 lastPoint = new Point((r.x + r.width) - 1,
1051 (r.y + r.height) - 1);
1052 } else {
1053 lastPoint = new Point(r.x, (r.y + r.height) - 1);
1054 }
1055 int location = locationToIndex(lastPoint);
1056
1057 if (location != -1) {
1058 Rectangle bounds = getCellBounds(location, location);
1059
1060 if (bounds != null) {
1061 SwingUtilities.computeIntersection(r.x, r.y, r.width,
1062 r.height, bounds);
1063 if (bounds.width == 0 || bounds.height == 0) {
1064 // Try the top left(LTR) or top right(RTL) corner, and
1065 // then go across checking each cell for HORIZONTAL_WRAP.
1066 // Try the lower left corner, and then go across checking
1067 // each cell for other list layout orientation.
1068 boolean isHorizontalWrap = (getLayoutOrientation() == HORIZONTAL_WRAP);
1069 Point visibleLocation = isHorizontalWrap ? new Point(
1070 lastPoint.x, r.y)
1071 : new Point(r.x, lastPoint.y);
1072 int last;
1073 int visIndex = -1;
1074 int lIndex = location;
1075 location = -1;
1076
1077 do {
1078 last = visIndex;
1079 visIndex = locationToIndex(visibleLocation);
1080
1081 if (visIndex != -1) {
1082 bounds = getCellBounds(visIndex, visIndex);
1083 if (visIndex != lIndex && bounds != null
1084 && bounds.contains(visibleLocation)) {
1085 location = visIndex;
1086 if (isHorizontalWrap) {
1087 visibleLocation.y = bounds.y
1088 + bounds.height;
1089 if (visibleLocation.y >= lastPoint.y) {
1090 // Past visible region, bail.
1091 last = visIndex;
1092 }
1093 } else {
1094 visibleLocation.x = bounds.x
1095 + bounds.width;
1096 if (visibleLocation.x >= lastPoint.x) {
1097 // Past visible region, bail.
1098 last = visIndex;
1099 }
1100 }
1101
1102 } else {
1103 last = visIndex;
1104 }
1105 }
1106 } while (visIndex != -1 && last != visIndex);
1107 }
1108 }
1109 }
1110 return location;
1111 }
1112
1113 /**
1114 * Scrolls the list within an enclosing viewport to make the specified
1115 * cell completely visible. This calls {@code scrollRectToVisible} with
1116 * the bounds of the specified cell. For this method to work, the
1117 * {@code JList} must be within a <code>JViewport</code>.
1118 * <p>
1119 * If the given index is outside the list's range of cells, this method
1120 * results in nothing.
1121 *
1122 * @param index the index of the cell to make visible
1123 * @see JComponent#scrollRectToVisible
1124 * @see #getVisibleRect
1125 */
1126 public void ensureIndexIsVisible(int index) {
1127 Rectangle cellBounds = getCellBounds(index, index);
1128 if (cellBounds != null) {
1129 scrollRectToVisible(cellBounds);
1130 }
1131 }
1132
1133 /**
1134 * Turns on or off automatic drag handling. In order to enable automatic
1135 * drag handling, this property should be set to {@code true}, and the
1136 * list's {@code TransferHandler} needs to be {@code non-null}.
1137 * The default value of the {@code dragEnabled} property is {@code false}.
1138 * <p>
1139 * The job of honoring this property, and recognizing a user drag gesture,
1140 * lies with the look and feel implementation, and in particular, the list's
1141 * {@code ListUI}. When automatic drag handling is enabled, most look and
1142 * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1143 * drag and drop operation whenever the user presses the mouse button over
1144 * an item and then moves the mouse a few pixels. Setting this property to
1145 * {@code true} can therefore have a subtle effect on how selections behave.
1146 * <p>
1147 * If a look and feel is used that ignores this property, you can still
1148 * begin a drag and drop operation by calling {@code exportAsDrag} on the
1149 * list's {@code TransferHandler}.
1150 *
1151 * @param b whether or not to enable automatic drag handling
1152 * @exception HeadlessException if
1153 * <code>b</code> is <code>true</code> and
1154 * <code>GraphicsEnvironment.isHeadless()</code>
1155 * returns <code>true</code>
1156 * @see java.awt.GraphicsEnvironment#isHeadless
1157 * @see #getDragEnabled
1158 * @see #setTransferHandler
1159 * @see TransferHandler
1160 * @since 1.4
1161 *
1162 * @beaninfo
1163 * description: determines whether automatic drag handling is enabled
1164 * bound: false
1165 */
1166 public void setDragEnabled(boolean b) {
1167 if (b && GraphicsEnvironment.isHeadless()) {
1168 throw new HeadlessException();
1169 }
1170 dragEnabled = b;
1171 }
1172
1173 /**
1174 * Returns whether or not automatic drag handling is enabled.
1175 *
1176 * @return the value of the {@code dragEnabled} property
1177 * @see #setDragEnabled
1178 * @since 1.4
1179 */
1180 public boolean getDragEnabled() {
1181 return dragEnabled;
1182 }
1183
1184 /**
1185 * Sets the drop mode for this component. For backward compatibility,
1186 * the default for this property is <code>DropMode.USE_SELECTION</code>.
1187 * Usage of one of the other modes is recommended, however, for an
1188 * improved user experience. <code>DropMode.ON</code>, for instance,
1189 * offers similar behavior of showing items as selected, but does so without
1190 * affecting the actual selection in the list.
1191 * <p>
1192 * <code>JList</code> supports the following drop modes:
1193 * <ul>
1194 * <li><code>DropMode.USE_SELECTION</code></li>
1195 * <li><code>DropMode.ON</code></li>
1196 * <li><code>DropMode.INSERT</code></li>
1197 * <li><code>DropMode.ON_OR_INSERT</code></li>
1198 * </ul>
1199 * The drop mode is only meaningful if this component has a
1200 * <code>TransferHandler</code> that accepts drops.
1201 *
1202 * @param dropMode the drop mode to use
1203 * @throws IllegalArgumentException if the drop mode is unsupported
1204 * or <code>null</code>
1205 * @see #getDropMode
1206 * @see #getDropLocation
1207 * @see #setTransferHandler
1208 * @see TransferHandler
1209 * @since 1.6
1210 */
1211 public final void setDropMode(DropMode dropMode) {
1212 if (dropMode != null) {
1213 switch (dropMode) {
1214 case USE_SELECTION:
1215 case ON:
1216 case INSERT:
1217 case ON_OR_INSERT:
1218 this .dropMode = dropMode;
1219 return;
1220 }
1221 }
1222
1223 throw new IllegalArgumentException(dropMode
1224 + ": Unsupported drop mode for list");
1225 }
1226
1227 /**
1228 * Returns the drop mode for this component.
1229 *
1230 * @return the drop mode for this component
1231 * @see #setDropMode
1232 * @since 1.6
1233 */
1234 public final DropMode getDropMode() {
1235 return dropMode;
1236 }
1237
1238 /**
1239 * Calculates a drop location in this component, representing where a
1240 * drop at the given point should insert data.
1241 *
1242 * @param p the point to calculate a drop location for
1243 * @return the drop location, or <code>null</code>
1244 */
1245 DropLocation dropLocationForPoint(Point p) {
1246 DropLocation location = null;
1247 Rectangle rect = null;
1248
1249 int index = locationToIndex(p);
1250 if (index != -1) {
1251 rect = getCellBounds(index, index);
1252 }
1253
1254 switch (dropMode) {
1255 case USE_SELECTION:
1256 case ON:
1257 location = new DropLocation(p, (rect != null && rect
1258 .contains(p)) ? index : -1, false);
1259
1260 break;
1261 case INSERT:
1262 if (index == -1) {
1263 location = new DropLocation(p, getModel().getSize(),
1264 true);
1265 break;
1266 }
1267
1268 if (layoutOrientation == HORIZONTAL_WRAP) {
1269 boolean ltr = getComponentOrientation().isLeftToRight();
1270
1271 if (SwingUtilities2.liesInHorizontal(rect, p, ltr,
1272 false) == TRAILING) {
1273 index++;
1274 // special case for below all cells
1275 } else if (index == getModel().getSize() - 1
1276 && p.y >= rect.y + rect.height) {
1277 index++;
1278 }
1279 } else {
1280 if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) {
1281 index++;
1282 }
1283 }
1284
1285 location = new DropLocation(p, index, true);
1286
1287 break;
1288 case ON_OR_INSERT:
1289 if (index == -1) {
1290 location = new DropLocation(p, getModel().getSize(),
1291 true);
1292 break;
1293 }
1294
1295 boolean between = false;
1296
1297 if (layoutOrientation == HORIZONTAL_WRAP) {
1298 boolean ltr = getComponentOrientation().isLeftToRight();
1299
1300 Section section = SwingUtilities2.liesInHorizontal(
1301 rect, p, ltr, true);
1302 if (section == TRAILING) {
1303 index++;
1304 between = true;
1305 // special case for below all cells
1306 } else if (index == getModel().getSize() - 1
1307 && p.y >= rect.y + rect.height) {
1308 index++;
1309 between = true;
1310 } else if (section == LEADING) {
1311 between = true;
1312 }
1313 } else {
1314 Section section = SwingUtilities2.liesInVertical(rect,
1315 p, true);
1316 if (section == LEADING) {
1317 between = true;
1318 } else if (section == TRAILING) {
1319 index++;
1320 between = true;
1321 }
1322 }
1323
1324 location = new DropLocation(p, index, between);
1325
1326 break;
1327 default:
1328 assert false : "Unexpected drop mode";
1329 }
1330
1331 return location;
1332 }
1333
1334 /**
1335 * Called to set or clear the drop location during a DnD operation.
1336 * In some cases, the component may need to use it's internal selection
1337 * temporarily to indicate the drop location. To help facilitate this,
1338 * this method returns and accepts as a parameter a state object.
1339 * This state object can be used to store, and later restore, the selection
1340 * state. Whatever this method returns will be passed back to it in
1341 * future calls, as the state parameter. If it wants the DnD system to
1342 * continue storing the same state, it must pass it back every time.
1343 * Here's how this is used:
1344 * <p>
1345 * Let's say that on the first call to this method the component decides
1346 * to save some state (because it is about to use the selection to show
1347 * a drop index). It can return a state object to the caller encapsulating
1348 * any saved selection state. On a second call, let's say the drop location
1349 * is being changed to something else. The component doesn't need to
1350 * restore anything yet, so it simply passes back the same state object
1351 * to have the DnD system continue storing it. Finally, let's say this
1352 * method is messaged with <code>null</code>. This means DnD
1353 * is finished with this component for now, meaning it should restore
1354 * state. At this point, it can use the state parameter to restore
1355 * said state, and of course return <code>null</code> since there's
1356 * no longer anything to store.
1357 *
1358 * @param location the drop location (as calculated by
1359 * <code>dropLocationForPoint</code>) or <code>null</code>
1360 * if there's no longer a valid drop location
1361 * @param state the state object saved earlier for this component,
1362 * or <code>null</code>
1363 * @param forDrop whether or not the method is being called because an
1364 * actual drop occurred
1365 * @return any saved state for this component, or <code>null</code> if none
1366 */
1367 Object setDropLocation(TransferHandler.DropLocation location,
1368 Object state, boolean forDrop) {
1369
1370 Object retVal = null;
1371 DropLocation listLocation = (DropLocation) location;
1372
1373 if (dropMode == DropMode.USE_SELECTION) {
1374 if (listLocation == null) {
1375 if (!forDrop && state != null) {
1376 setSelectedIndices(((int[][]) state)[0]);
1377
1378 int anchor = ((int[][]) state)[1][0];
1379 int lead = ((int[][]) state)[1][1];
1380
1381 SwingUtilities2.setLeadAnchorWithoutSelection(
1382 getSelectionModel(), lead, anchor);
1383 }
1384 } else {
1385 if (dropLocation == null) {
1386 int[] inds = getSelectedIndices();
1387 retVal = new int[][] {
1388 inds,
1389 { getAnchorSelectionIndex(),
1390 getLeadSelectionIndex() } };
1391 } else {
1392 retVal = state;
1393 }
1394
1395 int index = listLocation.getIndex();
1396 if (index == -1) {
1397 clearSelection();
1398 getSelectionModel().setAnchorSelectionIndex(-1);
1399 getSelectionModel().setLeadSelectionIndex(-1);
1400 } else {
1401 setSelectionInterval(index, index);
1402 }
1403 }
1404 }
1405
1406 DropLocation old = dropLocation;
1407 dropLocation = listLocation;
1408 firePropertyChange("dropLocation", old, dropLocation);
1409
1410 return retVal;
1411 }
1412
1413 /**
1414 * Returns the location that this component should visually indicate
1415 * as the drop location during a DnD operation over the component,
1416 * or {@code null} if no location is to currently be shown.
1417 * <p>
1418 * This method is not meant for querying the drop location
1419 * from a {@code TransferHandler}, as the drop location is only
1420 * set after the {@code TransferHandler}'s <code>canImport</code>
1421 * has returned and has allowed for the location to be shown.
1422 * <p>
1423 * When this property changes, a property change event with
1424 * name "dropLocation" is fired by the component.
1425 * <p>
1426 * By default, responsibility for listening for changes to this property
1427 * and indicating the drop location visually lies with the list's
1428 * {@code ListUI}, which may paint it directly and/or install a cell
1429 * renderer to do so. Developers wishing to implement custom drop location
1430 * painting and/or replace the default cell renderer, may need to honor
1431 * this property.
1432 *
1433 * @return the drop location
1434 * @see #setDropMode
1435 * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1436 * @since 1.6
1437 */
1438 public final DropLocation getDropLocation() {
1439 return dropLocation;
1440 }
1441
1442 /**
1443 * Returns the next list element whose {@code toString} value
1444 * starts with the given prefix.
1445 *
1446 * @param prefix the string to test for a match
1447 * @param startIndex the index for starting the search
1448 * @param bias the search direction, either
1449 * Position.Bias.Forward or Position.Bias.Backward.
1450 * @return the index of the next list element that
1451 * starts with the prefix; otherwise {@code -1}
1452 * @exception IllegalArgumentException if prefix is {@code null}
1453 * or startIndex is out of bounds
1454 * @since 1.4
1455 */
1456 public int getNextMatch(String prefix, int startIndex,
1457 Position.Bias bias) {
1458 ListModel model = getModel();
1459 int max = model.getSize();
1460 if (prefix == null) {
1461 throw new IllegalArgumentException();
1462 }
1463 if (startIndex < 0 || startIndex >= max) {
1464 throw new IllegalArgumentException();
1465 }
1466 prefix = prefix.toUpperCase();
1467
1468 // start search from the next element after the selected element
1469 int increment = (bias == Position.Bias.Forward) ? 1 : -1;
1470 int index = startIndex;
1471 do {
1472 Object o = model.getElementAt(index);
1473
1474 if (o != null) {
1475 String string;
1476
1477 if (o instanceof String) {
1478 string = ((String) o).toUpperCase();
1479 } else {
1480 string = o.toString();
1481 if (string != null) {
1482 string = string.toUpperCase();
1483 }
1484 }
1485
1486 if (string != null && string.startsWith(prefix)) {
1487 return index;
1488 }
1489 }
1490 index = (index + increment + max) % max;
1491 } while (index != startIndex);
1492 return -1;
1493 }
1494
1495 /**
1496 * Returns the tooltip text to be used for the given event. This overrides
1497 * {@code JComponent}'s {@code getToolTipText} to first check the cell
1498 * renderer component for the cell over which the event occurred, returning
1499 * its tooltip text, if any. This implementation allows you to specify
1500 * tooltip text on the cell level, by using {@code setToolTipText} on your
1501 * cell renderer component.
1502 * <p>
1503 * <bold>Note:</bold> For <code>JList</code> to properly display the
1504 * tooltips of its renderers in this manner, <code>JList</code> must be a
1505 * registered component with the <code>ToolTipManager</code>. This registration
1506 * is done automatically in the constructor. However, if at a later point
1507 * <code>JList</code> is unregistered, by way of a call to
1508 * {@code setToolTipText(null)}, tips from the renderers will no longer display.
1509 *
1510 * @param event the {@code MouseEvent} to fetch the tooltip text for
1511 * @see JComponent#setToolTipText
1512 * @see JComponent#getToolTipText
1513 */
1514 public String getToolTipText(MouseEvent event) {
1515 if (event != null) {
1516 Point p = event.getPoint();
1517 int index = locationToIndex(p);
1518 ListCellRenderer r = getCellRenderer();
1519 Rectangle cellBounds;
1520
1521 if (index != -1
1522 && r != null
1523 && (cellBounds = getCellBounds(index, index)) != null
1524 && cellBounds.contains(p.x, p.y)) {
1525 ListSelectionModel lsm = getSelectionModel();
1526 Component rComponent = r.getListCellRendererComponent(
1527 this , getModel().getElementAt(index), index,
1528 lsm.isSelectedIndex(index), (hasFocus() && (lsm
1529 .getLeadSelectionIndex() == index)));
1530
1531 if (rComponent instanceof JComponent) {
1532 MouseEvent newEvent;
1533
1534 p.translate(-cellBounds.x, -cellBounds.y);
1535 newEvent = new MouseEvent(rComponent,
1536 event.getID(), event.getWhen(), event
1537 .getModifiers(), p.x, p.y, event
1538 .getXOnScreen(), event
1539 .getYOnScreen(), event
1540 .getClickCount(), event
1541 .isPopupTrigger(),
1542 MouseEvent.NOBUTTON);
1543
1544 String tip = ((JComponent) rComponent)
1545 .getToolTipText(newEvent);
1546
1547 if (tip != null) {
1548 return tip;
1549 }
1550 }
1551 }
1552 }
1553 return super .getToolTipText();
1554 }
1555
1556 /**
1557 * --- ListUI Delegations ---
1558 */
1559
1560 /**
1561 * Returns the cell index closest to the given location in the list's
1562 * coordinate system. To determine if the cell actually contains the
1563 * specified location, compare the point against the cell's bounds,
1564 * as provided by {@code getCellBounds}. This method returns {@code -1}
1565 * if the model is empty
1566 * <p>
1567 * This is a cover method that delegates to the method of the same name
1568 * in the list's {@code ListUI}. It returns {@code -1} if the list has
1569 * no {@code ListUI}.
1570 *
1571 * @param location the coordinates of the point
1572 * @return the cell index closest to the given location, or {@code -1}
1573 */
1574 public int locationToIndex(Point location) {
1575 ListUI ui = getUI();
1576 return (ui != null) ? ui.locationToIndex(this , location) : -1;
1577 }
1578
1579 /**
1580 * Returns the origin of the specified item in the list's coordinate
1581 * system. This method returns {@code null} if the index isn't valid.
1582 * <p>
1583 * This is a cover method that delegates to the method of the same name
1584 * in the list's {@code ListUI}. It returns {@code null} if the list has
1585 * no {@code ListUI}.
1586 *
1587 * @param index the cell index
1588 * @return the origin of the cell, or {@code null}
1589 */
1590 public Point indexToLocation(int index) {
1591 ListUI ui = getUI();
1592 return (ui != null) ? ui.indexToLocation(this , index) : null;
1593 }
1594
1595 /**
1596 * Returns the bounding rectangle, in the list's coordinate system,
1597 * for the range of cells specified by the two indices.
1598 * These indices can be supplied in any order.
1599 * <p>
1600 * If the smaller index is outside the list's range of cells, this method
1601 * returns {@code null}. If the smaller index is valid, but the larger
1602 * index is outside the list's range, the bounds of just the first index
1603 * is returned. Otherwise, the bounds of the valid range is returned.
1604 * <p>
1605 * This is a cover method that delegates to the method of the same name
1606 * in the list's {@code ListUI}. It returns {@code null} if the list has
1607 * no {@code ListUI}.
1608 *
1609 * @param index0 the first index in the range
1610 * @param index1 the second index in the range
1611 * @return the bounding rectangle for the range of cells, or {@code null}
1612 */
1613 public Rectangle getCellBounds(int index0, int index1) {
1614 ListUI ui = getUI();
1615 return (ui != null) ? ui.getCellBounds(this , index0, index1)
1616 : null;
1617 }
1618
1619 /**
1620 * --- ListModel Support ---
1621 */
1622
1623 /**
1624 * Returns the data model that holds the list of items displayed
1625 * by the <code>JList</code> component.
1626 *
1627 * @return the <code>ListModel</code> that provides the displayed
1628 * list of items
1629 * @see #setModel
1630 */
1631 public ListModel getModel() {
1632 return dataModel;
1633 }
1634
1635 /**
1636 * Sets the model that represents the contents or "value" of the
1637 * list, notifies property change listeners, and then clears the
1638 * list's selection.
1639 * <p>
1640 * This is a JavaBeans bound property.
1641 *
1642 * @param model the <code>ListModel</code> that provides the
1643 * list of items for display
1644 * @exception IllegalArgumentException if <code>model</code> is
1645 * <code>null</code>
1646 * @see #getModel
1647 * @see #clearSelection
1648 * @beaninfo
1649 * bound: true
1650 * attribute: visualUpdate true
1651 * description: The object that contains the data to be drawn by this JList.
1652 */
1653 public void setModel(ListModel model) {
1654 if (model == null) {
1655 throw new IllegalArgumentException("model must be non null");
1656 }
1657 ListModel oldValue = dataModel;
1658 dataModel = model;
1659 firePropertyChange("model", oldValue, dataModel);
1660 clearSelection();
1661 }
1662
1663 /**
1664 * Constructs a read-only <code>ListModel</code> from an array of objects,
1665 * and calls {@code setModel} with this model.
1666 * <p>
1667 * Attempts to pass a {@code null} value to this method results in
1668 * undefined behavior and, most likely, exceptions. The created model
1669 * references the given array directly. Attempts to modify the array
1670 * after invoking this method results in undefined behavior.
1671 *
1672 * @param listData an array of {@code Objects} containing the items to
1673 * display in the list
1674 * @see #setModel
1675 */
1676 public void setListData(final Object[] listData) {
1677 setModel(new AbstractListModel() {
1678 public int getSize() {
1679 return listData.length;
1680 }
1681
1682 public Object getElementAt(int i) {
1683 return listData[i];
1684 }
1685 });
1686 }
1687
1688 /**
1689 * Constructs a read-only <code>ListModel</code> from a <code>Vector</code>
1690 * and calls {@code setModel} with this model.
1691 * <p>
1692 * Attempts to pass a {@code null} value to this method results in
1693 * undefined behavior and, most likely, exceptions. The created model
1694 * references the given {@code Vector} directly. Attempts to modify the
1695 * {@code Vector} after invoking this method results in undefined behavior.
1696 *
1697 * @param listData a <code>Vector</code> containing the items to
1698 * display in the list
1699 * @see #setModel
1700 */
1701 public void setListData(final Vector<?> listData) {
1702 setModel(new AbstractListModel() {
1703 public int getSize() {
1704 return listData.size();
1705 }
1706
1707 public Object getElementAt(int i) {
1708 return listData.elementAt(i);
1709 }
1710 });
1711 }
1712
1713 /**
1714 * --- ListSelectionModel delegations and extensions ---
1715 */
1716
1717 /**
1718 * Returns an instance of {@code DefaultListSelectionModel}; called
1719 * during construction to initialize the list's selection model
1720 * property.
1721 *
1722 * @return a {@code DefaultListSelecitonModel}, used to initialize
1723 * the list's selection model property during construction
1724 * @see #setSelectionModel
1725 * @see DefaultListSelectionModel
1726 */
1727 protected ListSelectionModel createSelectionModel() {
1728 return new DefaultListSelectionModel();
1729 }
1730
1731 /**
1732 * Returns the current selection model. The selection model maintains the
1733 * selection state of the list. See the class level documentation for more
1734 * details.
1735 *
1736 * @return the <code>ListSelectionModel</code> that maintains the
1737 * list's selections
1738 *
1739 * @see #setSelectionModel
1740 * @see ListSelectionModel
1741 */
1742 public ListSelectionModel getSelectionModel() {
1743 return selectionModel;
1744 }
1745
1746 /**
1747 * Notifies {@code ListSelectionListener}s added directly to the list
1748 * of selection changes made to the selection model. {@code JList}
1749 * listens for changes made to the selection in the selection model,
1750 * and forwards notification to listeners added to the list directly,
1751 * by calling this method.
1752 * <p>
1753 * This method constructs a {@code ListSelectionEvent} with this list
1754 * as the source, and the specified arguments, and sends it to the
1755 * registered {@code ListSelectionListeners}.
1756 *
1757 * @param firstIndex the first index in the range, {@code <= lastIndex}
1758 * @param lastIndex the last index in the range, {@code >= firstIndex}
1759 * @param isAdjusting whether or not this is one in a series of
1760 * multiple events, where changes are still being made
1761 *
1762 * @see #addListSelectionListener
1763 * @see #removeListSelectionListener
1764 * @see javax.swing.event.ListSelectionEvent
1765 * @see EventListenerList
1766 */
1767 protected void fireSelectionValueChanged(int firstIndex,
1768 int lastIndex, boolean isAdjusting) {
1769 Object[] listeners = listenerList.getListenerList();
1770 ListSelectionEvent e = null;
1771
1772 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1773 if (listeners[i] == ListSelectionListener.class) {
1774 if (e == null) {
1775 e = new ListSelectionEvent(this , firstIndex,
1776 lastIndex, isAdjusting);
1777 }
1778 ((ListSelectionListener) listeners[i + 1])
1779 .valueChanged(e);
1780 }
1781 }
1782 }
1783
1784 /* A ListSelectionListener that forwards ListSelectionEvents from
1785 * the selectionModel to the JList ListSelectionListeners. The
1786 * forwarded events only differ from the originals in that their
1787 * source is the JList instead of the selectionModel itself.
1788 */
1789 private class ListSelectionHandler implements
1790 ListSelectionListener, Serializable {
1791 public void valueChanged(ListSelectionEvent e) {
1792 fireSelectionValueChanged(e.getFirstIndex(), e
1793 .getLastIndex(), e.getValueIsAdjusting());
1794 }
1795 }
1796
1797 /**
1798 * Adds a listener to the list, to be notified each time a change to the
1799 * selection occurs; the preferred way of listening for selection state
1800 * changes. {@code JList} takes care of listening for selection state
1801 * changes in the selection model, and notifies the given listener of
1802 * each change. {@code ListSelectionEvent}s sent to the listener have a
1803 * {@code source} property set to this list.
1804 *
1805 * @param listener the {@code ListSelectionListener} to add
1806 * @see #getSelectionModel
1807 * @see #getListSelectionListeners
1808 */
1809 public void addListSelectionListener(ListSelectionListener listener) {
1810 if (selectionListener == null) {
1811 selectionListener = new ListSelectionHandler();
1812 getSelectionModel().addListSelectionListener(
1813 selectionListener);
1814 }
1815
1816 listenerList.add(ListSelectionListener.class, listener);
1817 }
1818
1819 /**
1820 * Removes a selection listener from the list.
1821 *
1822 * @param listener the {@code ListSelectionListener} to remove
1823 * @see #addListSelectionListener
1824 * @see #getSelectionModel
1825 */
1826 public void removeListSelectionListener(
1827 ListSelectionListener listener) {
1828 listenerList.remove(ListSelectionListener.class, listener);
1829 }
1830
1831 /**
1832 * Returns an array of all the {@code ListSelectionListener}s added
1833 * to this {@code JList} by way of {@code addListSelectionListener}.
1834 *
1835 * @return all of the {@code ListSelectionListener}s on this list, or
1836 * an empty array if no listeners have been added
1837 * @see #addListSelectionListener
1838 * @since 1.4
1839 */
1840 public ListSelectionListener[] getListSelectionListeners() {
1841 return (ListSelectionListener[]) listenerList
1842 .getListeners(ListSelectionListener.class);
1843 }
1844
1845 /**
1846 * Sets the <code>selectionModel</code> for the list to a
1847 * non-<code>null</code> <code>ListSelectionModel</code>
1848 * implementation. The selection model handles the task of making single
1849 * selections, selections of contiguous ranges, and non-contiguous
1850 * selections.
1851 * <p>
1852 * This is a JavaBeans bound property.
1853 *
1854 * @param selectionModel the <code>ListSelectionModel</code> that
1855 * implements the selections
1856 * @exception IllegalArgumentException if <code>selectionModel</code>
1857 * is <code>null</code>
1858 * @see #getSelectionModel
1859 * @beaninfo
1860 * bound: true
1861 * description: The selection model, recording which cells are selected.
1862 */
1863 public void setSelectionModel(ListSelectionModel selectionModel) {
1864 if (selectionModel == null) {
1865 throw new IllegalArgumentException(
1866 "selectionModel must be non null");
1867 }
1868
1869 /* Remove the forwarding ListSelectionListener from the old
1870 * selectionModel, and add it to the new one, if necessary.
1871 */
1872 if (selectionListener != null) {
1873 this .selectionModel
1874 .removeListSelectionListener(selectionListener);
1875 selectionModel.addListSelectionListener(selectionListener);
1876 }
1877
1878 ListSelectionModel oldValue = this .selectionModel;
1879 this .selectionModel = selectionModel;
1880 firePropertyChange("selectionModel", oldValue, selectionModel);
1881 }
1882
1883 /**
1884 * Sets the selection mode for the list. This is a cover method that sets
1885 * the selection mode directly on the selection model.
1886 * <p>
1887 * The following list describes the accepted selection modes:
1888 * <ul>
1889 * <li>{@code ListSelectionModel.SINGLE_SELECTION} -
1890 * Only one list index can be selected at a time. In this mode,
1891 * {@code setSelectionInterval} and {@code addSelectionInterval} are
1892 * equivalent, both replacing the current selection with the index
1893 * represented by the second argument (the "lead").
1894 * <li>{@code ListSelectionModel.SINGLE_INTERVAL_SELECTION} -
1895 * Only one contiguous interval can be selected at a time.
1896 * In this mode, {@code addSelectionInterval} behaves like
1897 * {@code setSelectionInterval} (replacing the current selection},
1898 * unless the given interval is immediately adjacent to or overlaps
1899 * the existing selection, and can be used to grow the selection.
1900 * <li>{@code ListSelectionModel.MULTIPLE_INTERVAL_SELECTION} -
1901 * In this mode, there's no restriction on what can be selected.
1902 * This mode is the default.
1903 * </ul>
1904 *
1905 * @param selectionMode the selection mode
1906 * @see #getSelectionMode
1907 * @throws IllegalArgumentException if the selection mode isn't
1908 * one of those allowed
1909 * @beaninfo
1910 * description: The selection mode.
1911 * enum: SINGLE_SELECTION ListSelectionModel.SINGLE_SELECTION
1912 * SINGLE_INTERVAL_SELECTION ListSelectionModel.SINGLE_INTERVAL_SELECTION
1913 * MULTIPLE_INTERVAL_SELECTION ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
1914 */
1915 public void setSelectionMode(int selectionMode) {
1916 getSelectionModel().setSelectionMode(selectionMode);
1917 }
1918
1919 /**
1920 * Returns the current selection mode for the list. This is a cover
1921 * method that delegates to the method of the same name on the
1922 * list's selection model.
1923 *
1924 * @return the current selection mode
1925 * @see #setSelectionMode
1926 */
1927 public int getSelectionMode() {
1928 return getSelectionModel().getSelectionMode();
1929 }
1930
1931 /**
1932 * Returns the anchor selection index. This is a cover method that
1933 * delegates to the method of the same name on the list's selection model.
1934 *
1935 * @return the anchor selection index
1936 * @see ListSelectionModel#getAnchorSelectionIndex
1937 */
1938 public int getAnchorSelectionIndex() {
1939 return getSelectionModel().getAnchorSelectionIndex();
1940 }
1941
1942 /**
1943 * Returns the lead selection index. This is a cover method that
1944 * delegates to the method of the same name on the list's selection model.
1945 *
1946 * @return the lead selection index
1947 * @see ListSelectionModel#getLeadSelectionIndex
1948 * @beaninfo
1949 * description: The lead selection index.
1950 */
1951 public int getLeadSelectionIndex() {
1952 return getSelectionModel().getLeadSelectionIndex();
1953 }
1954
1955 /**
1956 * Returns the smallest selected cell index, or {@code -1} if the selection
1957 * is empty. This is a cover method that delegates to the method of the same
1958 * name on the list's selection model.
1959 *
1960 * @return the smallest selected cell index, or {@code -1}
1961 * @see ListSelectionModel#getMinSelectionIndex
1962 */
1963 public int getMinSelectionIndex() {
1964 return getSelectionModel().getMinSelectionIndex();
1965 }
1966
1967 /**
1968 * Returns the largest selected cell index, or {@code -1} if the selection
1969 * is empty. This is a cover method that delegates to the method of the same
1970 * name on the list's selection model.
1971 *
1972 * @return the largest selected cell index
1973 * @see ListSelectionModel#getMaxSelectionIndex
1974 */
1975 public int getMaxSelectionIndex() {
1976 return getSelectionModel().getMaxSelectionIndex();
1977 }
1978
1979 /**
1980 * Returns {@code true} if the specified index is selected,
1981 * else {@code false}. This is a cover method that delegates to the method
1982 * of the same name on the list's selection model.
1983 *
1984 * @param index index to be queried for selection state
1985 * @return {@code true} if the specified index is selected,
1986 * else {@code false}
1987 * @see ListSelectionModel#isSelectedIndex
1988 * @see #setSelectedIndex
1989 */
1990 public boolean isSelectedIndex(int index) {
1991 return getSelectionModel().isSelectedIndex(index);
1992 }
1993
1994 /**
1995 * Returns {@code true} if nothing is selected, else {@code false}.
1996 * This is a cover method that delegates to the method of the same
1997 * name on the list's selection model.
1998 *
1999 * @return {@code true} if nothing is selected, else {@code false}
2000 * @see ListSelectionModel#isSelectionEmpty
2001 * @see #clearSelection
2002 */
2003 public boolean isSelectionEmpty() {
2004 return getSelectionModel().isSelectionEmpty();
2005 }
2006
2007 /**
2008 * Clears the selection; after calling this method, {@code isSelectionEmpty}
2009 * will return {@code true}. This is a cover method that delegates to the
2010 * method of the same name on the list's selection model.
2011 *
2012 * @see ListSelectionModel#clearSelection
2013 * @see #isSelectionEmpty
2014 */
2015 public void clearSelection() {
2016 getSelectionModel().clearSelection();
2017 }
2018
2019 /**
2020 * Selects the specified interval. Both {@code anchor} and {@code lead}
2021 * indices are included. {@code anchor} doesn't have to be less than or
2022 * equal to {@code lead}. This is a cover method that delegates to the
2023 * method of the same name on the list's selection model.
2024 * <p>
2025 * Refer to the documentation of the selection model class being used
2026 * for details on how values less than {@code 0} are handled.
2027 *
2028 * @param anchor the first index to select
2029 * @param lead the last index to select
2030 * @see ListSelectionModel#setSelectionInterval
2031 * @see DefaultListSelectionModel#setSelectionInterval
2032 * @see #createSelectionModel
2033 * @see #addSelectionInterval
2034 * @see #removeSelectionInterval
2035 */
2036 public void setSelectionInterval(int anchor, int lead) {
2037 getSelectionModel().setSelectionInterval(anchor, lead);
2038 }
2039
2040 /**
2041 * Sets the selection to be the union of the specified interval with current
2042 * selection. Both the {@code anchor} and {@code lead} indices are
2043 * included. {@code anchor} doesn't have to be less than or
2044 * equal to {@code lead}. This is a cover method that delegates to the
2045 * method of the same name on the list's selection model.
2046 * <p>
2047 * Refer to the documentation of the selection model class being used
2048 * for details on how values less than {@code 0} are handled.
2049 *
2050 * @param anchor the first index to add to the selection
2051 * @param lead the last index to add to the selection
2052 * @see ListSelectionModel#addSelectionInterval
2053 * @see DefaultListSelectionModel#addSelectionInterval
2054 * @see #createSelectionModel
2055 * @see #setSelectionInterval
2056 * @see #removeSelectionInterval
2057 */
2058 public void addSelectionInterval(int anchor, int lead) {
2059 getSelectionModel().addSelectionInterval(anchor, lead);
2060 }
2061
2062 /**
2063 * Sets the selection to be the set difference of the specified interval
2064 * and the current selection. Both the {@code index0} and {@code index1}
2065 * indices are removed. {@code index0} doesn't have to be less than or
2066 * equal to {@code index1}. This is a cover method that delegates to the
2067 * method of the same name on the list's selection model.
2068 * <p>
2069 * Refer to the documentation of the selection model class being used
2070 * for details on how values less than {@code 0} are handled.
2071 *
2072 * @param index0 the first index to remove from the selection
2073 * @param index1 the last index to remove from the selection
2074 * @see ListSelectionModel#removeSelectionInterval
2075 * @see DefaultListSelectionModel#removeSelectionInterval
2076 * @see #createSelectionModel
2077 * @see #setSelectionInterval
2078 * @see #addSelectionInterval
2079 */
2080 public void removeSelectionInterval(int index0, int index1) {
2081 getSelectionModel().removeSelectionInterval(index0, index1);
2082 }
2083
2084 /**
2085 * Sets the selection model's {@code valueIsAdjusting} property. When
2086 * {@code true}, upcoming changes to selection should be considered part
2087 * of a single change. This property is used internally and developers
2088 * typically need not call this method. For example, when the model is being
2089 * updated in response to a user drag, the value of the property is set
2090 * to {@code true} when the drag is initiated and set to {@code false}
2091 * when the drag is finished. This allows listeners to update only
2092 * when a change has been finalized, rather than handling all of the
2093 * intermediate values.
2094 * <p>
2095 * You may want to use this directly if making a series of changes
2096 * that should be considered part of a single change.
2097 * <p>
2098 * This is a cover method that delegates to the method of the same name on
2099 * the list's selection model. See the documentation for
2100 * {@link javax.swing.ListSelectionModel#setValueIsAdjusting} for
2101 * more details.
2102 *
2103 * @param b the new value for the property
2104 * @see ListSelectionModel#setValueIsAdjusting
2105 * @see javax.swing.event.ListSelectionEvent#getValueIsAdjusting
2106 * @see #getValueIsAdjusting
2107 */
2108 public void setValueIsAdjusting(boolean b) {
2109 getSelectionModel().setValueIsAdjusting(b);
2110 }
2111
2112 /**
2113 * Returns the value of the selection model's {@code isAdjusting} property.
2114 * <p>
2115 * This is a cover method that delegates to the method of the same name on
2116 * the list's selection model.
2117 *
2118 * @return the value of the selection model's {@code isAdjusting} property.
2119 *
2120 * @see #setValueIsAdjusting
2121 * @see ListSelectionModel#getValueIsAdjusting
2122 */
2123 public boolean getValueIsAdjusting() {
2124 return getSelectionModel().getValueIsAdjusting();
2125 }
2126
2127 /**
2128 * Returns an array of all of the selected indices, in increasing
2129 * order.
2130 *
2131 * @return all of the selected indices, in increasing order,
2132 * or an empty array if nothing is selected
2133 * @see #removeSelectionInterval
2134 * @see #addListSelectionListener
2135 */
2136 public int[] getSelectedIndices() {
2137 ListSelectionModel sm = getSelectionModel();
2138 int iMin = sm.getMinSelectionIndex();
2139 int iMax = sm.getMaxSelectionIndex();
2140
2141 if ((iMin < 0) || (iMax < 0)) {
2142 return new int[0];
2143 }
2144
2145 int[] rvTmp = new int[1 + (iMax - iMin)];
2146 int n = 0;
2147 for (int i = iMin; i <= iMax; i++) {
2148 if (sm.isSelectedIndex(i)) {
2149 rvTmp[n++] = i;
2150 }
2151 }
2152 int[] rv = new int[n];
2153 System.arraycopy(rvTmp, 0, rv, 0, n);
2154 return rv;
2155 }
2156
2157 /**
2158 * Selects a single cell. Does nothing if the given index is greater
2159 * than or equal to the model size. This is a convenience method that uses
2160 * {@code setSelectionInterval} on the selection model. Refer to the
2161 * documentation for the selection model class being used for details on
2162 * how values less than {@code 0} are handled.
2163 *
2164 * @param index the index of the cell to select
2165 * @see ListSelectionModel#setSelectionInterval
2166 * @see #isSelectedIndex
2167 * @see #addListSelectionListener
2168 * @beaninfo
2169 * description: The index of the selected cell.
2170 */
2171 public void setSelectedIndex(int index) {
2172 if (index >= getModel().getSize()) {
2173 return;
2174 }
2175 getSelectionModel().setSelectionInterval(index, index);
2176 }
2177
2178 /**
2179 * Changes the selection to be the set of indices specified by the given
2180 * array. Indices greater than or equal to the model size are ignored.
2181 * This is a convenience method that clears the selection and then uses
2182 * {@code addSelectionInterval} on the selection model to add the indices.
2183 * Refer to the documentation of the selection model class being used for
2184 * details on how values less than {@code 0} are handled.
2185 *
2186 * @param indices an array of the indices of the cells to select,
2187 * {@code non-null}
2188 * @see ListSelectionModel#addSelectionInterval
2189 * @see #isSelectedIndex
2190 * @see #addListSelectionListener
2191 * @throws NullPointerException if the given array is {@code null}
2192 */
2193 public void setSelectedIndices(int[] indices) {
2194 ListSelectionModel sm = getSelectionModel();
2195 sm.clearSelection();
2196 int size = getModel().getSize();
2197 for (int i = 0; i < indices.length; i++) {
2198 if (indices[i] < size) {
2199 sm.addSelectionInterval(indices[i], indices[i]);
2200 }
2201 }
2202 }
2203
2204 /**
2205 * Returns an array of all the selected values, in increasing order based
2206 * on their indices in the list.
2207 *
2208 * @return the selected values, or an empty array if nothing is selected
2209 * @see #isSelectedIndex
2210 * @see #getModel
2211 * @see #addListSelectionListener
2212 */
2213 public Object[] getSelectedValues() {
2214 ListSelectionModel sm = getSelectionModel();
2215 ListModel dm = getModel();
2216
2217 int iMin = sm.getMinSelectionIndex();
2218 int iMax = sm.getMaxSelectionIndex();
2219
2220 if ((iMin < 0) || (iMax < 0)) {
2221 return new Object[0];
2222 }
2223
2224 Object[] rvTmp = new Object[1 + (iMax - iMin)];
2225 int n = 0;
2226 for (int i = iMin; i <= iMax; i++) {
2227 if (sm.isSelectedIndex(i)) {
2228 rvTmp[n++] = dm.getElementAt(i);
2229 }
2230 }
2231 Object[] rv = new Object[n];
2232 System.arraycopy(rvTmp, 0, rv, 0, n);
2233 return rv;
2234 }
2235
2236 /**
2237 * Returns the smallest selected cell index; <i>the selection</i> when only
2238 * a single item is selected in the list. When multiple items are selected,
2239 * it is simply the smallest selected index. Returns {@code -1} if there is
2240 * no selection.
2241 * <p>
2242 * This method is a cover that delegates to {@code getMinSelectionIndex}.
2243 *
2244 * @return the smallest selected cell index
2245 * @see #getMinSelectionIndex
2246 * @see #addListSelectionListener
2247 */
2248 public int getSelectedIndex() {
2249 return getMinSelectionIndex();
2250 }
2251
2252 /**
2253 * Returns the value for the smallest selected cell index;
2254 * <i>the selected value</i> when only a single item is selected in the
2255 * list. When multiple items are selected, it is simply the value for the
2256 * smallest selected index. Returns {@code null} if there is no selection.
2257 * <p>
2258 * This is a convenience method that simply returns the model value for
2259 * {@code getMinSelectionIndex}.
2260 *
2261 * @return the first selected value
2262 * @see #getMinSelectionIndex
2263 * @see #getModel
2264 * @see #addListSelectionListener
2265 */
2266 public Object getSelectedValue() {
2267 int i = getMinSelectionIndex();
2268 return (i == -1) ? null : getModel().getElementAt(i);
2269 }
2270
2271 /**
2272 * Selects the specified object from the list.
2273 *
2274 * @param anObject the object to select
2275 * @param shouldScroll {@code true} if the list should scroll to display
2276 * the selected object, if one exists; otherwise {@code false}
2277 */
2278 public void setSelectedValue(Object anObject, boolean shouldScroll) {
2279 if (anObject == null)
2280 setSelectedIndex(-1);
2281 else if (!anObject.equals(getSelectedValue())) {
2282 int i, c;
2283 ListModel dm = getModel();
2284 for (i = 0, c = dm.getSize(); i < c; i++)
2285 if (anObject.equals(dm.getElementAt(i))) {
2286 setSelectedIndex(i);
2287 if (shouldScroll)
2288 ensureIndexIsVisible(i);
2289 repaint();
2290 /** FIX-ME setSelectedIndex does not redraw all the time with the basic l&f**/
2291 return;
2292 }
2293 setSelectedIndex(-1);
2294 }
2295 repaint();
2296 /** FIX-ME setSelectedIndex does not redraw all the time with the basic l&f**/
2297 }
2298
2299 /**
2300 * --- The Scrollable Implementation ---
2301 */
2302
2303 private void checkScrollableParameters(Rectangle visibleRect,
2304 int orientation) {
2305 if (visibleRect == null) {
2306 throw new IllegalArgumentException(
2307 "visibleRect must be non-null");
2308 }
2309 switch (orientation) {
2310 case SwingConstants.VERTICAL:
2311 case SwingConstants.HORIZONTAL:
2312 break;
2313 default:
2314 throw new IllegalArgumentException(
2315 "orientation must be one of: VERTICAL, HORIZONTAL");
2316 }
2317 }
2318
2319 /**
2320 * Computes the size of viewport needed to display {@code visibleRowCount}
2321 * rows. The value returned by this method depends on the layout
2322 * orientation:
2323 * <p>
2324 * <b>{@code VERTICAL}:</b>
2325 * <br>
2326 * This is trivial if both {@code fixedCellWidth} and {@code fixedCellHeight}
2327 * have been set (either explicitly or by specifying a prototype cell value).
2328 * The width is simply the {@code fixedCellWidth} plus the list's horizontal
2329 * insets. The height is the {@code fixedCellHeight} multiplied by the
2330 * {@code visibleRowCount}, plus the list's vertical insets.
2331 * <p>
2332 * If either {@code fixedCellWidth} or {@code fixedCellHeight} haven't been
2333 * specified, heuristics are used. If the model is empty, the width is
2334 * the {@code fixedCellWidth}, if greater than {@code 0}, or a hard-coded
2335 * value of {@code 256}. The height is the {@code fixedCellHeight} multiplied
2336 * by {@code visibleRowCount}, if {@code fixedCellHeight} is greater than
2337 * {@code 0}, otherwise it is a hard-coded value of {@code 16} multiplied by
2338 * {@code visibleRowCount}.
2339 * <p>
2340 * If the model isn't empty, the width is the preferred size's width,
2341 * typically the width of the widest list element. The height is the
2342 * {@code fixedCellHeight} multiplied by the {@code visibleRowCount},
2343 * plus the list's vertical insets.
2344 * <p>
2345 * <b>{@code VERTICAL_WRAP} or {@code HORIZONTAL_WRAP}:</b>
2346 * <br>
2347 * This method simply returns the value from {@code getPreferredSize}.
2348 * The list's {@code ListUI} is expected to override {@code getPreferredSize}
2349 * to return an appropriate value.
2350 *
2351 * @return a dimension containing the size of the viewport needed
2352 * to display {@code visibleRowCount} rows
2353 * @see #getPreferredScrollableViewportSize
2354 * @see #setPrototypeCellValue
2355 */
2356 public Dimension getPreferredScrollableViewportSize() {
2357 if (getLayoutOrientation() != VERTICAL) {
2358 return getPreferredSize();
2359 }
2360 Insets insets = getInsets();
2361 int dx = insets.left + insets.right;
2362 int dy = insets.top + insets.bottom;
2363
2364 int visibleRowCount = getVisibleRowCount();
2365 int fixedCellWidth = getFixedCellWidth();
2366 int fixedCellHeight = getFixedCellHeight();
2367
2368 if ((fixedCellWidth > 0) && (fixedCellHeight > 0)) {
2369 int width = fixedCellWidth + dx;
2370 int height = (visibleRowCount * fixedCellHeight) + dy;
2371 return new Dimension(width, height);
2372 } else if (getModel().getSize() > 0) {
2373 int width = getPreferredSize().width;
2374 int height;
2375 Rectangle r = getCellBounds(0, 0);
2376 if (r != null) {
2377 height = (visibleRowCount * r.height) + dy;
2378 } else {
2379 // Will only happen if UI null, shouldn't matter what we return
2380 height = 1;
2381 }
2382 return new Dimension(width, height);
2383 } else {
2384 fixedCellWidth = (fixedCellWidth > 0) ? fixedCellWidth
2385 : 256;
2386 fixedCellHeight = (fixedCellHeight > 0) ? fixedCellHeight
2387 : 16;
2388 return new Dimension(fixedCellWidth, fixedCellHeight
2389 * visibleRowCount);
2390 }
2391 }
2392
2393 /**
2394 * Returns the distance to scroll to expose the next or previous
2395 * row (for vertical scrolling) or column (for horizontal scrolling).
2396 * <p>
2397 * For horizontal scrolling, if the layout orientation is {@code VERTICAL},
2398 * then the list's font size is returned (or {@code 1} if the font is
2399 * {@code null}).
2400 *
2401 * @param visibleRect the view area visible within the viewport
2402 * @param orientation {@code SwingConstants.HORIZONTAL} or
2403 * {@code SwingConstants.VERTICAL}
2404 * @param direction less or equal to zero to scroll up/back,
2405 * greater than zero for down/forward
2406 * @return the "unit" increment for scrolling in the specified direction;
2407 * always positive
2408 * @see #getScrollableBlockIncrement
2409 * @see Scrollable#getScrollableUnitIncrement
2410 * @throws IllegalArgumentException if {@code visibleRect} is {@code null}, or
2411 * {@code orientation} isn't one of {@code SwingConstants.VERTICAL} or
2412 * {@code SwingConstants.HORIZONTAL}
2413 */
2414 public int getScrollableUnitIncrement(Rectangle visibleRect,
2415 int orientation, int direction) {
2416 checkScrollableParameters(visibleRect, orientation);
2417
2418 if (orientation == SwingConstants.VERTICAL) {
2419 int row = locationToIndex(visibleRect.getLocation());
2420
2421 if (row == -1) {
2422 return 0;
2423 } else {
2424 /* Scroll Down */
2425 if (direction > 0) {
2426 Rectangle r = getCellBounds(row, row);
2427 return (r == null) ? 0 : r.height
2428 - (visibleRect.y - r.y);
2429 }
2430 /* Scroll Up */
2431 else {
2432 Rectangle r = getCellBounds(row, row);
2433
2434 /* The first row is completely visible and it's row 0.
2435 * We're done.
2436 */
2437 if ((r.y == visibleRect.y) && (row == 0)) {
2438 return 0;
2439 }
2440 /* The first row is completely visible, return the
2441 * height of the previous row or 0 if the first row
2442 * is the top row of the list.
2443 */
2444 else if (r.y == visibleRect.y) {
2445 Point loc = r.getLocation();
2446 loc.y--;
2447 int prevIndex = locationToIndex(loc);
2448 Rectangle prevR = getCellBounds(prevIndex,
2449 prevIndex);
2450
2451 if (prevR == null || prevR.y >= r.y) {
2452 return 0;
2453 }
2454 return prevR.height;
2455 }
2456 /* The first row is partially visible, return the
2457 * height of hidden part.
2458 */
2459 else {
2460 return visibleRect.y - r.y;
2461 }
2462 }
2463 }
2464 } else if (orientation == SwingConstants.HORIZONTAL
2465 && getLayoutOrientation() != JList.VERTICAL) {
2466 boolean leftToRight = getComponentOrientation()
2467 .isLeftToRight();
2468 int index;
2469 Point leadingPoint;
2470
2471 if (leftToRight) {
2472 leadingPoint = visibleRect.getLocation();
2473 } else {
2474 leadingPoint = new Point(visibleRect.x
2475 + visibleRect.width - 1, visibleRect.y);
2476 }
2477 index = locationToIndex(leadingPoint);
2478
2479 if (index != -1) {
2480 Rectangle cellBounds = getCellBounds(index, index);
2481 if (cellBounds != null
2482 && cellBounds.contains(leadingPoint)) {
2483 int leadingVisibleEdge;
2484 int leadingCellEdge;
2485
2486 if (leftToRight) {
2487 leadingVisibleEdge = visibleRect.x;
2488 leadingCellEdge = cellBounds.x;
2489 } else {
2490 leadingVisibleEdge = visibleRect.x
2491 + visibleRect.width;
2492 leadingCellEdge = cellBounds.x
2493 + cellBounds.width;
2494 }
2495
2496 if (leadingCellEdge != leadingVisibleEdge) {
2497 if (direction < 0) {
2498 // Show remainder of leading cell
2499 return Math.abs(leadingVisibleEdge
2500 - leadingCellEdge);
2501
2502 } else if (leftToRight) {
2503 // Hide rest of leading cell
2504 return leadingCellEdge + cellBounds.width
2505 - leadingVisibleEdge;
2506 } else {
2507 // Hide rest of leading cell
2508 return leadingVisibleEdge - cellBounds.x;
2509 }
2510 }
2511 // ASSUME: All cells are the same width
2512 return cellBounds.width;
2513 }
2514 }
2515 }
2516 Font f = getFont();
2517 return (f != null) ? f.getSize() : 1;
2518 }
2519
2520 /**
2521 * Returns the distance to scroll to expose the next or previous block.
2522 * <p>
2523 * For vertical scrolling, the following rules are used:
2524 * <ul>
2525 * <li>if scrolling down, returns the distance to scroll so that the last
2526 * visible element becomes the first completely visible element
2527 * <li>if scrolling up, returns the distance to scroll so that the first
2528 * visible element becomes the last completely visible element
2529 * <li>returns {@code visibleRect.height} if the list is empty
2530 * </ul>
2531 * <p>
2532 * For horizontal scrolling, when the layout orientation is either
2533 * {@code VERTICAL_WRAP} or {@code HORIZONTAL_WRAP}:
2534 * <ul>
2535 * <li>if scrolling right, returns the distance to scroll so that the
2536 * last visible element becomes
2537 * the first completely visible element
2538 * <li>if scrolling left, returns the distance to scroll so that the first
2539 * visible element becomes the last completely visible element
2540 * <li>returns {@code visibleRect.width} if the list is empty
2541 * </ul>
2542 * <p>
2543 * For horizontal scrolling and {@code VERTICAL} orientation,
2544 * returns {@code visibleRect.width}.
2545 * <p>
2546 * Note that the value of {@code visibleRect} must be the equal to
2547 * {@code this.getVisibleRect()}.
2548 *
2549 * @param visibleRect the view area visible within the viewport
2550 * @param orientation {@code SwingConstants.HORIZONTAL} or
2551 * {@code SwingConstants.VERTICAL}
2552 * @param direction less or equal to zero to scroll up/back,
2553 * greater than zero for down/forward
2554 * @return the "block" increment for scrolling in the specified direction;
2555 * always positive
2556 * @see #getScrollableUnitIncrement
2557 * @see Scrollable#getScrollableBlockIncrement
2558 * @throws IllegalArgumentException if {@code visibleRect} is {@code null}, or
2559 * {@code orientation} isn't one of {@code SwingConstants.VERTICAL} or
2560 * {@code SwingConstants.HORIZONTAL}
2561 */
2562 public int getScrollableBlockIncrement(Rectangle visibleRect,
2563 int orientation, int direction) {
2564 checkScrollableParameters(visibleRect, orientation);
2565 if (orientation == SwingConstants.VERTICAL) {
2566 int inc = visibleRect.height;
2567 /* Scroll Down */
2568 if (direction > 0) {
2569 // last cell is the lowest left cell
2570 int last = locationToIndex(new Point(visibleRect.x,
2571 visibleRect.y + visibleRect.height - 1));
2572 if (last != -1) {
2573 Rectangle lastRect = getCellBounds(last, last);
2574 if (lastRect != null) {
2575 inc = lastRect.y - visibleRect.y;
2576 if ((inc == 0)
2577 && (last < getModel().getSize() - 1)) {
2578 inc = lastRect.height;
2579 }
2580 }
2581 }
2582 }
2583 /* Scroll Up */
2584 else {
2585 int newFirst = locationToIndex(new Point(visibleRect.x,
2586 visibleRect.y - visibleRect.height));
2587 int first = getFirstVisibleIndex();
2588 if (newFirst != -1) {
2589 if (first == -1) {
2590 first = locationToIndex(visibleRect
2591 .getLocation());
2592 }
2593 Rectangle newFirstRect = getCellBounds(newFirst,
2594 newFirst);
2595 Rectangle firstRect = getCellBounds(first, first);
2596 if ((newFirstRect != null) && (firstRect != null)) {
2597 while ((newFirstRect.y + visibleRect.height < firstRect.y
2598 + firstRect.height)
2599 && (newFirstRect.y < firstRect.y)) {
2600 newFirst++;
2601 newFirstRect = getCellBounds(newFirst,
2602 newFirst);
2603 }
2604 inc = visibleRect.y - newFirstRect.y;
2605 if ((inc <= 0) && (newFirstRect.y > 0)) {
2606 newFirst--;
2607 newFirstRect = getCellBounds(newFirst,
2608 newFirst);
2609 if (newFirstRect != null) {
2610 inc = visibleRect.y - newFirstRect.y;
2611 }
2612 }
2613 }
2614 }
2615 }
2616 return inc;
2617 } else if (orientation == SwingConstants.HORIZONTAL
2618 && getLayoutOrientation() != JList.VERTICAL) {
2619 boolean leftToRight = getComponentOrientation()
2620 .isLeftToRight();
2621 int inc = visibleRect.width;
2622 /* Scroll Right (in ltr mode) or Scroll Left (in rtl mode) */
2623 if (direction > 0) {
2624 // position is upper right if ltr, or upper left otherwise
2625 int x = visibleRect.x
2626 + (leftToRight ? (visibleRect.width - 1) : 0);
2627 int last = locationToIndex(new Point(x, visibleRect.y));
2628
2629 if (last != -1) {
2630 Rectangle lastRect = getCellBounds(last, last);
2631 if (lastRect != null) {
2632 if (leftToRight) {
2633 inc = lastRect.x - visibleRect.x;
2634 } else {
2635 inc = visibleRect.x + visibleRect.width
2636 - (lastRect.x + lastRect.width);
2637 }
2638 if (inc < 0) {
2639 inc += lastRect.width;
2640 } else if ((inc == 0)
2641 && (last < getModel().getSize() - 1)) {
2642 inc = lastRect.width;
2643 }
2644 }
2645 }
2646 }
2647 /* Scroll Left (in ltr mode) or Scroll Right (in rtl mode) */
2648 else {
2649 // position is upper left corner of the visibleRect shifted
2650 // left by the visibleRect.width if ltr, or upper right shifted
2651 // right by the visibleRect.width otherwise
2652 int x = visibleRect.x
2653 + (leftToRight ? -visibleRect.width
2654 : visibleRect.width - 1
2655 + visibleRect.width);
2656 int first = locationToIndex(new Point(x, visibleRect.y));
2657
2658 if (first != -1) {
2659 Rectangle firstRect = getCellBounds(first, first);
2660 if (firstRect != null) {
2661 // the right of the first cell
2662 int firstRight = firstRect.x + firstRect.width;
2663
2664 if (leftToRight) {
2665 if ((firstRect.x < visibleRect.x
2666 - visibleRect.width)
2667 && (firstRight < visibleRect.x)) {
2668 inc = visibleRect.x - firstRight;
2669 } else {
2670 inc = visibleRect.x - firstRect.x;
2671 }
2672 } else {
2673 int visibleRight = visibleRect.x
2674 + visibleRect.width;
2675
2676 if ((firstRight > visibleRight
2677 + visibleRect.width)
2678 && (firstRect.x > visibleRight)) {
2679 inc = firstRect.x - visibleRight;
2680 } else {
2681 inc = firstRight - visibleRight;
2682 }
2683 }
2684 }
2685 }
2686 }
2687 return inc;
2688 }
2689 return visibleRect.width;
2690 }
2691
2692 /**
2693 * Returns {@code true} if this {@code JList} is displayed in a
2694 * {@code JViewport} and the viewport is wider than the list's
2695 * preferred width, or if the layout orientation is {@code HORIZONTAL_WRAP}
2696 * and {@code visibleRowCount <= 0}; otherwise returns {@code false}.
2697 * <p>
2698 * If {@code false}, then don't track the viewport's width. This allows
2699 * horizontal scrolling if the {@code JViewport} is itself embedded in a
2700 * {@code JScrollPane}.
2701 *
2702 * @return whether or not an enclosing viewport should force the list's
2703 * width to match its own
2704 * @see Scrollable#getScrollableTracksViewportWidth
2705 */
2706 public boolean getScrollableTracksViewportWidth() {
2707 if (getLayoutOrientation() == HORIZONTAL_WRAP
2708 && getVisibleRowCount() <= 0) {
2709 return true;
2710 }
2711 if (getParent() instanceof JViewport) {
2712 return (((JViewport) getParent()).getWidth() > getPreferredSize().width);
2713 }
2714 return false;
2715 }
2716
2717 /**
2718 * Returns {@code true} if this {@code JList} is displayed in a
2719 * {@code JViewport} and the viewport is taller than the list's
2720 * preferred height, or if the layout orientation is {@code VERTICAL_WRAP}
2721 * and {@code visibleRowCount <= 0}; otherwise returns {@code false}.
2722 * <p>
2723 * If {@code false}, then don't track the viewport's height. This allows
2724 * vertical scrolling if the {@code JViewport} is itself embedded in a
2725 * {@code JScrollPane}.
2726 *
2727 * @return whether or not an enclosing viewport should force the list's
2728 * height to match its own
2729 * @see Scrollable#getScrollableTracksViewportHeight
2730 */
2731 public boolean getScrollableTracksViewportHeight() {
2732 if (getLayoutOrientation() == VERTICAL_WRAP
2733 && getVisibleRowCount() <= 0) {
2734 return true;
2735 }
2736 if (getParent() instanceof JViewport) {
2737 return (((JViewport) getParent()).getHeight() > getPreferredSize().height);
2738 }
2739 return false;
2740 }
2741
2742 /*
2743 * See {@code readObject} and {@code writeObject} in {@code JComponent}
2744 * for more information about serialization in Swing.
2745 */
2746 private void writeObject(ObjectOutputStream s) throws IOException {
2747 s.defaultWriteObject();
2748 if (getUIClassID().equals(uiClassID)) {
2749 byte count = JComponent.getWriteObjCounter(this );
2750 JComponent.setWriteObjCounter(this , --count);
2751 if (count == 0 && ui != null) {
2752 ui.installUI(this );
2753 }
2754 }
2755 }
2756
2757 /**
2758 * Returns a {@code String} representation of this {@code JList}.
2759 * This method is intended to be used only for debugging purposes,
2760 * and the content and format of the returned {@code String} may vary
2761 * between implementations. The returned {@code String} may be empty,
2762 * but may not be {@code null}.
2763 *
2764 * @return a {@code String} representation of this {@code JList}.
2765 */
2766 protected String paramString() {
2767 String selectionForegroundString = (selectionForeground != null ? selectionForeground
2768 .toString()
2769 : "");
2770 String selectionBackgroundString = (selectionBackground != null ? selectionBackground
2771 .toString()
2772 : "");
2773
2774 return super .paramString() + ",fixedCellHeight="
2775 + fixedCellHeight + ",fixedCellWidth=" + fixedCellWidth
2776 + ",horizontalScrollIncrement="
2777 + horizontalScrollIncrement + ",selectionBackground="
2778 + selectionBackgroundString + ",selectionForeground="
2779 + selectionForegroundString + ",visibleRowCount="
2780 + visibleRowCount + ",layoutOrientation="
2781 + layoutOrientation;
2782 }
2783
2784 /**
2785 * --- Accessibility Support ---
2786 */
2787
2788 /**
2789 * Gets the {@code AccessibleContext} associated with this {@code JList}.
2790 * For {@code JList}, the {@code AccessibleContext} takes the form of an
2791 * {@code AccessibleJList}.
2792 * <p>
2793 * A new {@code AccessibleJList} instance is created if necessary.
2794 *
2795 * @return an {@code AccessibleJList} that serves as the
2796 * {@code AccessibleContext} of this {@code JList}
2797 */
2798 public AccessibleContext getAccessibleContext() {
2799 if (accessibleContext == null) {
2800 accessibleContext = new AccessibleJList();
2801 }
2802 return accessibleContext;
2803 }
2804
2805 /**
2806 * This class implements accessibility support for the
2807 * {@code JList} class. It provides an implementation of the
2808 * Java Accessibility API appropriate to list user-interface
2809 * elements.
2810 * <p>
2811 * <strong>Warning:</strong>
2812 * Serialized objects of this class will not be compatible with
2813 * future Swing releases. The current serialization support is
2814 * appropriate for short term storage or RMI between applications running
2815 * the same version of Swing. As of 1.4, support for long term storage
2816 * of all JavaBeans<sup><font size="-2">TM</font></sup>
2817 * has been added to the <code>java.beans</code> package.
2818 * Please see {@link java.beans.XMLEncoder}.
2819 */
2820 protected class AccessibleJList extends AccessibleJComponent
2821 implements AccessibleSelection, PropertyChangeListener,
2822 ListSelectionListener, ListDataListener {
2823
2824 int leadSelectionIndex;
2825
2826 public AccessibleJList() {
2827 super ();
2828 JList.this .addPropertyChangeListener(this );
2829 JList.this .getSelectionModel().addListSelectionListener(
2830 this );
2831 JList.this .getModel().addListDataListener(this );
2832 leadSelectionIndex = JList.this .getLeadSelectionIndex();
2833 }
2834
2835 /**
2836 * Property Change Listener change method. Used to track changes
2837 * to the DataModel and ListSelectionModel, in order to re-set
2838 * listeners to those for reporting changes there via the Accessibility
2839 * PropertyChange mechanism.
2840 *
2841 * @param e PropertyChangeEvent
2842 */
2843 public void propertyChange(PropertyChangeEvent e) {
2844 String name = e.getPropertyName();
2845 Object oldValue = e.getOldValue();
2846 Object newValue = e.getNewValue();
2847
2848 // re-set listData listeners
2849 if (name.compareTo("model") == 0) {
2850
2851 if (oldValue != null && oldValue instanceof ListModel) {
2852 ((ListModel) oldValue).removeListDataListener(this );
2853 }
2854 if (newValue != null && newValue instanceof ListModel) {
2855 ((ListModel) newValue).addListDataListener(this );
2856 }
2857
2858 // re-set listSelectionModel listeners
2859 } else if (name.compareTo("selectionModel") == 0) {
2860
2861 if (oldValue != null
2862 && oldValue instanceof ListSelectionModel) {
2863 ((ListSelectionModel) oldValue)
2864 .removeListSelectionListener(this );
2865 }
2866 if (newValue != null
2867 && newValue instanceof ListSelectionModel) {
2868 ((ListSelectionModel) newValue)
2869 .addListSelectionListener(this );
2870 }
2871
2872 firePropertyChange(
2873 AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2874 Boolean.valueOf(false), Boolean.valueOf(true));
2875 }
2876 }
2877
2878 /**
2879 * List Selection Listener value change method. Used to fire
2880 * the property change
2881 *
2882 * @param e ListSelectionEvent
2883 *
2884 */
2885 public void valueChanged(ListSelectionEvent e) {
2886 int oldLeadSelectionIndex = leadSelectionIndex;
2887 leadSelectionIndex = JList.this .getLeadSelectionIndex();
2888 if (oldLeadSelectionIndex != leadSelectionIndex) {
2889 Accessible oldLS, newLS;
2890 oldLS = (oldLeadSelectionIndex >= 0) ? getAccessibleChild(oldLeadSelectionIndex)
2891 : null;
2892 newLS = (leadSelectionIndex >= 0) ? getAccessibleChild(leadSelectionIndex)
2893 : null;
2894 firePropertyChange(
2895 AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
2896 oldLS, newLS);
2897 }
2898
2899 firePropertyChange(
2900 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2901 Boolean.valueOf(false), Boolean.valueOf(true));
2902 firePropertyChange(
2903 AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2904 Boolean.valueOf(false), Boolean.valueOf(true));
2905
2906 // Process the State changes for Multiselectable
2907 AccessibleStateSet s = getAccessibleStateSet();
2908 ListSelectionModel lsm = JList.this .getSelectionModel();
2909 if (lsm.getSelectionMode() != ListSelectionModel.SINGLE_SELECTION) {
2910 if (!s.contains(AccessibleState.MULTISELECTABLE)) {
2911 s.add(AccessibleState.MULTISELECTABLE);
2912 firePropertyChange(
2913 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2914 null, AccessibleState.MULTISELECTABLE);
2915 }
2916 } else {
2917 if (s.contains(AccessibleState.MULTISELECTABLE)) {
2918 s.remove(AccessibleState.MULTISELECTABLE);
2919 firePropertyChange(
2920 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2921 AccessibleState.MULTISELECTABLE, null);
2922 }
2923 }
2924 }
2925
2926 /**
2927 * List Data Listener interval added method. Used to fire the visible data property change
2928 *
2929 * @param e ListDataEvent
2930 *
2931 */
2932 public void intervalAdded(ListDataEvent e) {
2933 firePropertyChange(
2934 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2935 Boolean.valueOf(false), Boolean.valueOf(true));
2936 }
2937
2938 /**
2939 * List Data Listener interval removed method. Used to fire the visible data property change
2940 *
2941 * @param e ListDataEvent
2942 *
2943 */
2944 public void intervalRemoved(ListDataEvent e) {
2945 firePropertyChange(
2946 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2947 Boolean.valueOf(false), Boolean.valueOf(true));
2948 }
2949
2950 /**
2951 * List Data Listener contents changed method. Used to fire the visible data property change
2952 *
2953 * @param e ListDataEvent
2954 *
2955 */
2956 public void contentsChanged(ListDataEvent e) {
2957 firePropertyChange(
2958 AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2959 Boolean.valueOf(false), Boolean.valueOf(true));
2960 }
2961
2962 // AccessibleContext methods
2963
2964 /**
2965 * Get the state set of this object.
2966 *
2967 * @return an instance of AccessibleState containing the current state
2968 * of the object
2969 * @see AccessibleState
2970 */
2971 public AccessibleStateSet getAccessibleStateSet() {
2972 AccessibleStateSet states = super .getAccessibleStateSet();
2973 if (selectionModel.getSelectionMode() != ListSelectionModel.SINGLE_SELECTION) {
2974 states.add(AccessibleState.MULTISELECTABLE);
2975 }
2976 return states;
2977 }
2978
2979 /**
2980 * Get the role of this object.
2981 *
2982 * @return an instance of AccessibleRole describing the role of the
2983 * object
2984 * @see AccessibleRole
2985 */
2986 public AccessibleRole getAccessibleRole() {
2987 return AccessibleRole.LIST;
2988 }
2989
2990 /**
2991 * Returns the <code>Accessible</code> child contained at
2992 * the local coordinate <code>Point</code>, if one exists.
2993 * Otherwise returns <code>null</code>.
2994 *
2995 * @return the <code>Accessible</code> at the specified
2996 * location, if it exists
2997 */
2998 public Accessible getAccessibleAt(Point p) {
2999 int i = locationToIndex(p);
3000 if (i >= 0) {
3001 return new AccessibleJListChild(JList.this , i);
3002 } else {
3003 return null;
3004 }
3005 }
3006
3007 /**
3008 * Returns the number of accessible children in the object. If all
3009 * of the children of this object implement Accessible, than this
3010 * method should return the number of children of this object.
3011 *
3012 * @return the number of accessible children in the object.
3013 */
3014 public int getAccessibleChildrenCount() {
3015 return getModel().getSize();
3016 }
3017
3018 /**
3019 * Return the nth Accessible child of the object.
3020 *
3021 * @param i zero-based index of child
3022 * @return the nth Accessible child of the object
3023 */
3024 public Accessible getAccessibleChild(int i) {
3025 if (i >= getModel().getSize()) {
3026 return null;
3027 } else {
3028 return new AccessibleJListChild(JList.this , i);
3029 }
3030 }
3031
3032 /**
3033 * Get the AccessibleSelection associated with this object. In the
3034 * implementation of the Java Accessibility API for this class,
3035 * return this object, which is responsible for implementing the
3036 * AccessibleSelection interface on behalf of itself.
3037 *
3038 * @return this object
3039 */
3040 public AccessibleSelection getAccessibleSelection() {
3041 return this ;
3042 }
3043
3044 // AccessibleSelection methods
3045
3046 /**
3047 * Returns the number of items currently selected.
3048 * If no items are selected, the return value will be 0.
3049 *
3050 * @return the number of items currently selected.
3051 */
3052 public int getAccessibleSelectionCount() {
3053 return JList.this .getSelectedIndices().length;
3054 }
3055
3056 /**
3057 * Returns an Accessible representing the specified selected item
3058 * in the object. If there isn't a selection, or there are
3059 * fewer items selected than the integer passed in, the return
3060 * value will be <code>null</code>.
3061 *
3062 * @param i the zero-based index of selected items
3063 * @return an Accessible containing the selected item
3064 */
3065 public Accessible getAccessibleSelection(int i) {
3066 int len = getAccessibleSelectionCount();
3067 if (i < 0 || i >= len) {
3068 return null;
3069 } else {
3070 return getAccessibleChild(JList.this
3071 .getSelectedIndices()[i]);
3072 }
3073 }
3074
3075 /**
3076 * Returns true if the current child of this object is selected.
3077 *
3078 * @param i the zero-based index of the child in this Accessible
3079 * object.
3080 * @see AccessibleContext#getAccessibleChild
3081 */
3082 public boolean isAccessibleChildSelected(int i) {
3083 return isSelectedIndex(i);
3084 }
3085
3086 /**
3087 * Adds the specified selected item in the object to the object's
3088 * selection. If the object supports multiple selections,
3089 * the specified item is added to any existing selection, otherwise
3090 * it replaces any existing selection in the object. If the
3091 * specified item is already selected, this method has no effect.
3092 *
3093 * @param i the zero-based index of selectable items
3094 */
3095 public void addAccessibleSelection(int i) {
3096 JList.this .addSelectionInterval(i, i);
3097 }
3098
3099 /**
3100 * Removes the specified selected item in the object from the object's
3101 * selection. If the specified item isn't currently selected, this
3102 * method has no effect.
3103 *
3104 * @param i the zero-based index of selectable items
3105 */
3106 public void removeAccessibleSelection(int i) {
3107 JList.this .removeSelectionInterval(i, i);
3108 }
3109
3110 /**
3111 * Clears the selection in the object, so that nothing in the
3112 * object is selected.
3113 */
3114 public void clearAccessibleSelection() {
3115 JList.this .clearSelection();
3116 }
3117
3118 /**
3119 * Causes every selected item in the object to be selected
3120 * if the object supports multiple selections.
3121 */
3122 public void selectAllAccessibleSelection() {
3123 JList.this .addSelectionInterval(0,
3124 getAccessibleChildrenCount() - 1);
3125 }
3126
3127 /**
3128 * This class implements accessibility support appropriate
3129 * for list children.
3130 */
3131 protected class AccessibleJListChild extends AccessibleContext
3132 implements Accessible, AccessibleComponent {
3133 private JList parent = null;
3134 private int indexInParent;
3135 private Component component = null;
3136 private AccessibleContext accessibleContext = null;
3137 private ListModel listModel;
3138 private ListCellRenderer cellRenderer = null;
3139
3140 public AccessibleJListChild(JList parent, int indexInParent) {
3141 this .parent = parent;
3142 this .setAccessibleParent(parent);
3143 this .indexInParent = indexInParent;
3144 if (parent != null) {
3145 listModel = parent.getModel();
3146 cellRenderer = parent.getCellRenderer();
3147 }
3148 }
3149
3150 private Component getCurrentComponent() {
3151 return getComponentAtIndex(indexInParent);
3152 }
3153
3154 private AccessibleContext getCurrentAccessibleContext() {
3155 Component c = getComponentAtIndex(indexInParent);
3156 if (c instanceof Accessible) {
3157 return ((Accessible) c).getAccessibleContext();
3158 } else {
3159 return null;
3160 }
3161 }
3162
3163 private Component getComponentAtIndex(int index) {
3164 if (index < 0 || index >= listModel.getSize()) {
3165 return null;
3166 }
3167 if ((parent != null) && (listModel != null)
3168 && cellRenderer != null) {
3169 Object value = listModel.getElementAt(index);
3170 boolean isSelected = parent.isSelectedIndex(index);
3171 boolean isFocussed = parent.isFocusOwner()
3172 && (index == parent.getLeadSelectionIndex());
3173 return cellRenderer.getListCellRendererComponent(
3174 parent, value, index, isSelected,
3175 isFocussed);
3176 } else {
3177 return null;
3178 }
3179 }
3180
3181 // Accessible Methods
3182 /**
3183 * Get the AccessibleContext for this object. In the
3184 * implementation of the Java Accessibility API for this class,
3185 * returns this object, which is its own AccessibleContext.
3186 *
3187 * @return this object
3188 */
3189 public AccessibleContext getAccessibleContext() {
3190 return this ;
3191 }
3192
3193 // AccessibleContext methods
3194
3195 public String getAccessibleName() {
3196 AccessibleContext ac = getCurrentAccessibleContext();
3197 if (ac != null) {
3198 return ac.getAccessibleName();
3199 } else {
3200 return null;
3201 }
3202 }
3203
3204 public void setAccessibleName(String s) {
3205 AccessibleContext ac = getCurrentAccessibleContext();
3206 if (ac != null) {
3207 ac.setAccessibleName(s);
3208 }
3209 }
3210
3211 public String getAccessibleDescription() {
3212 AccessibleContext ac = getCurrentAccessibleContext();
3213 if (ac != null) {
3214 return ac.getAccessibleDescription();
3215 } else {
3216 return null;
3217 }
3218 }
3219
3220 public void setAccessibleDescription(String s) {
3221 AccessibleContext ac = getCurrentAccessibleContext();
3222 if (ac != null) {
3223 ac.setAccessibleDescription(s);
3224 }
3225 }
3226
3227 public AccessibleRole getAccessibleRole() {
3228 AccessibleContext ac = getCurrentAccessibleContext();
3229 if (ac != null) {
3230 return ac.getAccessibleRole();
3231 } else {
3232 return null;
3233 }
3234 }
3235
3236 public AccessibleStateSet getAccessibleStateSet() {
3237 AccessibleContext ac = getCurrentAccessibleContext();
3238 AccessibleStateSet s;
3239 if (ac != null) {
3240 s = ac.getAccessibleStateSet();
3241 } else {
3242 s = new AccessibleStateSet();
3243 }
3244
3245 s.add(AccessibleState.SELECTABLE);
3246 if (parent.isFocusOwner()
3247 && (indexInParent == parent
3248 .getLeadSelectionIndex())) {
3249 s.add(AccessibleState.ACTIVE);
3250 }
3251 if (parent.isSelectedIndex(indexInParent)) {
3252 s.add(AccessibleState.SELECTED);
3253 }
3254 if (this .isShowing()) {
3255 s.add(AccessibleState.SHOWING);
3256 } else if (s.contains(AccessibleState.SHOWING)) {
3257 s.remove(AccessibleState.SHOWING);
3258 }
3259 if (this .isVisible()) {
3260 s.add(AccessibleState.VISIBLE);
3261 } else if (s.contains(AccessibleState.VISIBLE)) {
3262 s.remove(AccessibleState.VISIBLE);
3263 }
3264 s.add(AccessibleState.TRANSIENT); // cell-rendered
3265 return s;
3266 }
3267
3268 public int getAccessibleIndexInParent() {
3269 return indexInParent;
3270 }
3271
3272 public int getAccessibleChildrenCount() {
3273 AccessibleContext ac = getCurrentAccessibleContext();
3274 if (ac != null) {
3275 return ac.getAccessibleChildrenCount();
3276 } else {
3277 return 0;
3278 }
3279 }
3280
3281 public Accessible getAccessibleChild(int i) {
3282 AccessibleContext ac = getCurrentAccessibleContext();
3283 if (ac != null) {
3284 Accessible accessibleChild = ac
3285 .getAccessibleChild(i);
3286 ac.setAccessibleParent(this );
3287 return accessibleChild;
3288 } else {
3289 return null;
3290 }
3291 }
3292
3293 public Locale getLocale() {
3294 AccessibleContext ac = getCurrentAccessibleContext();
3295 if (ac != null) {
3296 return ac.getLocale();
3297 } else {
3298 return null;
3299 }
3300 }
3301
3302 public void addPropertyChangeListener(
3303 PropertyChangeListener l) {
3304 AccessibleContext ac = getCurrentAccessibleContext();
3305 if (ac != null) {
3306 ac.addPropertyChangeListener(l);
3307 }
3308 }
3309
3310 public void removePropertyChangeListener(
3311 PropertyChangeListener l) {
3312 AccessibleContext ac = getCurrentAccessibleContext();
3313 if (ac != null) {
3314 ac.removePropertyChangeListener(l);
3315 }
3316 }
3317
3318 public AccessibleAction getAccessibleAction() {
3319 return getCurrentAccessibleContext()
3320 .getAccessibleAction();
3321 }
3322
3323 /**
3324 * Get the AccessibleComponent associated with this object. In the
3325 * implementation of the Java Accessibility API for this class,
3326 * return this object, which is responsible for implementing the
3327 * AccessibleComponent interface on behalf of itself.
3328 *
3329 * @return this object
3330 */
3331 public AccessibleComponent getAccessibleComponent() {
3332 return this ; // to override getBounds()
3333 }
3334
3335 public AccessibleSelection getAccessibleSelection() {
3336 return getCurrentAccessibleContext()
3337 .getAccessibleSelection();
3338 }
3339
3340 public AccessibleText getAccessibleText() {
3341 return getCurrentAccessibleContext()
3342 .getAccessibleText();
3343 }
3344
3345 public AccessibleValue getAccessibleValue() {
3346 return getCurrentAccessibleContext()
3347 .getAccessibleValue();
3348 }
3349
3350 // AccessibleComponent methods
3351
3352 public Color getBackground() {
3353 AccessibleContext ac = getCurrentAccessibleContext();
3354 if (ac instanceof AccessibleComponent) {
3355 return ((AccessibleComponent) ac).getBackground();
3356 } else {
3357 Component c = getCurrentComponent();
3358 if (c != null) {
3359 return c.getBackground();
3360 } else {
3361 return null;
3362 }
3363 }
3364 }
3365
3366 public void setBackground(Color c) {
3367 AccessibleContext ac = getCurrentAccessibleContext();
3368 if (ac instanceof AccessibleComponent) {
3369 ((AccessibleComponent) ac).setBackground(c);
3370 } else {
3371 Component cp = getCurrentComponent();
3372 if (cp != null) {
3373 cp.setBackground(c);
3374 }
3375 }
3376 }
3377
3378 public Color getForeground() {
3379 AccessibleContext ac = getCurrentAccessibleContext();
3380 if (ac instanceof AccessibleComponent) {
3381 return ((AccessibleComponent) ac).getForeground();
3382 } else {
3383 Component c = getCurrentComponent();
3384 if (c != null) {
3385 return c.getForeground();
3386 } else {
3387 return null;
3388 }
3389 }
3390 }
3391
3392 public void setForeground(Color c) {
3393 AccessibleContext ac = getCurrentAccessibleContext();
3394 if (ac instanceof AccessibleComponent) {
3395 ((AccessibleComponent) ac).setForeground(c);
3396 } else {
3397 Component cp = getCurrentComponent();
3398 if (cp != null) {
3399 cp.setForeground(c);
3400 }
3401 }
3402 }
3403
3404 public Cursor getCursor() {
3405 AccessibleContext ac = getCurrentAccessibleContext();
3406 if (ac instanceof AccessibleComponent) {
3407 return ((AccessibleComponent) ac).getCursor();
3408 } else {
3409 Component c = getCurrentComponent();
3410 if (c != null) {
3411 return c.getCursor();
3412 } else {
3413 Accessible ap = getAccessibleParent();
3414 if (ap instanceof AccessibleComponent) {
3415 return ((AccessibleComponent) ap)
3416 .getCursor();
3417 } else {
3418 return null;
3419 }
3420 }
3421 }
3422 }
3423
3424 public void setCursor(Cursor c) {
3425 AccessibleContext ac = getCurrentAccessibleContext();
3426 if (ac instanceof AccessibleComponent) {
3427 ((AccessibleComponent) ac).setCursor(c);
3428 } else {
3429 Component cp = getCurrentComponent();
3430 if (cp != null) {
3431 cp.setCursor(c);
3432 }
3433 }
3434 }
3435
3436 public Font getFont() {
3437 AccessibleContext ac = getCurrentAccessibleContext();
3438 if (ac instanceof AccessibleComponent) {
3439 return ((AccessibleComponent) ac).getFont();
3440 } else {
3441 Component c = getCurrentComponent();
3442 if (c != null) {
3443 return c.getFont();
3444 } else {
3445 return null;
3446 }
3447 }
3448 }
3449
3450 public void setFont(Font f) {
3451 AccessibleContext ac = getCurrentAccessibleContext();
3452 if (ac instanceof AccessibleComponent) {
3453 ((AccessibleComponent) ac).setFont(f);
3454 } else {
3455 Component c = getCurrentComponent();
3456 if (c != null) {
3457 c.setFont(f);
3458 }
3459 }
3460 }
3461
3462 public FontMetrics getFontMetrics(Font f) {
3463 AccessibleContext ac = getCurrentAccessibleContext();
3464 if (ac instanceof AccessibleComponent) {
3465 return ((AccessibleComponent) ac).getFontMetrics(f);
3466 } else {
3467 Component c = getCurrentComponent();
3468 if (c != null) {
3469 return c.getFontMetrics(f);
3470 } else {
3471 return null;
3472 }
3473 }
3474 }
3475
3476 public boolean isEnabled() {
3477 AccessibleContext ac = getCurrentAccessibleContext();
3478 if (ac instanceof AccessibleComponent) {
3479 return ((AccessibleComponent) ac).isEnabled();
3480 } else {
3481 Component c = getCurrentComponent();
3482 if (c != null) {
3483 return c.isEnabled();
3484 } else {
3485 return false;
3486 }
3487 }
3488 }
3489
3490 public void setEnabled(boolean b) {
3491 AccessibleContext ac = getCurrentAccessibleContext();
3492 if (ac instanceof AccessibleComponent) {
3493 ((AccessibleComponent) ac).setEnabled(b);
3494 } else {
3495 Component c = getCurrentComponent();
3496 if (c != null) {
3497 c.setEnabled(b);
3498 }
3499 }
3500 }
3501
3502 public boolean isVisible() {
3503 int fi = parent.getFirstVisibleIndex();
3504 int li = parent.getLastVisibleIndex();
3505 // The UI incorrectly returns a -1 for the last
3506 // visible index if the list is smaller than the
3507 // viewport size.
3508 if (li == -1) {
3509 li = parent.getModel().getSize() - 1;
3510 }
3511 return ((indexInParent >= fi) && (indexInParent <= li));
3512 }
3513
3514 public void setVisible(boolean b) {
3515 }
3516
3517 public boolean isShowing() {
3518 return (parent.isShowing() && isVisible());
3519 }
3520
3521 public boolean contains(Point p) {
3522 AccessibleContext ac = getCurrentAccessibleContext();
3523 if (ac instanceof AccessibleComponent) {
3524 Rectangle r = ((AccessibleComponent) ac)
3525 .getBounds();
3526 return r.contains(p);
3527 } else {
3528 Component c = getCurrentComponent();
3529 if (c != null) {
3530 Rectangle r = c.getBounds();
3531 return r.contains(p);
3532 } else {
3533 return getBounds().contains(p);
3534 }
3535 }
3536 }
3537
3538 public Point getLocationOnScreen() {
3539 if (parent != null) {
3540 Point listLocation = parent.getLocationOnScreen();
3541 Point componentLocation = parent
3542 .indexToLocation(indexInParent);
3543 if (componentLocation != null) {
3544 componentLocation.translate(listLocation.x,
3545 listLocation.y);
3546 return componentLocation;
3547 } else {
3548 return null;
3549 }
3550 } else {
3551 return null;
3552 }
3553 }
3554
3555 public Point getLocation() {
3556 if (parent != null) {
3557 return parent.indexToLocation(indexInParent);
3558 } else {
3559 return null;
3560 }
3561 }
3562
3563 public void setLocation(Point p) {
3564 if ((parent != null) && (parent.contains(p))) {
3565 ensureIndexIsVisible(indexInParent);
3566 }
3567 }
3568
3569 public Rectangle getBounds() {
3570 if (parent != null) {
3571 return parent.getCellBounds(indexInParent,
3572 indexInParent);
3573 } else {
3574 return null;
3575 }
3576 }
3577
3578 public void setBounds(Rectangle r) {
3579 AccessibleContext ac = getCurrentAccessibleContext();
3580 if (ac instanceof AccessibleComponent) {
3581 ((AccessibleComponent) ac).setBounds(r);
3582 }
3583 }
3584
3585 public Dimension getSize() {
3586 Rectangle cellBounds = this .getBounds();
3587 if (cellBounds != null) {
3588 return cellBounds.getSize();
3589 } else {
3590 return null;
3591 }
3592 }
3593
3594 public void setSize(Dimension d) {
3595 AccessibleContext ac = getCurrentAccessibleContext();
3596 if (ac instanceof AccessibleComponent) {
3597 ((AccessibleComponent) ac).setSize(d);
3598 } else {
3599 Component c = getCurrentComponent();
3600 if (c != null) {
3601 c.setSize(d);
3602 }
3603 }
3604 }
3605
3606 public Accessible getAccessibleAt(Point p) {
3607 AccessibleContext ac = getCurrentAccessibleContext();
3608 if (ac instanceof AccessibleComponent) {
3609 return ((AccessibleComponent) ac)
3610 .getAccessibleAt(p);
3611 } else {
3612 return null;
3613 }
3614 }
3615
3616 public boolean isFocusTraversable() {
3617 AccessibleContext ac = getCurrentAccessibleContext();
3618 if (ac instanceof AccessibleComponent) {
3619 return ((AccessibleComponent) ac)
3620 .isFocusTraversable();
3621 } else {
3622 Component c = getCurrentComponent();
3623 if (c != null) {
3624 return c.isFocusTraversable();
3625 } else {
3626 return false;
3627 }
3628 }
3629 }
3630
3631 public void requestFocus() {
3632 AccessibleContext ac = getCurrentAccessibleContext();
3633 if (ac instanceof AccessibleComponent) {
3634 ((AccessibleComponent) ac).requestFocus();
3635 } else {
3636 Component c = getCurrentComponent();
3637 if (c != null) {
3638 c.requestFocus();
3639 }
3640 }
3641 }
3642
3643 public void addFocusListener(FocusListener l) {
3644 AccessibleContext ac = getCurrentAccessibleContext();
3645 if (ac instanceof AccessibleComponent) {
3646 ((AccessibleComponent) ac).addFocusListener(l);
3647 } else {
3648 Component c = getCurrentComponent();
3649 if (c != null) {
3650 c.addFocusListener(l);
3651 }
3652 }
3653 }
3654
3655 public void removeFocusListener(FocusListener l) {
3656 AccessibleContext ac = getCurrentAccessibleContext();
3657 if (ac instanceof AccessibleComponent) {
3658 ((AccessibleComponent) ac).removeFocusListener(l);
3659 } else {
3660 Component c = getCurrentComponent();
3661 if (c != null) {
3662 c.removeFocusListener(l);
3663 }
3664 }
3665 }
3666
3667 // TIGER - 4733624
3668 /**
3669 * Returns the icon for the element renderer, as the only item
3670 * of an array of <code>AccessibleIcon</code>s or a <code>null</code> array
3671 * if the renderer component contains no icons.
3672 *
3673 * @return an array containing the accessible icon
3674 * or a <code>null</code> array if none
3675 * @since 1.3
3676 */
3677 public AccessibleIcon[] getAccessibleIcon() {
3678 AccessibleContext ac = getCurrentAccessibleContext();
3679 if (ac != null) {
3680 return ac.getAccessibleIcon();
3681 } else {
3682 return null;
3683 }
3684 }
3685 } // inner class AccessibleJListChild
3686 } // inner class AccessibleJList
3687 }
|