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