blob: 8446d05a88b0e2651c98a21c04f139d4af4b001a [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2007 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
26package javax.swing;
27
28import java.awt.event.*;
29import java.awt.*;
30
31import java.util.Vector;
32import java.util.Locale;
33
34import java.beans.*;
35
36import javax.swing.event.*;
37import javax.accessibility.*;
38import javax.swing.plaf.*;
39import javax.swing.text.Position;
40
41import java.io.ObjectOutputStream;
42import java.io.ObjectInputStream;
43import java.io.IOException;
44import java.io.Serializable;
45
46import sun.swing.SwingUtilities2;
47import sun.swing.SwingUtilities2.Section;
48import static sun.swing.SwingUtilities2.Section.*;
49
50
51/**
52 * A component that displays a list of objects and allows the user to select
53 * one or more items. A separate model, {@code ListModel}, maintains the
54 * contents of the list.
55 * <p>
56 * It's easy to display an array or Vector of objects, using the {@code JList}
57 * constructor that automatically builds a read-only {@code ListModel} instance
58 * for you:
59 * <pre>
60 * // Create a JList that displays strings from an array
61 *
62 * String[] data = {"one", "two", "three", "four"};
63 * JList myList = new JList(data);
64 *
65 * // Create a JList that displays the superclasses of JList.class, by
66 * // creating it with a Vector populated with this data
67 *
68 * Vector superClasses = new Vector();
69 * Class rootClass = javax.swing.JList.class;
70 * for(Class cls = rootClass; cls != null; cls = cls.getSuperclass()) {
71 * superClasses.addElement(cls);
72 * }
73 * JList myList = new JList(superClasses);
74 *
75 * // The automatically created model is stored in JList's "model"
76 * // property, which you can retrieve
77 *
78 * ListModel model = myList.getModel();
79 * for(int i = 0; i < model.getSize(); i++) {
80 * System.out.println(model.getElementAt(i));
81 * }
82 * </pre>
83 * <p>
84 * A {@code ListModel} can be supplied directly to a {@code JList} by way of a
85 * constructor or the {@code setModel} method. The contents need not be static -
86 * the number of items, and the values of items can change over time. A correct
87 * {@code ListModel} implementation notifies the set of
88 * {@code javax.swing.event.ListDataListener}s that have been added to it, each
89 * time a change occurs. These changes are characterized by a
90 * {@code javax.swing.event.ListDataEvent}, which identifies the range of list
91 * indices that have been modified, added, or removed. {@code JList}'s
92 * {@code ListUI} is responsible for keeping the visual representation up to
93 * date with changes, by listening to the model.
94 * <p>
95 * Simple, dynamic-content, {@code JList} applications can use the
96 * {@code DefaultListModel} class to maintain list elements. This class
97 * implements the {@code ListModel} interface and also provides a
98 * <code>java.util.Vector</code>-like API. Applications that need a more
99 * custom <code>ListModel</code> implementation may instead wish to subclass
100 * {@code AbstractListModel}, which provides basic support for managing and
101 * notifying listeners. For example, a read-only implementation of
102 * {@code AbstractListModel}:
103 * <pre>
104 * // This list model has about 2^16 elements. Enjoy scrolling.
105 *
106 * ListModel bigData = new AbstractListModel() {
107 * public int getSize() { return Short.MAX_VALUE; }
108 * public Object getElementAt(int index) { return "Index " + index; }
109 * };
110 * </pre>
111 * <p>
112 * The selection state of a {@code JList} is managed by another separate
113 * model, an instance of {@code ListSelectionModel}. {@code JList} is
114 * initialized with a selection model on construction, and also contains
115 * methods to query or set this selection model. Additionally, {@code JList}
116 * provides convenient methods for easily managing the selection. These methods,
117 * such as {@code setSelectedIndex} and {@code getSelectedValue}, are cover
118 * methods that take care of the details of interacting with the selection
119 * model. By default, {@code JList}'s selection model is configured to allow any
120 * combination of items to be selected at a time; selection mode
121 * {@code MULTIPLE_INTERVAL_SELECTION}. The selection mode can be changed
122 * on the selection model directly, or via {@code JList}'s cover method.
123 * Responsibility for updating the selection model in response to user gestures
124 * lies with the list's {@code ListUI}.
125 * <p>
126 * A correct {@code ListSelectionModel} implementation notifies the set of
127 * {@code javax.swing.event.ListSelectionListener}s that have been added to it
128 * each time a change to the selection occurs. These changes are characterized
129 * by a {@code javax.swing.event.ListSelectionEvent}, which identifies the range
130 * of the selection change.
131 * <p>
132 * The preferred way to listen for changes in list selection is to add
133 * {@code ListSelectionListener}s directly to the {@code JList}. {@code JList}
134 * then takes care of listening to the the selection model and notifying your
135 * listeners of change.
136 * <p>
137 * Responsibility for listening to selection changes in order to keep the list's
138 * visual representation up to date lies with the list's {@code ListUI}.
139 * <p>
140 * <a name="renderer">
141 * Painting of cells in a {@code JList} is handled by a delegate called a
142 * cell renderer, installed on the list as the {@code cellRenderer} property.
143 * The renderer provides a {@code java.awt.Component} that is used
144 * like a "rubber stamp" to paint the cells. Each time a cell needs to be
145 * painted, the list's {@code ListUI} asks the cell renderer for the component,
146 * moves it into place, and has it paint the contents of the cell by way of its
147 * {@code paint} method. A default cell renderer, which uses a {@code JLabel}
148 * component to render, is installed by the lists's {@code ListUI}. You can
149 * substitute your own renderer using code like this:
150 * <pre>
151 * // Display an icon and a string for each object in the list.
152 *
153 * class MyCellRenderer extends JLabel implements ListCellRenderer {
154 * final static ImageIcon longIcon = new ImageIcon("long.gif");
155 * final static ImageIcon shortIcon = new ImageIcon("short.gif");
156 *
157 * // This is the only method defined by ListCellRenderer.
158 * // We just reconfigure the JLabel each time we're called.
159 *
160 * public Component getListCellRendererComponent(
161 * JList list, // the list
162 * Object value, // value to display
163 * int index, // cell index
164 * boolean isSelected, // is the cell selected
165 * boolean cellHasFocus) // does the cell have focus
166 * {
167 * String s = value.toString();
168 * setText(s);
169 * setIcon((s.length() > 10) ? longIcon : shortIcon);
170 * if (isSelected) {
171 * setBackground(list.getSelectionBackground());
172 * setForeground(list.getSelectionForeground());
173 * } else {
174 * setBackground(list.getBackground());
175 * setForeground(list.getForeground());
176 * }
177 * setEnabled(list.isEnabled());
178 * setFont(list.getFont());
179 * setOpaque(true);
180 * return this;
181 * }
182 * }
183 *
184 * myList.setCellRenderer(new MyCellRenderer());
185 * </pre>
186 * <p>
187 * Another job for the cell renderer is in helping to determine sizing
188 * information for the list. By default, the list's {@code ListUI} determines
189 * the size of cells by asking the cell renderer for its preferred
190 * size for each list item. This can be expensive for large lists of items.
191 * To avoid these calculations, you can set a {@code fixedCellWidth} and
192 * {@code fixedCellHeight} on the list, or have these values calculated
193 * automatically based on a single prototype value:
194 * <a name="prototype_example">
195 * <pre>
196 * JList bigDataList = new JList(bigData);
197 *
198 * // We don't want the JList implementation to compute the width
199 * // or height of all of the list cells, so we give it a string
200 * // that's as big as we'll need for any cell. It uses this to
201 * // compute values for the fixedCellWidth and fixedCellHeight
202 * // properties.
203 *
204 * bigDataList.setPrototypeCellValue("Index 1234567890");
205 * </pre>
206 * <p>
207 * {@code JList} doesn't implement scrolling directly. To create a list that
208 * scrolls, make it the viewport view of a {@code JScrollPane}. For example:
209 * <pre>
210 * JScrollPane scrollPane = new JScrollPane(myList);
211 *
212 * // Or in two steps:
213 * JScrollPane scrollPane = new JScrollPane();
214 * scrollPane.getViewport().setView(myList);
215 * </pre>
216 * <p>
217 * {@code JList} doesn't provide any special handling of double or triple
218 * (or N) mouse clicks, but it's easy to add a {@code MouseListener} if you
219 * wish to take action on these events. Use the {@code locationToIndex}
220 * method to determine what cell was clicked. For example:
221 * <pre>
222 * MouseListener mouseListener = new MouseAdapter() {
223 * public void mouseClicked(MouseEvent e) {
224 * if (e.getClickCount() == 2) {
225 * int index = list.locationToIndex(e.getPoint());
226 * System.out.println("Double clicked on Item " + index);
227 * }
228 * }
229 * };
230 * list.addMouseListener(mouseListener);
231 * </pre>
232 * <p>
233 * <strong>Warning:</strong> Swing is not thread safe. For more
234 * information see <a
235 * href="package-summary.html#threading">Swing's Threading
236 * Policy</a>.
237 * <p>
238 * <strong>Warning:</strong>
239 * Serialized objects of this class will not be compatible with
240 * future Swing releases. The current serialization support is
241 * appropriate for short term storage or RMI between applications running
242 * the same version of Swing. As of 1.4, support for long term storage
243 * of all JavaBeans<sup><font size="-2">TM</font></sup>
244 * has been added to the <code>java.beans</code> package.
245 * Please see {@link java.beans.XMLEncoder}.
246 * <p>
247 * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/list.html">How to Use Lists</a>
248 * in <a href="http://java.sun.com/Series/Tutorial/index.html"><em>The Java Tutorial</em></a>
249 * for further documentation.
250 * Also see the article <a href="http://java.sun.com/products/jfc/tsc/tech_topics/jlist_1/jlist.html">Advanced JList Programming</a>
251 * in <a href="http://java.sun.com/products/jfc/tsc"><em>The Swing Connection</em></a>.
252 * <p>
253 * @see ListModel
254 * @see AbstractListModel
255 * @see DefaultListModel
256 * @see ListSelectionModel
257 * @see DefaultListSelectionModel
258 * @see ListCellRenderer
259 * @see DefaultListCellRenderer
260 *
261 * @beaninfo
262 * attribute: isContainer false
263 * description: A component which allows for the selection of one or more objects from a list.
264 *
265 * @author Hans Muller
266 */
267public class JList extends JComponent implements Scrollable, Accessible
268{
269 /**
270 * @see #getUIClassID
271 * @see #readObject
272 */
273 private static final String uiClassID = "ListUI";
274
275 /**
276 * Indicates a vertical layout of cells, in a single column;
277 * the default layout.
278 * @see #setLayoutOrientation
279 * @since 1.4
280 */
281 public static final int VERTICAL = 0;
282
283 /**
284 * Indicates a "newspaper style" layout with cells flowing vertically
285 * then horizontally.
286 * @see #setLayoutOrientation
287 * @since 1.4
288 */
289 public static final int VERTICAL_WRAP = 1;
290
291 /**
292 * Indicates a "newspaper style" layout with cells flowing horizontally
293 * then vertically.
294 * @see #setLayoutOrientation
295 * @since 1.4
296 */
297 public static final int HORIZONTAL_WRAP = 2;
298
299 private int fixedCellWidth = -1;
300 private int fixedCellHeight = -1;
301 private int horizontalScrollIncrement = -1;
302 private Object prototypeCellValue;
303 private int visibleRowCount = 8;
304 private Color selectionForeground;
305 private Color selectionBackground;
306 private boolean dragEnabled;
307
308 private ListSelectionModel selectionModel;
309 private ListModel dataModel;
310 private ListCellRenderer cellRenderer;
311 private ListSelectionListener selectionListener;
312
313 /**
314 * How to lay out the cells; defaults to <code>VERTICAL</code>.
315 */
316 private int layoutOrientation;
317
318 /**
319 * The drop mode for this component.
320 */
321 private DropMode dropMode = DropMode.USE_SELECTION;
322
323 /**
324 * The drop location.
325 */
326 private transient DropLocation dropLocation;
327
328 /**
329 * A subclass of <code>TransferHandler.DropLocation</code> representing
330 * a drop location for a <code>JList</code>.
331 *
332 * @see #getDropLocation
333 * @since 1.6
334 */
335 public static final class DropLocation extends TransferHandler.DropLocation {
336 private final int index;
337 private final boolean isInsert;
338
339 private DropLocation(Point p, int index, boolean isInsert) {
340 super(p);
341 this.index = index;
342 this.isInsert = isInsert;
343 }
344
345 /**
346 * Returns the index where dropped data should be placed in the
347 * list. Interpretation of the value depends on the drop mode set on
348 * the associated component. If the drop mode is either
349 * <code>DropMode.USE_SELECTION</code> or <code>DropMode.ON</code>,
350 * the return value is an index of a row in the list. If the drop mode is
351 * <code>DropMode.INSERT</code>, the return value refers to the index
352 * where the data should be inserted. If the drop mode is
353 * <code>DropMode.ON_OR_INSERT</code>, the value of
354 * <code>isInsert()</code> indicates whether the index is an index
355 * of a row, or an insert index.
356 * <p>
357 * <code>-1</code> indicates that the drop occurred over empty space,
358 * and no index could be calculated.
359 *
360 * @return the drop index
361 */
362 public int getIndex() {
363 return index;
364 }
365
366 /**
367 * Returns whether or not this location represents an insert
368 * location.
369 *
370 * @return whether or not this is an insert location
371 */
372 public boolean isInsert() {
373 return isInsert;
374 }
375
376 /**
377 * Returns a string representation of this drop location.
378 * This method is intended to be used for debugging purposes,
379 * and the content and format of the returned string may vary
380 * between implementations.
381 *
382 * @return a string representation of this drop location
383 */
384 public String toString() {
385 return getClass().getName()
386 + "[dropPoint=" + getDropPoint() + ","
387 + "index=" + index + ","
388 + "insert=" + isInsert + "]";
389 }
390 }
391
392 /**
393 * Constructs a {@code JList} that displays elements from the specified,
394 * {@code non-null}, model. All {@code JList} constructors delegate to
395 * this one.
396 * <p>
397 * This constructor registers the list with the {@code ToolTipManager},
398 * allowing for tooltips to be provided by the cell renderers.
399 *
400 * @param dataModel the model for the list
401 * @exception IllegalArgumentException if the model is {@code null}
402 */
403 public JList(ListModel dataModel)
404 {
405 if (dataModel == null) {
406 throw new IllegalArgumentException("dataModel must be non null");
407 }
408
409 // Register with the ToolTipManager so that tooltips from the
410 // renderer show through.
411 ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
412 toolTipManager.registerComponent(this);
413
414 layoutOrientation = VERTICAL;
415
416 this.dataModel = dataModel;
417 selectionModel = createSelectionModel();
418 setAutoscrolls(true);
419 setOpaque(true);
420 updateUI();
421 }
422
423
424 /**
425 * Constructs a <code>JList</code> that displays the elements in
426 * the specified array. This constructor creates a read-only model
427 * for the given array, and then delegates to the constructor that
428 * takes a {@code ListModel}.
429 * <p>
430 * Attempts to pass a {@code null} value to this method results in
431 * undefined behavior and, most likely, exceptions. The created model
432 * references the given array directly. Attempts to modify the array
433 * after constructing the list results in undefined behavior.
434 *
435 * @param listData the array of Objects to be loaded into the data model,
436 * {@code non-null}
437 */
438 public JList(final Object[] listData)
439 {
440 this (
441 new AbstractListModel() {
442 public int getSize() { return listData.length; }
443 public Object getElementAt(int i) { return listData[i]; }
444 }
445 );
446 }
447
448
449 /**
450 * Constructs a <code>JList</code> that displays the elements in
451 * the specified <code>Vector</code>. This constructor creates a read-only
452 * model for the given {@code Vector}, and then delegates to the constructor
453 * that takes a {@code ListModel}.
454 * <p>
455 * Attempts to pass a {@code null} value to this method results in
456 * undefined behavior and, most likely, exceptions. The created model
457 * references the given {@code Vector} directly. Attempts to modify the
458 * {@code Vector} after constructing the list results in undefined behavior.
459 *
460 * @param listData the <code>Vector</code> to be loaded into the
461 * data model, {@code non-null}
462 */
463 public JList(final Vector<?> listData) {
464 this (
465 new AbstractListModel() {
466 public int getSize() { return listData.size(); }
467 public Object getElementAt(int i) { return listData.elementAt(i); }
468 }
469 );
470 }
471
472
473 /**
474 * Constructs a <code>JList</code> with an empty, read-only, model.
475 */
476 public JList() {
477 this (
478 new AbstractListModel() {
479 public int getSize() { return 0; }
480 public Object getElementAt(int i) { return "No Data Model"; }
481 }
482 );
483 }
484
485
486 /**
487 * Returns the {@code ListUI}, the look and feel object that
488 * renders this component.
489 *
490 * @return the <code>ListUI</code> object that renders this component
491 */
492 public ListUI getUI() {
493 return (ListUI)ui;
494 }
495
496
497 /**
498 * Sets the {@code ListUI}, the look and feel object that
499 * renders this component.
500 *
501 * @param ui the <code>ListUI</code> object
502 * @see UIDefaults#getUI
503 * @beaninfo
504 * bound: true
505 * hidden: true
506 * attribute: visualUpdate true
507 * description: The UI object that implements the Component's LookAndFeel.
508 */
509 public void setUI(ListUI ui) {
510 super.setUI(ui);
511 }
512
513
514 /**
515 * Resets the {@code ListUI} property by setting it to the value provided
516 * by the current look and feel. If the current cell renderer was installed
517 * by the developer (rather than the look and feel itself), this also causes
518 * the cell renderer and its children to be updated, by calling
519 * {@code SwingUtilities.updateComponentTreeUI} on it.
520 *
521 * @see UIManager#getUI
522 * @see SwingUtilities#updateComponentTreeUI
523 */
524 public void updateUI() {
525 setUI((ListUI)UIManager.getUI(this));
526
527 ListCellRenderer renderer = getCellRenderer();
528 if (renderer instanceof Component) {
529 SwingUtilities.updateComponentTreeUI((Component)renderer);
530 }
531 }
532
533
534 /**
535 * Returns {@code "ListUI"}, the <code>UIDefaults</code> key used to look
536 * up the name of the {@code javax.swing.plaf.ListUI} class that defines
537 * the look and feel for this component.
538 *
539 * @return the string "ListUI"
540 * @see JComponent#getUIClassID
541 * @see UIDefaults#getUI
542 */
543 public String getUIClassID() {
544 return uiClassID;
545 }
546
547
548 /* -----private-----
549 * This method is called by setPrototypeCellValue and setCellRenderer
550 * to update the fixedCellWidth and fixedCellHeight properties from the
551 * current value of prototypeCellValue (if it's non null).
552 * <p>
553 * This method sets fixedCellWidth and fixedCellHeight but does <b>not</b>
554 * generate PropertyChangeEvents for them.
555 *
556 * @see #setPrototypeCellValue
557 * @see #setCellRenderer
558 */
559 private void updateFixedCellSize()
560 {
561 ListCellRenderer cr = getCellRenderer();
562 Object value = getPrototypeCellValue();
563
564 if ((cr != null) && (value != null)) {
565 Component c = cr.getListCellRendererComponent(this, value, 0, false, false);
566
567 /* The ListUI implementation will add Component c to its private
568 * CellRendererPane however we can't assume that's already
569 * been done here. So we temporarily set the one "inherited"
570 * property that may affect the renderer components preferred size:
571 * its font.
572 */
573 Font f = c.getFont();
574 c.setFont(getFont());
575
576 Dimension d = c.getPreferredSize();
577 fixedCellWidth = d.width;
578 fixedCellHeight = d.height;
579
580 c.setFont(f);
581 }
582 }
583
584
585 /**
586 * Returns the "prototypical" cell value -- a value used to calculate a
587 * fixed width and height for cells. This can be {@code null} if there
588 * is no such value.
589 *
590 * @return the value of the {@code prototypeCellValue} property
591 * @see #setPrototypeCellValue
592 */
593 public Object getPrototypeCellValue() {
594 return prototypeCellValue;
595 }
596
597 /**
598 * Sets the {@code prototypeCellValue} property, and then (if the new value
599 * is {@code non-null}), computes the {@code fixedCellWidth} and
600 * {@code fixedCellHeight} properties by requesting the cell renderer
601 * component for the given value (and index 0) from the cell renderer, and
602 * using that component's preferred size.
603 * <p>
604 * This method is useful when the list is too long to allow the
605 * {@code ListUI} to compute the width/height of each cell, and there is a
606 * single cell value that is known to occupy as much space as any of the
607 * others, a so-called prototype.
608 * <p>
609 * While all three of the {@code prototypeCellValue},
610 * {@code fixedCellHeight}, and {@code fixedCellWidth} properties may be
611 * modified by this method, {@code PropertyChangeEvent} notifications are
612 * only sent when the {@code prototypeCellValue} property changes.
613 * <p>
614 * To see an example which sets this property, see the
615 * <a href="#prototype_example">class description</a> above.
616 * <p>
617 * The default value of this property is <code>null</code>.
618 * <p>
619 * This is a JavaBeans bound property.
620 *
621 * @param prototypeCellValue the value on which to base
622 * <code>fixedCellWidth</code> and
623 * <code>fixedCellHeight</code>
624 * @see #getPrototypeCellValue
625 * @see #setFixedCellWidth
626 * @see #setFixedCellHeight
627 * @see JComponent#addPropertyChangeListener
628 * @beaninfo
629 * bound: true
630 * attribute: visualUpdate true
631 * description: The cell prototype value, used to compute cell width and height.
632 */
633 public void setPrototypeCellValue(Object prototypeCellValue) {
634 Object oldValue = this.prototypeCellValue;
635 this.prototypeCellValue = prototypeCellValue;
636
637 /* If the prototypeCellValue has changed and is non-null,
638 * then recompute fixedCellWidth and fixedCellHeight.
639 */
640
641 if ((prototypeCellValue != null) && !prototypeCellValue.equals(oldValue)) {
642 updateFixedCellSize();
643 }
644
645 firePropertyChange("prototypeCellValue", oldValue, prototypeCellValue);
646 }
647
648
649 /**
650 * Returns the value of the {@code fixedCellWidth} property.
651 *
652 * @return the fixed cell width
653 * @see #setFixedCellWidth
654 */
655 public int getFixedCellWidth() {
656 return fixedCellWidth;
657 }
658
659 /**
660 * Sets a fixed value to be used for the width of every cell in the list.
661 * If {@code width} is -1, cell widths are computed in the {@code ListUI}
662 * by applying <code>getPreferredSize</code> to the cell renderer component
663 * for each list element.
664 * <p>
665 * The default value of this property is {@code -1}.
666 * <p>
667 * This is a JavaBeans bound property.
668 *
669 * @param width the width to be used for all cells in the list
670 * @see #setPrototypeCellValue
671 * @see #setFixedCellWidth
672 * @see JComponent#addPropertyChangeListener
673 * @beaninfo
674 * bound: true
675 * attribute: visualUpdate true
676 * description: Defines a fixed cell width when greater than zero.
677 */
678 public void setFixedCellWidth(int width) {
679 int oldValue = fixedCellWidth;
680 fixedCellWidth = width;
681 firePropertyChange("fixedCellWidth", oldValue, fixedCellWidth);
682 }
683
684
685 /**
686 * Returns the value of the {@code fixedCellHeight} property.
687 *
688 * @return the fixed cell height
689 * @see #setFixedCellHeight
690 */
691 public int getFixedCellHeight() {
692 return fixedCellHeight;
693 }
694
695 /**
696 * Sets a fixed value to be used for the height of every cell in the list.
697 * If {@code height} is -1, cell heights are computed in the {@code ListUI}
698 * by applying <code>getPreferredSize</code> to the cell renderer component
699 * for each list element.
700 * <p>
701 * The default value of this property is {@code -1}.
702 * <p>
703 * This is a JavaBeans bound property.
704 *
705 * @param height the height to be used for for all cells in the list
706 * @see #setPrototypeCellValue
707 * @see #setFixedCellWidth
708 * @see JComponent#addPropertyChangeListener
709 * @beaninfo
710 * bound: true
711 * attribute: visualUpdate true
712 * description: Defines a fixed cell height when greater than zero.
713 */
714 public void setFixedCellHeight(int height) {
715 int oldValue = fixedCellHeight;
716 fixedCellHeight = height;
717 firePropertyChange("fixedCellHeight", oldValue, fixedCellHeight);
718 }
719
720
721 /**
722 * Returns the object responsible for painting list items.
723 *
724 * @return the value of the {@code cellRenderer} property
725 * @see #setCellRenderer
726 */
727 public ListCellRenderer getCellRenderer() {
728 return cellRenderer;
729 }
730
731 /**
732 * Sets the delegate that is used to paint each cell in the list.
733 * The job of a cell renderer is discussed in detail in the
734 * <a href="#renderer">class level documentation</a>.
735 * <p>
736 * If the {@code prototypeCellValue} property is {@code non-null},
737 * setting the cell renderer also causes the {@code fixedCellWidth} and
738 * {@code fixedCellHeight} properties to be re-calculated. Only one
739 * <code>PropertyChangeEvent</code> is generated however -
740 * for the <code>cellRenderer</code> property.
741 * <p>
742 * The default value of this property is provided by the {@code ListUI}
743 * delegate, i.e. by the look and feel implementation.
744 * <p>
745 * This is a JavaBeans bound property.
746 *
747 * @param cellRenderer the <code>ListCellRenderer</code>
748 * that paints list cells
749 * @see #getCellRenderer
750 * @beaninfo
751 * bound: true
752 * attribute: visualUpdate true
753 * description: The component used to draw the cells.
754 */
755 public void setCellRenderer(ListCellRenderer cellRenderer) {
756 ListCellRenderer oldValue = this.cellRenderer;
757 this.cellRenderer = cellRenderer;
758
759 /* If the cellRenderer has changed and prototypeCellValue
760 * was set, then recompute fixedCellWidth and fixedCellHeight.
761 */
762 if ((cellRenderer != null) && !cellRenderer.equals(oldValue)) {
763 updateFixedCellSize();
764 }
765
766 firePropertyChange("cellRenderer", oldValue, cellRenderer);
767 }
768
769
770 /**
771 * Returns the color used to draw the foreground of selected items.
772 * {@code DefaultListCellRenderer} uses this color to draw the foreground
773 * of items in the selected state, as do the renderers installed by most
774 * {@code ListUI} implementations.
775 *
776 * @return the color to draw the foreground of selected items
777 * @see #setSelectionForeground
778 * @see DefaultListCellRenderer
779 */
780 public Color getSelectionForeground() {
781 return selectionForeground;
782 }
783
784
785 /**
786 * Sets the color used to draw the foreground of selected items, which
787 * cell renderers can use to render text and graphics.
788 * {@code DefaultListCellRenderer} uses this color to draw the foreground
789 * of items in the selected state, as do the renderers installed by most
790 * {@code ListUI} implementations.
791 * <p>
792 * The default value of this property is defined by the look and feel
793 * implementation.
794 * <p>
795 * This is a JavaBeans bound property.
796 *
797 * @param selectionForeground the {@code Color} to use in the foreground
798 * for selected list items
799 * @see #getSelectionForeground
800 * @see #setSelectionBackground
801 * @see #setForeground
802 * @see #setBackground
803 * @see #setFont
804 * @see DefaultListCellRenderer
805 * @beaninfo
806 * bound: true
807 * attribute: visualUpdate true
808 * description: The foreground color of selected cells.
809 */
810 public void setSelectionForeground(Color selectionForeground) {
811 Color oldValue = this.selectionForeground;
812 this.selectionForeground = selectionForeground;
813 firePropertyChange("selectionForeground", oldValue, selectionForeground);
814 }
815
816
817 /**
818 * Returns the color used to draw the background of selected items.
819 * {@code DefaultListCellRenderer} uses this color to draw the background
820 * of items in the selected state, as do the renderers installed by most
821 * {@code ListUI} implementations.
822 *
823 * @return the color to draw the background of selected items
824 * @see #setSelectionBackground
825 * @see DefaultListCellRenderer
826 */
827 public Color getSelectionBackground() {
828 return selectionBackground;
829 }
830
831
832 /**
833 * Sets the color used to draw the background of selected items, which
834 * cell renderers can use fill selected cells.
835 * {@code DefaultListCellRenderer} uses this color to fill the background
836 * of items in the selected state, as do the renderers installed by most
837 * {@code ListUI} implementations.
838 * <p>
839 * The default value of this property is defined by the look
840 * and feel implementation.
841 * <p>
842 * This is a JavaBeans bound property.
843 *
844 * @param selectionBackground the {@code Color} to use for the
845 * background of selected cells
846 * @see #getSelectionBackground
847 * @see #setSelectionForeground
848 * @see #setForeground
849 * @see #setBackground
850 * @see #setFont
851 * @see DefaultListCellRenderer
852 * @beaninfo
853 * bound: true
854 * attribute: visualUpdate true
855 * description: The background color of selected cells.
856 */
857 public void setSelectionBackground(Color selectionBackground) {
858 Color oldValue = this.selectionBackground;
859 this.selectionBackground = selectionBackground;
860 firePropertyChange("selectionBackground", oldValue, selectionBackground);
861 }
862
863
864 /**
865 * Returns the value of the {@code visibleRowCount} property. See the
866 * documentation for {@link #setVisibleRowCount} for details on how to
867 * interpret this value.
868 *
869 * @return the value of the {@code visibleRowCount} property.
870 * @see #setVisibleRowCount
871 */
872 public int getVisibleRowCount() {
873 return visibleRowCount;
874 }
875
876 /**
877 * Sets the {@code visibleRowCount} property, which has different meanings
878 * depending on the layout orientation: For a {@code VERTICAL} layout
879 * orientation, this sets the preferred number of rows to display without
880 * requiring scrolling; for other orientations, it affects the wrapping of
881 * cells.
882 * <p>
883 * In {@code VERTICAL} orientation:<br>
884 * Setting this property affects the return value of the
885 * {@link #getPreferredScrollableViewportSize} method, which is used to
886 * calculate the preferred size of an enclosing viewport. See that method's
887 * documentation for more details.
888 * <p>
889 * In {@code HORIZONTAL_WRAP} and {@code VERTICAL_WRAP} orientations:<br>
890 * This affects how cells are wrapped. See the documentation of
891 * {@link #setLayoutOrientation} for more details.
892 * <p>
893 * The default value of this property is {@code 8}.
894 * <p>
895 * Calling this method with a negative value results in the property
896 * being set to {@code 0}.
897 * <p>
898 * This is a JavaBeans bound property.
899 *
900 * @param visibleRowCount an integer specifying the preferred number of
901 * rows to display without requiring scrolling
902 * @see #getVisibleRowCount
903 * @see #getPreferredScrollableViewportSize
904 * @see #setLayoutOrientation
905 * @see JComponent#getVisibleRect
906 * @see JViewport
907 * @beaninfo
908 * bound: true
909 * attribute: visualUpdate true
910 * description: The preferred number of rows to display without
911 * requiring scrolling
912 */
913 public void setVisibleRowCount(int visibleRowCount) {
914 int oldValue = this.visibleRowCount;
915 this.visibleRowCount = Math.max(0, visibleRowCount);
916 firePropertyChange("visibleRowCount", oldValue, visibleRowCount);
917 }
918
919
920 /**
921 * Returns the layout orientation property for the list: {@code VERTICAL}
922 * if the layout is a single column of cells, {@code VERTICAL_WRAP} if the
923 * layout is "newspaper style" with the content flowing vertically then
924 * horizontally, or {@code HORIZONTAL_WRAP} if the layout is "newspaper
925 * style" with the content flowing horizontally then vertically.
926 *
927 * @return the value of the {@code layoutOrientation} property
928 * @see #setLayoutOrientation
929 * @since 1.4
930 */
931 public int getLayoutOrientation() {
932 return layoutOrientation;
933 }
934
935
936 /**
937 * Defines the way list cells are layed out. Consider a {@code JList}
938 * with five cells. Cells can be layed out in one of the following ways:
939 * <p>
940 * <pre>
941 * VERTICAL: 0
942 * 1
943 * 2
944 * 3
945 * 4
946 *
947 * HORIZONTAL_WRAP: 0 1 2
948 * 3 4
949 *
950 * VERTICAL_WRAP: 0 3
951 * 1 4
952 * 2
953 * </pre>
954 * <p>
955 * A description of these layouts follows:
956 *
957 * <table border="1"
958 * summary="Describes layouts VERTICAL, HORIZONTAL_WRAP, and VERTICAL_WRAP">
959 * <tr><th><p align="left">Value</p></th><th><p align="left">Description</p></th></tr>
960 * <tr><td><code>VERTICAL</code>
961 * <td>Cells are layed out vertically in a single column.
962 * <tr><td><code>HORIZONTAL_WRAP</code>
963 * <td>Cells are layed out horizontally, wrapping to a new row as
964 * necessary. If the {@code visibleRowCount} property is less than
965 * or equal to zero, wrapping is determined by the width of the
966 * list; otherwise wrapping is done in such a way as to ensure
967 * {@code visibleRowCount} rows in the list.
968 * <tr><td><code>VERTICAL_WRAP</code>
969 * <td>Cells are layed out vertically, wrapping to a new column as
970 * necessary. If the {@code visibleRowCount} property is less than
971 * or equal to zero, wrapping is determined by the height of the
972 * list; otherwise wrapping is done at {@code visibleRowCount} rows.
973 * </table>
974 * <p>
975 * The default value of this property is <code>VERTICAL</code>.
976 *
977 * @param layoutOrientation the new layout orientation, one of:
978 * {@code VERTICAL}, {@code HORIZONTAL_WRAP} or {@code VERTICAL_WRAP}
979 * @see #getLayoutOrientation
980 * @see #setVisibleRowCount
981 * @see #getScrollableTracksViewportHeight
982 * @see #getScrollableTracksViewportWidth
983 * @throws IllegalArgumentException if {@code layoutOrientation} isn't one of the
984 * allowable values
985 * @since 1.4
986 * @beaninfo
987 * bound: true
988 * attribute: visualUpdate true
989 * description: Defines the way list cells are layed out.
990 * enum: VERTICAL JList.VERTICAL
991 * HORIZONTAL_WRAP JList.HORIZONTAL_WRAP
992 * VERTICAL_WRAP JList.VERTICAL_WRAP
993 */
994 public void setLayoutOrientation(int layoutOrientation) {
995 int oldValue = this.layoutOrientation;
996 switch (layoutOrientation) {
997 case VERTICAL:
998 case VERTICAL_WRAP:
999 case HORIZONTAL_WRAP:
1000 this.layoutOrientation = layoutOrientation;
1001 firePropertyChange("layoutOrientation", oldValue, layoutOrientation);
1002 break;
1003 default:
1004 throw new IllegalArgumentException("layoutOrientation must be one of: VERTICAL, HORIZONTAL_WRAP or VERTICAL_WRAP");
1005 }
1006 }
1007
1008
1009 /**
1010 * Returns the smallest list index that is currently visible.
1011 * In a left-to-right {@code componentOrientation}, the first visible
1012 * cell is found closest to the list's upper-left corner. In right-to-left
1013 * orientation, it is found closest to the upper-right corner.
1014 * If nothing is visible or the list is empty, {@code -1} is returned.
1015 * Note that the returned cell may only be partially visible.
1016 *
1017 * @return the index of the first visible cell
1018 * @see #getLastVisibleIndex
1019 * @see JComponent#getVisibleRect
1020 */
1021 public int getFirstVisibleIndex() {
1022 Rectangle r = getVisibleRect();
1023 int first;
1024 if (this.getComponentOrientation().isLeftToRight()) {
1025 first = locationToIndex(r.getLocation());
1026 } else {
1027 first = locationToIndex(new Point((r.x + r.width) - 1, r.y));
1028 }
1029 if (first != -1) {
1030 Rectangle bounds = getCellBounds(first, first);
1031 if (bounds != null) {
1032 SwingUtilities.computeIntersection(r.x, r.y, r.width, r.height, bounds);
1033 if (bounds.width == 0 || bounds.height == 0) {
1034 first = -1;
1035 }
1036 }
1037 }
1038 return first;
1039 }
1040
1041
1042 /**
1043 * Returns the largest list index that is currently visible.
1044 * If nothing is visible or the list is empty, {@code -1} is returned.
1045 * Note that the returned cell may only be partially visible.
1046 *
1047 * @return the index of the last visible cell
1048 * @see #getFirstVisibleIndex
1049 * @see JComponent#getVisibleRect
1050 */
1051 public int getLastVisibleIndex() {
1052 boolean leftToRight = this.getComponentOrientation().isLeftToRight();
1053 Rectangle r = getVisibleRect();
1054 Point lastPoint;
1055 if (leftToRight) {
1056 lastPoint = new Point((r.x + r.width) - 1, (r.y + r.height) - 1);
1057 } else {
1058 lastPoint = new Point(r.x, (r.y + r.height) - 1);
1059 }
1060 int location = locationToIndex(lastPoint);
1061
1062 if (location != -1) {
1063 Rectangle bounds = getCellBounds(location, location);
1064
1065 if (bounds != null) {
1066 SwingUtilities.computeIntersection(r.x, r.y, r.width, r.height, bounds);
1067 if (bounds.width == 0 || bounds.height == 0) {
1068 // Try the top left(LTR) or top right(RTL) corner, and
1069 // then go across checking each cell for HORIZONTAL_WRAP.
1070 // Try the lower left corner, and then go across checking
1071 // each cell for other list layout orientation.
1072 boolean isHorizontalWrap =
1073 (getLayoutOrientation() == HORIZONTAL_WRAP);
1074 Point visibleLocation = isHorizontalWrap ?
1075 new Point(lastPoint.x, r.y) :
1076 new Point(r.x, lastPoint.y);
1077 int last;
1078 int visIndex = -1;
1079 int lIndex = location;
1080 location = -1;
1081
1082 do {
1083 last = visIndex;
1084 visIndex = locationToIndex(visibleLocation);
1085
1086 if (visIndex != -1) {
1087 bounds = getCellBounds(visIndex, visIndex);
1088 if (visIndex != lIndex && bounds != null &&
1089 bounds.contains(visibleLocation)) {
1090 location = visIndex;
1091 if (isHorizontalWrap) {
1092 visibleLocation.y = bounds.y + bounds.height;
1093 if (visibleLocation.y >= lastPoint.y) {
1094 // Past visible region, bail.
1095 last = visIndex;
1096 }
1097 }
1098 else {
1099 visibleLocation.x = bounds.x + bounds.width;
1100 if (visibleLocation.x >= lastPoint.x) {
1101 // Past visible region, bail.
1102 last = visIndex;
1103 }
1104 }
1105
1106 }
1107 else {
1108 last = visIndex;
1109 }
1110 }
1111 } while (visIndex != -1 && last != visIndex);
1112 }
1113 }
1114 }
1115 return location;
1116 }
1117
1118
1119 /**
1120 * Scrolls the list within an enclosing viewport to make the specified
1121 * cell completely visible. This calls {@code scrollRectToVisible} with
1122 * the bounds of the specified cell. For this method to work, the
1123 * {@code JList} must be within a <code>JViewport</code>.
1124 * <p>
1125 * If the given index is outside the list's range of cells, this method
1126 * results in nothing.
1127 *
1128 * @param index the index of the cell to make visible
1129 * @see JComponent#scrollRectToVisible
1130 * @see #getVisibleRect
1131 */
1132 public void ensureIndexIsVisible(int index) {
1133 Rectangle cellBounds = getCellBounds(index, index);
1134 if (cellBounds != null) {
1135 scrollRectToVisible(cellBounds);
1136 }
1137 }
1138
1139 /**
1140 * Turns on or off automatic drag handling. In order to enable automatic
1141 * drag handling, this property should be set to {@code true}, and the
1142 * list's {@code TransferHandler} needs to be {@code non-null}.
1143 * The default value of the {@code dragEnabled} property is {@code false}.
1144 * <p>
1145 * The job of honoring this property, and recognizing a user drag gesture,
1146 * lies with the look and feel implementation, and in particular, the list's
1147 * {@code ListUI}. When automatic drag handling is enabled, most look and
1148 * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1149 * drag and drop operation whenever the user presses the mouse button over
1150 * an item and then moves the mouse a few pixels. Setting this property to
1151 * {@code true} can therefore have a subtle effect on how selections behave.
1152 * <p>
1153 * If a look and feel is used that ignores this property, you can still
1154 * begin a drag and drop operation by calling {@code exportAsDrag} on the
1155 * list's {@code TransferHandler}.
1156 *
1157 * @param b whether or not to enable automatic drag handling
1158 * @exception HeadlessException if
1159 * <code>b</code> is <code>true</code> and
1160 * <code>GraphicsEnvironment.isHeadless()</code>
1161 * returns <code>true</code>
1162 * @see java.awt.GraphicsEnvironment#isHeadless
1163 * @see #getDragEnabled
1164 * @see #setTransferHandler
1165 * @see TransferHandler
1166 * @since 1.4
1167 *
1168 * @beaninfo
1169 * description: determines whether automatic drag handling is enabled
1170 * bound: false
1171 */
1172 public void setDragEnabled(boolean b) {
1173 if (b && GraphicsEnvironment.isHeadless()) {
1174 throw new HeadlessException();
1175 }
1176 dragEnabled = b;
1177 }
1178
1179 /**
1180 * Returns whether or not automatic drag handling is enabled.
1181 *
1182 * @return the value of the {@code dragEnabled} property
1183 * @see #setDragEnabled
1184 * @since 1.4
1185 */
1186 public boolean getDragEnabled() {
1187 return dragEnabled;
1188 }
1189
1190 /**
1191 * Sets the drop mode for this component. For backward compatibility,
1192 * the default for this property is <code>DropMode.USE_SELECTION</code>.
1193 * Usage of one of the other modes is recommended, however, for an
1194 * improved user experience. <code>DropMode.ON</code>, for instance,
1195 * offers similar behavior of showing items as selected, but does so without
1196 * affecting the actual selection in the list.
1197 * <p>
1198 * <code>JList</code> supports the following drop modes:
1199 * <ul>
1200 * <li><code>DropMode.USE_SELECTION</code></li>
1201 * <li><code>DropMode.ON</code></li>
1202 * <li><code>DropMode.INSERT</code></li>
1203 * <li><code>DropMode.ON_OR_INSERT</code></li>
1204 * </ul>
1205 * The drop mode is only meaningful if this component has a
1206 * <code>TransferHandler</code> that accepts drops.
1207 *
1208 * @param dropMode the drop mode to use
1209 * @throws IllegalArgumentException if the drop mode is unsupported
1210 * or <code>null</code>
1211 * @see #getDropMode
1212 * @see #getDropLocation
1213 * @see #setTransferHandler
1214 * @see TransferHandler
1215 * @since 1.6
1216 */
1217 public final void setDropMode(DropMode dropMode) {
1218 if (dropMode != null) {
1219 switch (dropMode) {
1220 case USE_SELECTION:
1221 case ON:
1222 case INSERT:
1223 case ON_OR_INSERT:
1224 this.dropMode = dropMode;
1225 return;
1226 }
1227 }
1228
1229 throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for list");
1230 }
1231
1232 /**
1233 * Returns the drop mode for this component.
1234 *
1235 * @return the drop mode for this component
1236 * @see #setDropMode
1237 * @since 1.6
1238 */
1239 public final DropMode getDropMode() {
1240 return dropMode;
1241 }
1242
1243 /**
1244 * Calculates a drop location in this component, representing where a
1245 * drop at the given point should insert data.
1246 *
1247 * @param p the point to calculate a drop location for
1248 * @return the drop location, or <code>null</code>
1249 */
1250 DropLocation dropLocationForPoint(Point p) {
1251 DropLocation location = null;
1252 Rectangle rect = null;
1253
1254 int index = locationToIndex(p);
1255 if (index != -1) {
1256 rect = getCellBounds(index, index);
1257 }
1258
1259 switch(dropMode) {
1260 case USE_SELECTION:
1261 case ON:
1262 location = new DropLocation(p,
1263 (rect != null && rect.contains(p)) ? index : -1,
1264 false);
1265
1266 break;
1267 case INSERT:
1268 if (index == -1) {
1269 location = new DropLocation(p, getModel().getSize(), true);
1270 break;
1271 }
1272
1273 if (layoutOrientation == HORIZONTAL_WRAP) {
1274 boolean ltr = getComponentOrientation().isLeftToRight();
1275
1276 if (SwingUtilities2.liesInHorizontal(rect, p, ltr, false) == TRAILING) {
1277 index++;
1278 // special case for below all cells
1279 } else if (index == getModel().getSize() - 1 && p.y >= rect.y + rect.height) {
1280 index++;
1281 }
1282 } else {
1283 if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) {
1284 index++;
1285 }
1286 }
1287
1288 location = new DropLocation(p, index, true);
1289
1290 break;
1291 case ON_OR_INSERT:
1292 if (index == -1) {
1293 location = new DropLocation(p, getModel().getSize(), true);
1294 break;
1295 }
1296
1297 boolean between = false;
1298
1299 if (layoutOrientation == HORIZONTAL_WRAP) {
1300 boolean ltr = getComponentOrientation().isLeftToRight();
1301
1302 Section section = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1303 if (section == TRAILING) {
1304 index++;
1305 between = true;
1306 // special case for below all cells
1307 } else if (index == getModel().getSize() - 1 && 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, p, true);
1315 if (section == LEADING) {
1316 between = true;
1317 } else if (section == TRAILING) {
1318 index++;
1319 between = true;
1320 }
1321 }
1322
1323 location = new DropLocation(p, index, between);
1324
1325 break;
1326 default:
1327 assert false : "Unexpected drop mode";
1328 }
1329
1330 return location;
1331 }
1332
1333 /**
1334 * Called to set or clear the drop location during a DnD operation.
1335 * In some cases, the component may need to use it's internal selection
1336 * temporarily to indicate the drop location. To help facilitate this,
1337 * this method returns and accepts as a parameter a state object.
1338 * This state object can be used to store, and later restore, the selection
1339 * state. Whatever this method returns will be passed back to it in
1340 * future calls, as the state parameter. If it wants the DnD system to
1341 * continue storing the same state, it must pass it back every time.
1342 * Here's how this is used:
1343 * <p>
1344 * Let's say that on the first call to this method the component decides
1345 * to save some state (because it is about to use the selection to show
1346 * a drop index). It can return a state object to the caller encapsulating
1347 * any saved selection state. On a second call, let's say the drop location
1348 * is being changed to something else. The component doesn't need to
1349 * restore anything yet, so it simply passes back the same state object
1350 * to have the DnD system continue storing it. Finally, let's say this
1351 * method is messaged with <code>null</code>. This means DnD
1352 * is finished with this component for now, meaning it should restore
1353 * state. At this point, it can use the state parameter to restore
1354 * said state, and of course return <code>null</code> since there's
1355 * no longer anything to store.
1356 *
1357 * @param location the drop location (as calculated by
1358 * <code>dropLocationForPoint</code>) or <code>null</code>
1359 * if there's no longer a valid drop location
1360 * @param state the state object saved earlier for this component,
1361 * or <code>null</code>
1362 * @param forDrop whether or not the method is being called because an
1363 * actual drop occurred
1364 * @return any saved state for this component, or <code>null</code> if none
1365 */
1366 Object setDropLocation(TransferHandler.DropLocation location,
1367 Object state,
1368 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[][] {inds, {getAnchorSelectionIndex(),
1388 getLeadSelectionIndex()}};
1389 } else {
1390 retVal = state;
1391 }
1392
1393 int index = listLocation.getIndex();
1394 if (index == -1) {
1395 clearSelection();
1396 getSelectionModel().setAnchorSelectionIndex(-1);
1397 getSelectionModel().setLeadSelectionIndex(-1);
1398 } else {
1399 setSelectionInterval(index, index);
1400 }
1401 }
1402 }
1403
1404 DropLocation old = dropLocation;
1405 dropLocation = listLocation;
1406 firePropertyChange("dropLocation", old, dropLocation);
1407
1408 return retVal;
1409 }
1410
1411 /**
1412 * Returns the location that this component should visually indicate
1413 * as the drop location during a DnD operation over the component,
1414 * or {@code null} if no location is to currently be shown.
1415 * <p>
1416 * This method is not meant for querying the drop location
1417 * from a {@code TransferHandler}, as the drop location is only
1418 * set after the {@code TransferHandler}'s <code>canImport</code>
1419 * has returned and has allowed for the location to be shown.
1420 * <p>
1421 * When this property changes, a property change event with
1422 * name "dropLocation" is fired by the component.
1423 * <p>
1424 * By default, responsibility for listening for changes to this property
1425 * and indicating the drop location visually lies with the list's
1426 * {@code ListUI}, which may paint it directly and/or install a cell
1427 * renderer to do so. Developers wishing to implement custom drop location
1428 * painting and/or replace the default cell renderer, may need to honor
1429 * this property.
1430 *
1431 * @return the drop location
1432 * @see #setDropMode
1433 * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1434 * @since 1.6
1435 */
1436 public final DropLocation getDropLocation() {
1437 return dropLocation;
1438 }
1439
1440 /**
1441 * Returns the next list element whose {@code toString} value
1442 * starts with the given prefix.
1443 *
1444 * @param prefix the string to test for a match
1445 * @param startIndex the index for starting the search
1446 * @param bias the search direction, either
1447 * Position.Bias.Forward or Position.Bias.Backward.
1448 * @return the index of the next list element that
1449 * starts with the prefix; otherwise {@code -1}
1450 * @exception IllegalArgumentException if prefix is {@code null}
1451 * or startIndex is out of bounds
1452 * @since 1.4
1453 */
1454 public int getNextMatch(String prefix, int startIndex, Position.Bias bias) {
1455 ListModel model = getModel();
1456 int max = model.getSize();
1457 if (prefix == null) {
1458 throw new IllegalArgumentException();
1459 }
1460 if (startIndex < 0 || startIndex >= max) {
1461 throw new IllegalArgumentException();
1462 }
1463 prefix = prefix.toUpperCase();
1464
1465 // start search from the next element after the selected element
1466 int increment = (bias == Position.Bias.Forward) ? 1 : -1;
1467 int index = startIndex;
1468 do {
1469 Object o = model.getElementAt(index);
1470
1471 if (o != null) {
1472 String string;
1473
1474 if (o instanceof String) {
1475 string = ((String)o).toUpperCase();
1476 }
1477 else {
1478 string = o.toString();
1479 if (string != null) {
1480 string = string.toUpperCase();
1481 }
1482 }
1483
1484 if (string != null && string.startsWith(prefix)) {
1485 return index;
1486 }
1487 }
1488 index = (index + increment + max) % max;
1489 } while (index != startIndex);
1490 return -1;
1491 }
1492
1493 /**
1494 * Returns the tooltip text to be used for the given event. This overrides
1495 * {@code JComponent}'s {@code getToolTipText} to first check the cell
1496 * renderer component for the cell over which the event occurred, returning
1497 * its tooltip text, if any. This implementation allows you to specify
1498 * tooltip text on the cell level, by using {@code setToolTipText} on your
1499 * cell renderer component.
1500 * <p>
1501 * <bold>Note:</bold> For <code>JList</code> to properly display the
1502 * tooltips of its renderers in this manner, <code>JList</code> must be a
1503 * registered component with the <code>ToolTipManager</code>. This registration
1504 * is done automatically in the constructor. However, if at a later point
1505 * <code>JList</code> is unregistered, by way of a call to
1506 * {@code setToolTipText(null)}, tips from the renderers will no longer display.
1507 *
1508 * @param event the {@code MouseEvent} to fetch the tooltip text for
1509 * @see JComponent#setToolTipText
1510 * @see JComponent#getToolTipText
1511 */
1512 public String getToolTipText(MouseEvent event) {
1513 if(event != null) {
1514 Point p = event.getPoint();
1515 int index = locationToIndex(p);
1516 ListCellRenderer r = getCellRenderer();
1517 Rectangle cellBounds;
1518
1519 if (index != -1 && r != null && (cellBounds =
1520 getCellBounds(index, index)) != null &&
1521 cellBounds.contains(p.x, p.y)) {
1522 ListSelectionModel lsm = getSelectionModel();
1523 Component rComponent = r.getListCellRendererComponent(
1524 this, getModel().getElementAt(index), index,
1525 lsm.isSelectedIndex(index),
1526 (hasFocus() && (lsm.getLeadSelectionIndex() ==
1527 index)));
1528
1529 if(rComponent instanceof JComponent) {
1530 MouseEvent newEvent;
1531
1532 p.translate(-cellBounds.x, -cellBounds.y);
1533 newEvent = new MouseEvent(rComponent, event.getID(),
1534 event.getWhen(),
1535 event.getModifiers(),
1536 p.x, p.y,
1537 event.getXOnScreen(),
1538 event.getYOnScreen(),
1539 event.getClickCount(),
1540 event.isPopupTrigger(),
1541 MouseEvent.NOBUTTON);
1542
1543 String tip = ((JComponent)rComponent).getToolTipText(
1544 newEvent);
1545
1546 if (tip != null) {
1547 return tip;
1548 }
1549 }
1550 }
1551 }
1552 return super.getToolTipText();
1553 }
1554
1555 /**
1556 * --- ListUI Delegations ---
1557 */
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 /**
1581 * Returns the origin of the specified item in the list's coordinate
1582 * system. This method returns {@code null} if the index isn't valid.
1583 * <p>
1584 * This is a cover method that delegates to the method of the same name
1585 * in the list's {@code ListUI}. It returns {@code null} if the list has
1586 * no {@code ListUI}.
1587 *
1588 * @param index the cell index
1589 * @return the origin of the cell, or {@code null}
1590 */
1591 public Point indexToLocation(int index) {
1592 ListUI ui = getUI();
1593 return (ui != null) ? ui.indexToLocation(this, index) : null;
1594 }
1595
1596
1597 /**
1598 * Returns the bounding rectangle, in the list's coordinate system,
1599 * for the range of cells specified by the two indices.
1600 * These indices can be supplied in any order.
1601 * <p>
1602 * If the smaller index is outside the list's range of cells, this method
1603 * returns {@code null}. If the smaller index is valid, but the larger
1604 * index is outside the list's range, the bounds of just the first index
1605 * is returned. Otherwise, the bounds of the valid range is returned.
1606 * <p>
1607 * This is a cover method that delegates to the method of the same name
1608 * in the list's {@code ListUI}. It returns {@code null} if the list has
1609 * no {@code ListUI}.
1610 *
1611 * @param index0 the first index in the range
1612 * @param index1 the second index in the range
1613 * @return the bounding rectangle for the range of cells, or {@code null}
1614 */
1615 public Rectangle getCellBounds(int index0, int index1) {
1616 ListUI ui = getUI();
1617 return (ui != null) ? ui.getCellBounds(this, index0, index1) : null;
1618 }
1619
1620
1621 /**
1622 * --- ListModel Support ---
1623 */
1624
1625
1626 /**
1627 * Returns the data model that holds the list of items displayed
1628 * by the <code>JList</code> component.
1629 *
1630 * @return the <code>ListModel</code> that provides the displayed
1631 * list of items
1632 * @see #setModel
1633 */
1634 public ListModel getModel() {
1635 return dataModel;
1636 }
1637
1638 /**
1639 * Sets the model that represents the contents or "value" of the
1640 * list, notifies property change listeners, and then clears the
1641 * list's selection.
1642 * <p>
1643 * This is a JavaBeans bound property.
1644 *
1645 * @param model the <code>ListModel</code> that provides the
1646 * list of items for display
1647 * @exception IllegalArgumentException if <code>model</code> is
1648 * <code>null</code>
1649 * @see #getModel
1650 * @see #clearSelection
1651 * @beaninfo
1652 * bound: true
1653 * attribute: visualUpdate true
1654 * description: The object that contains the data to be drawn by this JList.
1655 */
1656 public void setModel(ListModel model) {
1657 if (model == null) {
1658 throw new IllegalArgumentException("model must be non null");
1659 }
1660 ListModel oldValue = dataModel;
1661 dataModel = model;
1662 firePropertyChange("model", oldValue, dataModel);
1663 clearSelection();
1664 }
1665
1666
1667 /**
1668 * Constructs a read-only <code>ListModel</code> from an array of objects,
1669 * and calls {@code setModel} with this model.
1670 * <p>
1671 * Attempts to pass a {@code null} value to this method results in
1672 * undefined behavior and, most likely, exceptions. The created model
1673 * references the given array directly. Attempts to modify the array
1674 * after invoking this method results in undefined behavior.
1675 *
1676 * @param listData an array of {@code Objects} containing the items to
1677 * display in the list
1678 * @see #setModel
1679 */
1680 public void setListData(final Object[] listData) {
1681 setModel (
1682 new AbstractListModel() {
1683 public int getSize() { return listData.length; }
1684 public Object getElementAt(int i) { return listData[i]; }
1685 }
1686 );
1687 }
1688
1689
1690 /**
1691 * Constructs a read-only <code>ListModel</code> from a <code>Vector</code>
1692 * and calls {@code setModel} with this model.
1693 * <p>
1694 * Attempts to pass a {@code null} value to this method results in
1695 * undefined behavior and, most likely, exceptions. The created model
1696 * references the given {@code Vector} directly. Attempts to modify the
1697 * {@code Vector} after invoking this method results in undefined behavior.
1698 *
1699 * @param listData a <code>Vector</code> containing the items to
1700 * display in the list
1701 * @see #setModel
1702 */
1703 public void setListData(final Vector<?> listData) {
1704 setModel (
1705 new AbstractListModel() {
1706 public int getSize() { return listData.size(); }
1707 public Object getElementAt(int i) { return listData.elementAt(i); }
1708 }
1709 );
1710 }
1711
1712
1713 /**
1714 * --- ListSelectionModel delegations and extensions ---
1715 */
1716
1717
1718 /**
1719 * Returns an instance of {@code DefaultListSelectionModel}; called
1720 * during construction to initialize the list's selection model
1721 * property.
1722 *
1723 * @return a {@code DefaultListSelecitonModel}, used to initialize
1724 * the list's selection model property during construction
1725 * @see #setSelectionModel
1726 * @see DefaultListSelectionModel
1727 */
1728 protected ListSelectionModel createSelectionModel() {
1729 return new DefaultListSelectionModel();
1730 }
1731
1732
1733 /**
1734 * Returns the current selection model. The selection model maintains the
1735 * selection state of the list. See the class level documentation for more
1736 * details.
1737 *
1738 * @return the <code>ListSelectionModel</code> that maintains the
1739 * list's selections
1740 *
1741 * @see #setSelectionModel
1742 * @see ListSelectionModel
1743 */
1744 public ListSelectionModel getSelectionModel() {
1745 return selectionModel;
1746 }
1747
1748
1749 /**
1750 * Notifies {@code ListSelectionListener}s added directly to the list
1751 * of selection changes made to the selection model. {@code JList}
1752 * listens for changes made to the selection in the selection model,
1753 * and forwards notification to listeners added to the list directly,
1754 * by calling this method.
1755 * <p>
1756 * This method constructs a {@code ListSelectionEvent} with this list
1757 * as the source, and the specified arguments, and sends it to the
1758 * registered {@code ListSelectionListeners}.
1759 *
1760 * @param firstIndex the first index in the range, {@code <= lastIndex}
1761 * @param lastIndex the last index in the range, {@code >= firstIndex}
1762 * @param isAdjusting whether or not this is one in a series of
1763 * multiple events, where changes are still being made
1764 *
1765 * @see #addListSelectionListener
1766 * @see #removeListSelectionListener
1767 * @see javax.swing.event.ListSelectionEvent
1768 * @see EventListenerList
1769 */
1770 protected void fireSelectionValueChanged(int firstIndex, int lastIndex,
1771 boolean isAdjusting)
1772 {
1773 Object[] listeners = listenerList.getListenerList();
1774 ListSelectionEvent e = null;
1775
1776 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1777 if (listeners[i] == ListSelectionListener.class) {
1778 if (e == null) {
1779 e = new ListSelectionEvent(this, firstIndex, lastIndex,
1780 isAdjusting);
1781 }
1782 ((ListSelectionListener)listeners[i+1]).valueChanged(e);
1783 }
1784 }
1785 }
1786
1787
1788 /* A ListSelectionListener that forwards ListSelectionEvents from
1789 * the selectionModel to the JList ListSelectionListeners. The
1790 * forwarded events only differ from the originals in that their
1791 * source is the JList instead of the selectionModel itself.
1792 */
1793 private class ListSelectionHandler implements ListSelectionListener, Serializable
1794 {
1795 public void valueChanged(ListSelectionEvent e) {
1796 fireSelectionValueChanged(e.getFirstIndex(),
1797 e.getLastIndex(),
1798 e.getValueIsAdjusting());
1799 }
1800 }
1801
1802
1803 /**
1804 * Adds a listener to the list, to be notified each time a change to the
1805 * selection occurs; the preferred way of listening for selection state
1806 * changes. {@code JList} takes care of listening for selection state
1807 * changes in the selection model, and notifies the given listener of
1808 * each change. {@code ListSelectionEvent}s sent to the listener have a
1809 * {@code source} property set to this list.
1810 *
1811 * @param listener the {@code ListSelectionListener} to add
1812 * @see #getSelectionModel
1813 * @see #getListSelectionListeners
1814 */
1815 public void addListSelectionListener(ListSelectionListener listener)
1816 {
1817 if (selectionListener == null) {
1818 selectionListener = new ListSelectionHandler();
1819 getSelectionModel().addListSelectionListener(selectionListener);
1820 }
1821
1822 listenerList.add(ListSelectionListener.class, listener);
1823 }
1824
1825
1826 /**
1827 * Removes a selection listener from the list.
1828 *
1829 * @param listener the {@code ListSelectionListener} to remove
1830 * @see #addListSelectionListener
1831 * @see #getSelectionModel
1832 */
1833 public void removeListSelectionListener(ListSelectionListener listener) {
1834 listenerList.remove(ListSelectionListener.class, listener);
1835 }
1836
1837
1838 /**
1839 * Returns an array of all the {@code ListSelectionListener}s added
1840 * to this {@code JList} by way of {@code addListSelectionListener}.
1841 *
1842 * @return all of the {@code ListSelectionListener}s on this list, or
1843 * an empty array if no listeners have been added
1844 * @see #addListSelectionListener
1845 * @since 1.4
1846 */
1847 public ListSelectionListener[] getListSelectionListeners() {
1848 return (ListSelectionListener[])listenerList.getListeners(
1849 ListSelectionListener.class);
1850 }
1851
1852
1853 /**
1854 * Sets the <code>selectionModel</code> for the list to a
1855 * non-<code>null</code> <code>ListSelectionModel</code>
1856 * implementation. The selection model handles the task of making single
1857 * selections, selections of contiguous ranges, and non-contiguous
1858 * selections.
1859 * <p>
1860 * This is a JavaBeans bound property.
1861 *
1862 * @param selectionModel the <code>ListSelectionModel</code> that
1863 * implements the selections
1864 * @exception IllegalArgumentException if <code>selectionModel</code>
1865 * is <code>null</code>
1866 * @see #getSelectionModel
1867 * @beaninfo
1868 * bound: true
1869 * description: The selection model, recording which cells are selected.
1870 */
1871 public void setSelectionModel(ListSelectionModel selectionModel) {
1872 if (selectionModel == null) {
1873 throw new IllegalArgumentException("selectionModel must be non null");
1874 }
1875
1876 /* Remove the forwarding ListSelectionListener from the old
1877 * selectionModel, and add it to the new one, if necessary.
1878 */
1879 if (selectionListener != null) {
1880 this.selectionModel.removeListSelectionListener(selectionListener);
1881 selectionModel.addListSelectionListener(selectionListener);
1882 }
1883
1884 ListSelectionModel oldValue = this.selectionModel;
1885 this.selectionModel = selectionModel;
1886 firePropertyChange("selectionModel", oldValue, selectionModel);
1887 }
1888
1889
1890 /**
1891 * Sets the selection mode for the list. This is a cover method that sets
1892 * the selection mode directly on the selection model.
1893 * <p>
1894 * The following list describes the accepted selection modes:
1895 * <ul>
1896 * <li>{@code ListSelectionModel.SINGLE_SELECTION} -
1897 * Only one list index can be selected at a time. In this mode,
1898 * {@code setSelectionInterval} and {@code addSelectionInterval} are
1899 * equivalent, both replacing the current selection with the index
1900 * represented by the second argument (the "lead").
1901 * <li>{@code ListSelectionModel.SINGLE_INTERVAL_SELECTION} -
1902 * Only one contiguous interval can be selected at a time.
1903 * In this mode, {@code addSelectionInterval} behaves like
1904 * {@code setSelectionInterval} (replacing the current selection},
1905 * unless the given interval is immediately adjacent to or overlaps
1906 * the existing selection, and can be used to grow the selection.
1907 * <li>{@code ListSelectionModel.MULTIPLE_INTERVAL_SELECTION} -
1908 * In this mode, there's no restriction on what can be selected.
1909 * This mode is the default.
1910 * </ul>
1911 *
1912 * @param selectionMode the selection mode
1913 * @see #getSelectionMode
1914 * @throws IllegalArgumentException if the selection mode isn't
1915 * one of those allowed
1916 * @beaninfo
1917 * description: The selection mode.
1918 * enum: SINGLE_SELECTION ListSelectionModel.SINGLE_SELECTION
1919 * SINGLE_INTERVAL_SELECTION ListSelectionModel.SINGLE_INTERVAL_SELECTION
1920 * MULTIPLE_INTERVAL_SELECTION ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
1921 */
1922 public void setSelectionMode(int selectionMode) {
1923 getSelectionModel().setSelectionMode(selectionMode);
1924 }
1925
1926 /**
1927 * Returns the current selection mode for the list. This is a cover
1928 * method that delegates to the method of the same name on the
1929 * list's selection model.
1930 *
1931 * @return the current selection mode
1932 * @see #setSelectionMode
1933 */
1934 public int getSelectionMode() {
1935 return getSelectionModel().getSelectionMode();
1936 }
1937
1938
1939 /**
1940 * Returns the anchor selection index. This is a cover method that
1941 * delegates to the method of the same name on the list's selection model.
1942 *
1943 * @return the anchor selection index
1944 * @see ListSelectionModel#getAnchorSelectionIndex
1945 */
1946 public int getAnchorSelectionIndex() {
1947 return getSelectionModel().getAnchorSelectionIndex();
1948 }
1949
1950
1951 /**
1952 * Returns the lead selection index. This is a cover method that
1953 * delegates to the method of the same name on the list's selection model.
1954 *
1955 * @return the lead selection index
1956 * @see ListSelectionModel#getLeadSelectionIndex
1957 * @beaninfo
1958 * description: The lead selection index.
1959 */
1960 public int getLeadSelectionIndex() {
1961 return getSelectionModel().getLeadSelectionIndex();
1962 }
1963
1964
1965 /**
1966 * Returns the smallest selected cell index, or {@code -1} if the selection
1967 * is empty. This is a cover method that delegates to the method of the same
1968 * name on the list's selection model.
1969 *
1970 * @return the smallest selected cell index, or {@code -1}
1971 * @see ListSelectionModel#getMinSelectionIndex
1972 */
1973 public int getMinSelectionIndex() {
1974 return getSelectionModel().getMinSelectionIndex();
1975 }
1976
1977
1978 /**
1979 * Returns the largest selected cell index, or {@code -1} if the selection
1980 * is empty. This is a cover method that delegates to the method of the same
1981 * name on the list's selection model.
1982 *
1983 * @return the largest selected cell index
1984 * @see ListSelectionModel#getMaxSelectionIndex
1985 */
1986 public int getMaxSelectionIndex() {
1987 return getSelectionModel().getMaxSelectionIndex();
1988 }
1989
1990
1991 /**
1992 * Returns {@code true} if the specified index is selected,
1993 * else {@code false}. This is a cover method that delegates to the method
1994 * of the same name on the list's selection model.
1995 *
1996 * @param index index to be queried for selection state
1997 * @return {@code true} if the specified index is selected,
1998 * else {@code false}
1999 * @see ListSelectionModel#isSelectedIndex
2000 * @see #setSelectedIndex
2001 */
2002 public boolean isSelectedIndex(int index) {
2003 return getSelectionModel().isSelectedIndex(index);
2004 }
2005
2006
2007 /**
2008 * Returns {@code true} if nothing is selected, else {@code false}.
2009 * This is a cover method that delegates to the method of the same
2010 * name on the list's selection model.
2011 *
2012 * @return {@code true} if nothing is selected, else {@code false}
2013 * @see ListSelectionModel#isSelectionEmpty
2014 * @see #clearSelection
2015 */
2016 public boolean isSelectionEmpty() {
2017 return getSelectionModel().isSelectionEmpty();
2018 }
2019
2020
2021 /**
2022 * Clears the selection; after calling this method, {@code isSelectionEmpty}
2023 * will return {@code true}. This is a cover method that delegates to the
2024 * method of the same name on the list's selection model.
2025 *
2026 * @see ListSelectionModel#clearSelection
2027 * @see #isSelectionEmpty
2028 */
2029 public void clearSelection() {
2030 getSelectionModel().clearSelection();
2031 }
2032
2033
2034 /**
2035 * Selects the specified interval. Both {@code anchor} and {@code lead}
2036 * indices are included. {@code anchor} doesn't have to be less than or
2037 * equal to {@code lead}. This is a cover method that delegates to the
2038 * method of the same name on the list's selection model.
2039 * <p>
2040 * Refer to the documentation of the selection model class being used
2041 * for details on how values less than {@code 0} are handled.
2042 *
2043 * @param anchor the first index to select
2044 * @param lead the last index to select
2045 * @see ListSelectionModel#setSelectionInterval
2046 * @see DefaultListSelectionModel#setSelectionInterval
2047 * @see #createSelectionModel
2048 * @see #addSelectionInterval
2049 * @see #removeSelectionInterval
2050 */
2051 public void setSelectionInterval(int anchor, int lead) {
2052 getSelectionModel().setSelectionInterval(anchor, lead);
2053 }
2054
2055
2056 /**
2057 * Sets the selection to be the union of the specified interval with current
2058 * selection. Both the {@code anchor} and {@code lead} indices are
2059 * included. {@code anchor} doesn't have to be less than or
2060 * equal to {@code lead}. This is a cover method that delegates to the
2061 * method of the same name on the list's selection model.
2062 * <p>
2063 * Refer to the documentation of the selection model class being used
2064 * for details on how values less than {@code 0} are handled.
2065 *
2066 * @param anchor the first index to add to the selection
2067 * @param lead the last index to add to the selection
2068 * @see ListSelectionModel#addSelectionInterval
2069 * @see DefaultListSelectionModel#addSelectionInterval
2070 * @see #createSelectionModel
2071 * @see #setSelectionInterval
2072 * @see #removeSelectionInterval
2073 */
2074 public void addSelectionInterval(int anchor, int lead) {
2075 getSelectionModel().addSelectionInterval(anchor, lead);
2076 }
2077
2078
2079 /**
2080 * Sets the selection to be the set difference of the specified interval
2081 * and the current selection. Both the {@code index0} and {@code index1}
2082 * indices are removed. {@code index0} doesn't have to be less than or
2083 * equal to {@code index1}. This is a cover method that delegates to the
2084 * method of the same name on the list's selection model.
2085 * <p>
2086 * Refer to the documentation of the selection model class being used
2087 * for details on how values less than {@code 0} are handled.
2088 *
2089 * @param index0 the first index to remove from the selection
2090 * @param index1 the last index to remove from the selection
2091 * @see ListSelectionModel#removeSelectionInterval
2092 * @see DefaultListSelectionModel#removeSelectionInterval
2093 * @see #createSelectionModel
2094 * @see #setSelectionInterval
2095 * @see #addSelectionInterval
2096 */
2097 public void removeSelectionInterval(int index0, int index1) {
2098 getSelectionModel().removeSelectionInterval(index0, index1);
2099 }
2100
2101
2102 /**
2103 * Sets the selection model's {@code valueIsAdjusting} property. When
2104 * {@code true}, upcoming changes to selection should be considered part
2105 * of a single change. This property is used internally and developers
2106 * typically need not call this method. For example, when the model is being
2107 * updated in response to a user drag, the value of the property is set
2108 * to {@code true} when the drag is initiated and set to {@code false}
2109 * when the drag is finished. This allows listeners to update only
2110 * when a change has been finalized, rather than handling all of the
2111 * intermediate values.
2112 * <p>
2113 * You may want to use this directly if making a series of changes
2114 * that should be considered part of a single change.
2115 * <p>
2116 * This is a cover method that delegates to the method of the same name on
2117 * the list's selection model. See the documentation for
2118 * {@link javax.swing.ListSelectionModel#setValueIsAdjusting} for
2119 * more details.
2120 *
2121 * @param b the new value for the property
2122 * @see ListSelectionModel#setValueIsAdjusting
2123 * @see javax.swing.event.ListSelectionEvent#getValueIsAdjusting
2124 * @see #getValueIsAdjusting
2125 */
2126 public void setValueIsAdjusting(boolean b) {
2127 getSelectionModel().setValueIsAdjusting(b);
2128 }
2129
2130
2131 /**
2132 * Returns the value of the selection model's {@code isAdjusting} property.
2133 * <p>
2134 * This is a cover method that delegates to the method of the same name on
2135 * the list's selection model.
2136 *
2137 * @return the value of the selection model's {@code isAdjusting} property.
2138 *
2139 * @see #setValueIsAdjusting
2140 * @see ListSelectionModel#getValueIsAdjusting
2141 */
2142 public boolean getValueIsAdjusting() {
2143 return getSelectionModel().getValueIsAdjusting();
2144 }
2145
2146
2147 /**
2148 * Returns an array of all of the selected indices, in increasing
2149 * order.
2150 *
2151 * @return all of the selected indices, in increasing order,
2152 * or an empty array if nothing is selected
2153 * @see #removeSelectionInterval
2154 * @see #addListSelectionListener
2155 */
2156 public int[] getSelectedIndices() {
2157 ListSelectionModel sm = getSelectionModel();
2158 int iMin = sm.getMinSelectionIndex();
2159 int iMax = sm.getMaxSelectionIndex();
2160
2161 if ((iMin < 0) || (iMax < 0)) {
2162 return new int[0];
2163 }
2164
2165 int[] rvTmp = new int[1+ (iMax - iMin)];
2166 int n = 0;
2167 for(int i = iMin; i <= iMax; i++) {
2168 if (sm.isSelectedIndex(i)) {
2169 rvTmp[n++] = i;
2170 }
2171 }
2172 int[] rv = new int[n];
2173 System.arraycopy(rvTmp, 0, rv, 0, n);
2174 return rv;
2175 }
2176
2177
2178 /**
2179 * Selects a single cell. Does nothing if the given index is greater
2180 * than or equal to the model size. This is a convenience method that uses
2181 * {@code setSelectionInterval} on the selection model. Refer to the
2182 * documentation for the selection model class being used for details on
2183 * how values less than {@code 0} are handled.
2184 *
2185 * @param index the index of the cell to select
2186 * @see ListSelectionModel#setSelectionInterval
2187 * @see #isSelectedIndex
2188 * @see #addListSelectionListener
2189 * @beaninfo
2190 * description: The index of the selected cell.
2191 */
2192 public void setSelectedIndex(int index) {
2193 if (index >= getModel().getSize()) {
2194 return;
2195 }
2196 getSelectionModel().setSelectionInterval(index, index);
2197 }
2198
2199
2200 /**
2201 * Changes the selection to be the set of indices specified by the given
2202 * array. Indices greater than or equal to the model size are ignored.
2203 * This is a convenience method that clears the selection and then uses
2204 * {@code addSelectionInterval} on the selection model to add the indices.
2205 * Refer to the documentation of the selection model class being used for
2206 * details on how values less than {@code 0} are handled.
2207 *
2208 * @param indices an array of the indices of the cells to select,
2209 * {@code non-null}
2210 * @see ListSelectionModel#addSelectionInterval
2211 * @see #isSelectedIndex
2212 * @see #addListSelectionListener
2213 * @throws NullPointerException if the given array is {@code null}
2214 */
2215 public void setSelectedIndices(int[] indices) {
2216 ListSelectionModel sm = getSelectionModel();
2217 sm.clearSelection();
2218 int size = getModel().getSize();
2219 for(int i = 0; i < indices.length; i++) {
2220 if (indices[i] < size) {
2221 sm.addSelectionInterval(indices[i], indices[i]);
2222 }
2223 }
2224 }
2225
2226
2227 /**
2228 * Returns an array of all the selected values, in increasing order based
2229 * on their indices in the list.
2230 *
2231 * @return the selected values, or an empty array if nothing is selected
2232 * @see #isSelectedIndex
2233 * @see #getModel
2234 * @see #addListSelectionListener
2235 */
2236 public Object[] getSelectedValues() {
2237 ListSelectionModel sm = getSelectionModel();
2238 ListModel dm = getModel();
2239
2240 int iMin = sm.getMinSelectionIndex();
2241 int iMax = sm.getMaxSelectionIndex();
2242
2243 if ((iMin < 0) || (iMax < 0)) {
2244 return new Object[0];
2245 }
2246
2247 Object[] rvTmp = new Object[1+ (iMax - iMin)];
2248 int n = 0;
2249 for(int i = iMin; i <= iMax; i++) {
2250 if (sm.isSelectedIndex(i)) {
2251 rvTmp[n++] = dm.getElementAt(i);
2252 }
2253 }
2254 Object[] rv = new Object[n];
2255 System.arraycopy(rvTmp, 0, rv, 0, n);
2256 return rv;
2257 }
2258
2259
2260 /**
2261 * Returns the smallest selected cell index; <i>the selection</i> when only
2262 * a single item is selected in the list. When multiple items are selected,
2263 * it is simply the smallest selected index. Returns {@code -1} if there is
2264 * no selection.
2265 * <p>
2266 * This method is a cover that delegates to {@code getMinSelectionIndex}.
2267 *
2268 * @return the smallest selected cell index
2269 * @see #getMinSelectionIndex
2270 * @see #addListSelectionListener
2271 */
2272 public int getSelectedIndex() {
2273 return getMinSelectionIndex();
2274 }
2275
2276
2277 /**
2278 * Returns the value for the smallest selected cell index;
2279 * <i>the selected value</i> when only a single item is selected in the
2280 * list. When multiple items are selected, it is simply the value for the
2281 * smallest selected index. Returns {@code null} if there is no selection.
2282 * <p>
2283 * This is a convenience method that simply returns the model value for
2284 * {@code getMinSelectionIndex}.
2285 *
2286 * @return the first selected value
2287 * @see #getMinSelectionIndex
2288 * @see #getModel
2289 * @see #addListSelectionListener
2290 */
2291 public Object getSelectedValue() {
2292 int i = getMinSelectionIndex();
2293 return (i == -1) ? null : getModel().getElementAt(i);
2294 }
2295
2296
2297 /**
2298 * Selects the specified object from the list.
2299 *
2300 * @param anObject the object to select
2301 * @param shouldScroll {@code true} if the list should scroll to display
2302 * the selected object, if one exists; otherwise {@code false}
2303 */
2304 public void setSelectedValue(Object anObject,boolean shouldScroll) {
2305 if(anObject == null)
2306 setSelectedIndex(-1);
2307 else if(!anObject.equals(getSelectedValue())) {
2308 int i,c;
2309 ListModel dm = getModel();
2310 for(i=0,c=dm.getSize();i<c;i++)
2311 if(anObject.equals(dm.getElementAt(i))){
2312 setSelectedIndex(i);
2313 if(shouldScroll)
2314 ensureIndexIsVisible(i);
2315 repaint(); /** FIX-ME setSelectedIndex does not redraw all the time with the basic l&f**/
2316 return;
2317 }
2318 setSelectedIndex(-1);
2319 }
2320 repaint(); /** FIX-ME setSelectedIndex does not redraw all the time with the basic l&f**/
2321 }
2322
2323
2324
2325 /**
2326 * --- The Scrollable Implementation ---
2327 */
2328
2329 private void checkScrollableParameters(Rectangle visibleRect, int orientation) {
2330 if (visibleRect == null) {
2331 throw new IllegalArgumentException("visibleRect must be non-null");
2332 }
2333 switch (orientation) {
2334 case SwingConstants.VERTICAL:
2335 case SwingConstants.HORIZONTAL:
2336 break;
2337 default:
2338 throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
2339 }
2340 }
2341
2342
2343 /**
2344 * Computes the size of viewport needed to display {@code visibleRowCount}
2345 * rows. The value returned by this method depends on the layout
2346 * orientation:
2347 * <p>
2348 * <b>{@code VERTICAL}:</b>
2349 * <br>
2350 * This is trivial if both {@code fixedCellWidth} and {@code fixedCellHeight}
2351 * have been set (either explicitly or by specifying a prototype cell value).
2352 * The width is simply the {@code fixedCellWidth} plus the list's horizontal
2353 * insets. The height is the {@code fixedCellHeight} multiplied by the
2354 * {@code visibleRowCount}, plus the list's vertical insets.
2355 * <p>
2356 * If either {@code fixedCellWidth} or {@code fixedCellHeight} haven't been
2357 * specified, heuristics are used. If the model is empty, the width is
2358 * the {@code fixedCellWidth}, if greater than {@code 0}, or a hard-coded
2359 * value of {@code 256}. The height is the {@code fixedCellHeight} multiplied
2360 * by {@code visibleRowCount}, if {@code fixedCellHeight} is greater than
2361 * {@code 0}, otherwise it is a hard-coded value of {@code 16} multiplied by
2362 * {@code visibleRowCount}.
2363 * <p>
2364 * If the model isn't empty, the width is the preferred size's width,
2365 * typically the width of the widest list element. The height is the
2366 * {@code fixedCellHeight} multiplied by the {@code visibleRowCount},
2367 * plus the list's vertical insets.
2368 * <p>
2369 * <b>{@code VERTICAL_WRAP} or {@code HORIZONTAL_WRAP}:</b>
2370 * <br>
2371 * This method simply returns the value from {@code getPreferredSize}.
2372 * The list's {@code ListUI} is expected to override {@code getPreferredSize}
2373 * to return an appropriate value.
2374 *
2375 * @return a dimension containing the size of the viewport needed
2376 * to display {@code visibleRowCount} rows
2377 * @see #getPreferredScrollableViewportSize
2378 * @see #setPrototypeCellValue
2379 */
2380 public Dimension getPreferredScrollableViewportSize()
2381 {
2382 if (getLayoutOrientation() != VERTICAL) {
2383 return getPreferredSize();
2384 }
2385 Insets insets = getInsets();
2386 int dx = insets.left + insets.right;
2387 int dy = insets.top + insets.bottom;
2388
2389 int visibleRowCount = getVisibleRowCount();
2390 int fixedCellWidth = getFixedCellWidth();
2391 int fixedCellHeight = getFixedCellHeight();
2392
2393 if ((fixedCellWidth > 0) && (fixedCellHeight > 0)) {
2394 int width = fixedCellWidth + dx;
2395 int height = (visibleRowCount * fixedCellHeight) + dy;
2396 return new Dimension(width, height);
2397 }
2398 else if (getModel().getSize() > 0) {
2399 int width = getPreferredSize().width;
2400 int height;
2401 Rectangle r = getCellBounds(0, 0);
2402 if (r != null) {
2403 height = (visibleRowCount * r.height) + dy;
2404 }
2405 else {
2406 // Will only happen if UI null, shouldn't matter what we return
2407 height = 1;
2408 }
2409 return new Dimension(width, height);
2410 }
2411 else {
2412 fixedCellWidth = (fixedCellWidth > 0) ? fixedCellWidth : 256;
2413 fixedCellHeight = (fixedCellHeight > 0) ? fixedCellHeight : 16;
2414 return new Dimension(fixedCellWidth, fixedCellHeight * visibleRowCount);
2415 }
2416 }
2417
2418
2419 /**
2420 * Returns the distance to scroll to expose the next or previous
2421 * row (for vertical scrolling) or column (for horizontal scrolling).
2422 * <p>
2423 * For horizontal scrolling, if the layout orientation is {@code VERTICAL},
2424 * then the list's font size is returned (or {@code 1} if the font is
2425 * {@code null}).
2426 *
2427 * @param visibleRect the view area visible within the viewport
2428 * @param orientation {@code SwingConstants.HORIZONTAL} or
2429 * {@code SwingConstants.VERTICAL}
2430 * @param direction less or equal to zero to scroll up/back,
2431 * greater than zero for down/forward
2432 * @return the "unit" increment for scrolling in the specified direction;
2433 * always positive
2434 * @see #getScrollableBlockIncrement
2435 * @see Scrollable#getScrollableUnitIncrement
2436 * @throws IllegalArgumentException if {@code visibleRect} is {@code null}, or
2437 * {@code orientation} isn't one of {@code SwingConstants.VERTICAL} or
2438 * {@code SwingConstants.HORIZONTAL}
2439 */
2440 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
2441 {
2442 checkScrollableParameters(visibleRect, orientation);
2443
2444 if (orientation == SwingConstants.VERTICAL) {
2445 int row = locationToIndex(visibleRect.getLocation());
2446
2447 if (row == -1) {
2448 return 0;
2449 }
2450 else {
2451 /* Scroll Down */
2452 if (direction > 0) {
2453 Rectangle r = getCellBounds(row, row);
2454 return (r == null) ? 0 : r.height - (visibleRect.y - r.y);
2455 }
2456 /* Scroll Up */
2457 else {
2458 Rectangle r = getCellBounds(row, row);
2459
2460 /* The first row is completely visible and it's row 0.
2461 * We're done.
2462 */
2463 if ((r.y == visibleRect.y) && (row == 0)) {
2464 return 0;
2465 }
2466 /* The first row is completely visible, return the
2467 * height of the previous row or 0 if the first row
2468 * is the top row of the list.
2469 */
2470 else if (r.y == visibleRect.y) {
2471 Point loc = r.getLocation();
2472 loc.y--;
2473 int prevIndex = locationToIndex(loc);
2474 Rectangle prevR = getCellBounds(prevIndex, prevIndex);
2475
2476 if (prevR == null || prevR.y >= r.y) {
2477 return 0;
2478 }
2479 return prevR.height;
2480 }
2481 /* The first row is partially visible, return the
2482 * height of hidden part.
2483 */
2484 else {
2485 return visibleRect.y - r.y;
2486 }
2487 }
2488 }
2489 } else if (orientation == SwingConstants.HORIZONTAL &&
2490 getLayoutOrientation() != JList.VERTICAL) {
2491 boolean leftToRight = getComponentOrientation().isLeftToRight();
2492 int index;
2493 Point leadingPoint;
2494
2495 if (leftToRight) {
2496 leadingPoint = visibleRect.getLocation();
2497 }
2498 else {
2499 leadingPoint = new Point(visibleRect.x + visibleRect.width -1,
2500 visibleRect.y);
2501 }
2502 index = locationToIndex(leadingPoint);
2503
2504 if (index != -1) {
2505 Rectangle cellBounds = getCellBounds(index, index);
2506 if (cellBounds != null && cellBounds.contains(leadingPoint)) {
2507 int leadingVisibleEdge;
2508 int leadingCellEdge;
2509
2510 if (leftToRight) {
2511 leadingVisibleEdge = visibleRect.x;
2512 leadingCellEdge = cellBounds.x;
2513 }
2514 else {
2515 leadingVisibleEdge = visibleRect.x + visibleRect.width;
2516 leadingCellEdge = cellBounds.x + cellBounds.width;
2517 }
2518
2519 if (leadingCellEdge != leadingVisibleEdge) {
2520 if (direction < 0) {
2521 // Show remainder of leading cell
2522 return Math.abs(leadingVisibleEdge - leadingCellEdge);
2523
2524 }
2525 else if (leftToRight) {
2526 // Hide rest of leading cell
2527 return leadingCellEdge + cellBounds.width - leadingVisibleEdge;
2528 }
2529 else {
2530 // Hide rest of leading cell
2531 return leadingVisibleEdge - cellBounds.x;
2532 }
2533 }
2534 // ASSUME: All cells are the same width
2535 return cellBounds.width;
2536 }
2537 }
2538 }
2539 Font f = getFont();
2540 return (f != null) ? f.getSize() : 1;
2541 }
2542
2543
2544 /**
2545 * Returns the distance to scroll to expose the next or previous block.
2546 * <p>
2547 * For vertical scrolling, the following rules are used:
2548 * <ul>
2549 * <li>if scrolling down, returns the distance to scroll so that the last
2550 * visible element becomes the first completely visible element
2551 * <li>if scrolling up, returns the distance to scroll so that the first
2552 * visible element becomes the last completely visible element
2553 * <li>returns {@code visibleRect.height} if the list is empty
2554 * </ul>
2555 * <p>
2556 * For horizontal scrolling, when the layout orientation is either
2557 * {@code VERTICAL_WRAP} or {@code HORIZONTAL_WRAP}:
2558 * <ul>
2559 * <li>if scrolling right, returns the distance to scroll so that the
2560 * last visible element becomes
2561 * the first completely visible element
2562 * <li>if scrolling left, returns the distance to scroll so that the first
2563 * visible element becomes the last completely visible element
2564 * <li>returns {@code visibleRect.width} if the list is empty
2565 * </ul>
2566 * <p>
2567 * For horizontal scrolling and {@code VERTICAL} orientation,
2568 * returns {@code visibleRect.width}.
2569 * <p>
2570 * Note that the value of {@code visibleRect} must be the equal to
2571 * {@code this.getVisibleRect()}.
2572 *
2573 * @param visibleRect the view area visible within the viewport
2574 * @param orientation {@code SwingConstants.HORIZONTAL} or
2575 * {@code SwingConstants.VERTICAL}
2576 * @param direction less or equal to zero to scroll up/back,
2577 * greater than zero for down/forward
2578 * @return the "block" increment for scrolling in the specified direction;
2579 * always positive
2580 * @see #getScrollableUnitIncrement
2581 * @see Scrollable#getScrollableBlockIncrement
2582 * @throws IllegalArgumentException if {@code visibleRect} is {@code null}, or
2583 * {@code orientation} isn't one of {@code SwingConstants.VERTICAL} or
2584 * {@code SwingConstants.HORIZONTAL}
2585 */
2586 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
2587 checkScrollableParameters(visibleRect, orientation);
2588 if (orientation == SwingConstants.VERTICAL) {
2589 int inc = visibleRect.height;
2590 /* Scroll Down */
2591 if (direction > 0) {
2592 // last cell is the lowest left cell
2593 int last = locationToIndex(new Point(visibleRect.x, visibleRect.y+visibleRect.height-1));
2594 if (last != -1) {
2595 Rectangle lastRect = getCellBounds(last,last);
2596 if (lastRect != null) {
2597 inc = lastRect.y - visibleRect.y;
2598 if ( (inc == 0) && (last < getModel().getSize()-1) ) {
2599 inc = lastRect.height;
2600 }
2601 }
2602 }
2603 }
2604 /* Scroll Up */
2605 else {
2606 int newFirst = locationToIndex(new Point(visibleRect.x, visibleRect.y-visibleRect.height));
2607 int first = getFirstVisibleIndex();
2608 if (newFirst != -1) {
2609 if (first == -1) {
2610 first = locationToIndex(visibleRect.getLocation());
2611 }
2612 Rectangle newFirstRect = getCellBounds(newFirst,newFirst);
2613 Rectangle firstRect = getCellBounds(first,first);
2614 if ((newFirstRect != null) && (firstRect!=null)) {
2615 while ( (newFirstRect.y + visibleRect.height <
2616 firstRect.y + firstRect.height) &&
2617 (newFirstRect.y < firstRect.y) ) {
2618 newFirst++;
2619 newFirstRect = getCellBounds(newFirst,newFirst);
2620 }
2621 inc = visibleRect.y - newFirstRect.y;
2622 if ( (inc <= 0) && (newFirstRect.y > 0)) {
2623 newFirst--;
2624 newFirstRect = getCellBounds(newFirst,newFirst);
2625 if (newFirstRect != null) {
2626 inc = visibleRect.y - newFirstRect.y;
2627 }
2628 }
2629 }
2630 }
2631 }
2632 return inc;
2633 }
2634 else if (orientation == SwingConstants.HORIZONTAL &&
2635 getLayoutOrientation() != JList.VERTICAL) {
2636 boolean leftToRight = getComponentOrientation().isLeftToRight();
2637 int inc = visibleRect.width;
2638 /* Scroll Right (in ltr mode) or Scroll Left (in rtl mode) */
2639 if (direction > 0) {
2640 // position is upper right if ltr, or upper left otherwise
2641 int x = visibleRect.x + (leftToRight ? (visibleRect.width - 1) : 0);
2642 int last = locationToIndex(new Point(x, visibleRect.y));
2643
2644 if (last != -1) {
2645 Rectangle lastRect = getCellBounds(last,last);
2646 if (lastRect != null) {
2647 if (leftToRight) {
2648 inc = lastRect.x - visibleRect.x;
2649 } else {
2650 inc = visibleRect.x + visibleRect.width
2651 - (lastRect.x + lastRect.width);
2652 }
2653 if (inc < 0) {
2654 inc += lastRect.width;
2655 } else if ( (inc == 0) && (last < getModel().getSize()-1) ) {
2656 inc = lastRect.width;
2657 }
2658 }
2659 }
2660 }
2661 /* Scroll Left (in ltr mode) or Scroll Right (in rtl mode) */
2662 else {
2663 // position is upper left corner of the visibleRect shifted
2664 // left by the visibleRect.width if ltr, or upper right shifted
2665 // right by the visibleRect.width otherwise
2666 int x = visibleRect.x + (leftToRight
2667 ? -visibleRect.width
2668 : visibleRect.width - 1 + visibleRect.width);
2669 int first = locationToIndex(new Point(x, visibleRect.y));
2670
2671 if (first != -1) {
2672 Rectangle firstRect = getCellBounds(first,first);
2673 if (firstRect != null) {
2674 // the right of the first cell
2675 int firstRight = firstRect.x + firstRect.width;
2676
2677 if (leftToRight) {
2678 if ((firstRect.x < visibleRect.x - visibleRect.width)
2679 && (firstRight < visibleRect.x)) {
2680 inc = visibleRect.x - firstRight;
2681 } else {
2682 inc = visibleRect.x - firstRect.x;
2683 }
2684 } else {
2685 int visibleRight = visibleRect.x + visibleRect.width;
2686
2687 if ((firstRight > visibleRight + visibleRect.width)
2688 && (firstRect.x > visibleRight)) {
2689 inc = firstRect.x - visibleRight;
2690 } else {
2691 inc = firstRight - visibleRight;
2692 }
2693 }
2694 }
2695 }
2696 }
2697 return inc;
2698 }
2699 return visibleRect.width;
2700 }
2701
2702
2703 /**
2704 * Returns {@code true} if this {@code JList} is displayed in a
2705 * {@code JViewport} and the viewport is wider than the list's
2706 * preferred width, or if the layout orientation is {@code HORIZONTAL_WRAP}
2707 * and {@code visibleRowCount <= 0}; otherwise returns {@code false}.
2708 * <p>
2709 * If {@code false}, then don't track the viewport's width. This allows
2710 * horizontal scrolling if the {@code JViewport} is itself embedded in a
2711 * {@code JScrollPane}.
2712 *
2713 * @return whether or not an enclosing viewport should force the list's
2714 * width to match its own
2715 * @see Scrollable#getScrollableTracksViewportWidth
2716 */
2717 public boolean getScrollableTracksViewportWidth() {
2718 if (getLayoutOrientation() == HORIZONTAL_WRAP &&
2719 getVisibleRowCount() <= 0) {
2720 return true;
2721 }
2722 if (getParent() instanceof JViewport) {
2723 return (((JViewport)getParent()).getWidth() > getPreferredSize().width);
2724 }
2725 return false;
2726 }
2727
2728 /**
2729 * Returns {@code true} if this {@code JList} is displayed in a
2730 * {@code JViewport} and the viewport is taller than the list's
2731 * preferred height, or if the layout orientation is {@code VERTICAL_WRAP}
2732 * and {@code visibleRowCount <= 0}; otherwise returns {@code false}.
2733 * <p>
2734 * If {@code false}, then don't track the viewport's height. This allows
2735 * vertical scrolling if the {@code JViewport} is itself embedded in a
2736 * {@code JScrollPane}.
2737 *
2738 * @return whether or not an enclosing viewport should force the list's
2739 * height to match its own
2740 * @see Scrollable#getScrollableTracksViewportHeight
2741 */
2742 public boolean getScrollableTracksViewportHeight() {
2743 if (getLayoutOrientation() == VERTICAL_WRAP &&
2744 getVisibleRowCount() <= 0) {
2745 return true;
2746 }
2747 if (getParent() instanceof JViewport) {
2748 return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
2749 }
2750 return false;
2751 }
2752
2753
2754 /*
2755 * See {@code readObject} and {@code writeObject} in {@code JComponent}
2756 * for more information about serialization in Swing.
2757 */
2758 private void writeObject(ObjectOutputStream s) throws IOException {
2759 s.defaultWriteObject();
2760 if (getUIClassID().equals(uiClassID)) {
2761 byte count = JComponent.getWriteObjCounter(this);
2762 JComponent.setWriteObjCounter(this, --count);
2763 if (count == 0 && ui != null) {
2764 ui.installUI(this);
2765 }
2766 }
2767 }
2768
2769
2770 /**
2771 * Returns a {@code String} representation of this {@code JList}.
2772 * This method is intended to be used only for debugging purposes,
2773 * and the content and format of the returned {@code String} may vary
2774 * between implementations. The returned {@code String} may be empty,
2775 * but may not be {@code null}.
2776 *
2777 * @return a {@code String} representation of this {@code JList}.
2778 */
2779 protected String paramString() {
2780 String selectionForegroundString = (selectionForeground != null ?
2781 selectionForeground.toString() :
2782 "");
2783 String selectionBackgroundString = (selectionBackground != null ?
2784 selectionBackground.toString() :
2785 "");
2786
2787 return super.paramString() +
2788 ",fixedCellHeight=" + fixedCellHeight +
2789 ",fixedCellWidth=" + fixedCellWidth +
2790 ",horizontalScrollIncrement=" + horizontalScrollIncrement +
2791 ",selectionBackground=" + selectionBackgroundString +
2792 ",selectionForeground=" + selectionForegroundString +
2793 ",visibleRowCount=" + visibleRowCount +
2794 ",layoutOrientation=" + layoutOrientation;
2795 }
2796
2797
2798 /**
2799 * --- Accessibility Support ---
2800 */
2801
2802 /**
2803 * Gets the {@code AccessibleContext} associated with this {@code JList}.
2804 * For {@code JList}, the {@code AccessibleContext} takes the form of an
2805 * {@code AccessibleJList}.
2806 * <p>
2807 * A new {@code AccessibleJList} instance is created if necessary.
2808 *
2809 * @return an {@code AccessibleJList} that serves as the
2810 * {@code AccessibleContext} of this {@code JList}
2811 */
2812 public AccessibleContext getAccessibleContext() {
2813 if (accessibleContext == null) {
2814 accessibleContext = new AccessibleJList();
2815 }
2816 return accessibleContext;
2817 }
2818
2819 /**
2820 * This class implements accessibility support for the
2821 * {@code JList} class. It provides an implementation of the
2822 * Java Accessibility API appropriate to list user-interface
2823 * elements.
2824 * <p>
2825 * <strong>Warning:</strong>
2826 * Serialized objects of this class will not be compatible with
2827 * future Swing releases. The current serialization support is
2828 * appropriate for short term storage or RMI between applications running
2829 * the same version of Swing. As of 1.4, support for long term storage
2830 * of all JavaBeans<sup><font size="-2">TM</font></sup>
2831 * has been added to the <code>java.beans</code> package.
2832 * Please see {@link java.beans.XMLEncoder}.
2833 */
2834 protected class AccessibleJList extends AccessibleJComponent
2835 implements AccessibleSelection, PropertyChangeListener,
2836 ListSelectionListener, ListDataListener {
2837
2838 int leadSelectionIndex;
2839
2840 public AccessibleJList() {
2841 super();
2842 JList.this.addPropertyChangeListener(this);
2843 JList.this.getSelectionModel().addListSelectionListener(this);
2844 JList.this.getModel().addListDataListener(this);
2845 leadSelectionIndex = JList.this.getLeadSelectionIndex();
2846 }
2847
2848 /**
2849 * Property Change Listener change method. Used to track changes
2850 * to the DataModel and ListSelectionModel, in order to re-set
2851 * listeners to those for reporting changes there via the Accessibility
2852 * PropertyChange mechanism.
2853 *
2854 * @param e PropertyChangeEvent
2855 */
2856 public void propertyChange(PropertyChangeEvent e) {
2857 String name = e.getPropertyName();
2858 Object oldValue = e.getOldValue();
2859 Object newValue = e.getNewValue();
2860
2861 // re-set listData listeners
2862 if (name.compareTo("model") == 0) {
2863
2864 if (oldValue != null && oldValue instanceof ListModel) {
2865 ((ListModel) oldValue).removeListDataListener(this);
2866 }
2867 if (newValue != null && newValue instanceof ListModel) {
2868 ((ListModel) newValue).addListDataListener(this);
2869 }
2870
2871 // re-set listSelectionModel listeners
2872 } else if (name.compareTo("selectionModel") == 0) {
2873
2874 if (oldValue != null && oldValue instanceof ListSelectionModel) {
2875 ((ListSelectionModel) oldValue).removeListSelectionListener(this);
2876 }
2877 if (newValue != null && newValue instanceof ListSelectionModel) {
2878 ((ListSelectionModel) newValue).addListSelectionListener(this);
2879 }
2880
2881 firePropertyChange(
2882 AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2883 Boolean.valueOf(false), Boolean.valueOf(true));
2884 }
2885 }
2886
2887 /**
2888 * List Selection Listener value change method. Used to fire
2889 * the property change
2890 *
2891 * @param e ListSelectionEvent
2892 *
2893 */
2894 public void valueChanged(ListSelectionEvent e) {
2895 int oldLeadSelectionIndex = leadSelectionIndex;
2896 leadSelectionIndex = JList.this.getLeadSelectionIndex();
2897 if (oldLeadSelectionIndex != leadSelectionIndex) {
2898 Accessible oldLS, newLS;
2899 oldLS = (oldLeadSelectionIndex >= 0)
2900 ? getAccessibleChild(oldLeadSelectionIndex)
2901 : null;
2902 newLS = (leadSelectionIndex >= 0)
2903 ? getAccessibleChild(leadSelectionIndex)
2904 : null;
2905 firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
2906 oldLS, newLS);
2907 }
2908
2909 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2910 Boolean.valueOf(false), Boolean.valueOf(true));
2911 firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2912 Boolean.valueOf(false), Boolean.valueOf(true));
2913
2914 // Process the State changes for Multiselectable
2915 AccessibleStateSet s = getAccessibleStateSet();
2916 ListSelectionModel lsm = JList.this.getSelectionModel();
2917 if (lsm.getSelectionMode() != ListSelectionModel.SINGLE_SELECTION) {
2918 if (!s.contains(AccessibleState.MULTISELECTABLE)) {
2919 s.add(AccessibleState.MULTISELECTABLE);
2920 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2921 null, AccessibleState.MULTISELECTABLE);
2922 }
2923 } else {
2924 if (s.contains(AccessibleState.MULTISELECTABLE)) {
2925 s.remove(AccessibleState.MULTISELECTABLE);
2926 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
2927 AccessibleState.MULTISELECTABLE, null);
2928 }
2929 }
2930 }
2931
2932 /**
2933 * List Data Listener interval added method. Used to fire the visible data property change
2934 *
2935 * @param e ListDataEvent
2936 *
2937 */
2938 public void intervalAdded(ListDataEvent e) {
2939 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2940 Boolean.valueOf(false), Boolean.valueOf(true));
2941 }
2942
2943 /**
2944 * List Data Listener interval removed method. Used to fire the visible data property change
2945 *
2946 * @param e ListDataEvent
2947 *
2948 */
2949 public void intervalRemoved(ListDataEvent e) {
2950 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2951 Boolean.valueOf(false), Boolean.valueOf(true));
2952 }
2953
2954 /**
2955 * List Data Listener contents changed method. Used to fire the visible data property change
2956 *
2957 * @param e ListDataEvent
2958 *
2959 */
2960 public void contentsChanged(ListDataEvent e) {
2961 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2962 Boolean.valueOf(false), Boolean.valueOf(true));
2963 }
2964
2965 // AccessibleContext methods
2966
2967 /**
2968 * Get the state set of this object.
2969 *
2970 * @return an instance of AccessibleState containing the current state
2971 * of the object
2972 * @see AccessibleState
2973 */
2974 public AccessibleStateSet getAccessibleStateSet() {
2975 AccessibleStateSet states = super.getAccessibleStateSet();
2976 if (selectionModel.getSelectionMode() !=
2977 ListSelectionModel.SINGLE_SELECTION) {
2978 states.add(AccessibleState.MULTISELECTABLE);
2979 }
2980 return states;
2981 }
2982
2983 /**
2984 * Get the role of this object.
2985 *
2986 * @return an instance of AccessibleRole describing the role of the
2987 * object
2988 * @see AccessibleRole
2989 */
2990 public AccessibleRole getAccessibleRole() {
2991 return AccessibleRole.LIST;
2992 }
2993
2994 /**
2995 * Returns the <code>Accessible</code> child contained at
2996 * the local coordinate <code>Point</code>, if one exists.
2997 * Otherwise returns <code>null</code>.
2998 *
2999 * @return the <code>Accessible</code> at the specified
3000 * location, if it exists
3001 */
3002 public Accessible getAccessibleAt(Point p) {
3003 int i = locationToIndex(p);
3004 if (i >= 0) {
3005 return new AccessibleJListChild(JList.this, i);
3006 } else {
3007 return null;
3008 }
3009 }
3010
3011 /**
3012 * Returns the number of accessible children in the object. If all
3013 * of the children of this object implement Accessible, than this
3014 * method should return the number of children of this object.
3015 *
3016 * @return the number of accessible children in the object.
3017 */
3018 public int getAccessibleChildrenCount() {
3019 return getModel().getSize();
3020 }
3021
3022 /**
3023 * Return the nth Accessible child of the object.
3024 *
3025 * @param i zero-based index of child
3026 * @return the nth Accessible child of the object
3027 */
3028 public Accessible getAccessibleChild(int i) {
3029 if (i >= getModel().getSize()) {
3030 return null;
3031 } else {
3032 return new AccessibleJListChild(JList.this, i);
3033 }
3034 }
3035
3036 /**
3037 * Get the AccessibleSelection associated with this object. In the
3038 * implementation of the Java Accessibility API for this class,
3039 * return this object, which is responsible for implementing the
3040 * AccessibleSelection interface on behalf of itself.
3041 *
3042 * @return this object
3043 */
3044 public AccessibleSelection getAccessibleSelection() {
3045 return this;
3046 }
3047
3048
3049 // AccessibleSelection methods
3050
3051 /**
3052 * Returns the number of items currently selected.
3053 * If no items are selected, the return value will be 0.
3054 *
3055 * @return the number of items currently selected.
3056 */
3057 public int getAccessibleSelectionCount() {
3058 return JList.this.getSelectedIndices().length;
3059 }
3060
3061 /**
3062 * Returns an Accessible representing the specified selected item
3063 * in the object. If there isn't a selection, or there are
3064 * fewer items selected than the integer passed in, the return
3065 * value will be <code>null</code>.
3066 *
3067 * @param i the zero-based index of selected items
3068 * @return an Accessible containing the selected item
3069 */
3070 public Accessible getAccessibleSelection(int i) {
3071 int len = getAccessibleSelectionCount();
3072 if (i < 0 || i >= len) {
3073 return null;
3074 } else {
3075 return getAccessibleChild(JList.this.getSelectedIndices()[i]);
3076 }
3077 }
3078
3079 /**
3080 * Returns true if the current child of this object is selected.
3081 *
3082 * @param i the zero-based index of the child in this Accessible
3083 * object.
3084 * @see AccessibleContext#getAccessibleChild
3085 */
3086 public boolean isAccessibleChildSelected(int i) {
3087 return isSelectedIndex(i);
3088 }
3089
3090 /**
3091 * Adds the specified selected item in the object to the object's
3092 * selection. If the object supports multiple selections,
3093 * the specified item is added to any existing selection, otherwise
3094 * it replaces any existing selection in the object. If the
3095 * specified item is already selected, this method has no effect.
3096 *
3097 * @param i the zero-based index of selectable items
3098 */
3099 public void addAccessibleSelection(int i) {
3100 JList.this.addSelectionInterval(i, i);
3101 }
3102
3103 /**
3104 * Removes the specified selected item in the object from the object's
3105 * selection. If the specified item isn't currently selected, this
3106 * method has no effect.
3107 *
3108 * @param i the zero-based index of selectable items
3109 */
3110 public void removeAccessibleSelection(int i) {
3111 JList.this.removeSelectionInterval(i, i);
3112 }
3113
3114 /**
3115 * Clears the selection in the object, so that nothing in the
3116 * object is selected.
3117 */
3118 public void clearAccessibleSelection() {
3119 JList.this.clearSelection();
3120 }
3121
3122 /**
3123 * Causes every selected item in the object to be selected
3124 * if the object supports multiple selections.
3125 */
3126 public void selectAllAccessibleSelection() {
3127 JList.this.addSelectionInterval(0, getAccessibleChildrenCount() -1);
3128 }
3129
3130 /**
3131 * This class implements accessibility support appropriate
3132 * for list children.
3133 */
3134 protected class AccessibleJListChild extends AccessibleContext
3135 implements Accessible, AccessibleComponent {
3136 private JList parent = null;
3137 private int indexInParent;
3138 private Component component = null;
3139 private AccessibleContext accessibleContext = null;
3140 private ListModel listModel;
3141 private ListCellRenderer cellRenderer = null;
3142
3143 public AccessibleJListChild(JList parent, int indexInParent) {
3144 this.parent = parent;
3145 this.setAccessibleParent(parent);
3146 this.indexInParent = indexInParent;
3147 if (parent != null) {
3148 listModel = parent.getModel();
3149 cellRenderer = parent.getCellRenderer();
3150 }
3151 }
3152
3153 private Component getCurrentComponent() {
3154 return getComponentAtIndex(indexInParent);
3155 }
3156
3157 private AccessibleContext getCurrentAccessibleContext() {
3158 Component c = getComponentAtIndex(indexInParent);
3159 if (c instanceof Accessible) {
3160 return ((Accessible) c).getAccessibleContext();
3161 } else {
3162 return null;
3163 }
3164 }
3165
3166 private Component getComponentAtIndex(int index) {
3167 if (index < 0 || index >= listModel.getSize()) {
3168 return null;
3169 }
3170 if ((parent != null)
3171 && (listModel != null)
3172 && cellRenderer != null) {
3173 Object value = listModel.getElementAt(index);
3174 boolean isSelected = parent.isSelectedIndex(index);
3175 boolean isFocussed = parent.isFocusOwner()
3176 && (index == parent.getLeadSelectionIndex());
3177 return cellRenderer.getListCellRendererComponent(
3178 parent,
3179 value,
3180 index,
3181 isSelected,
3182 isFocussed);
3183 } else {
3184 return null;
3185 }
3186 }
3187
3188
3189 // Accessible Methods
3190 /**
3191 * Get the AccessibleContext for this object. In the
3192 * implementation of the Java Accessibility API for this class,
3193 * returns this object, which is its own AccessibleContext.
3194 *
3195 * @return this object
3196 */
3197 public AccessibleContext getAccessibleContext() {
3198 return this;
3199 }
3200
3201
3202 // AccessibleContext methods
3203
3204 public String getAccessibleName() {
3205 AccessibleContext ac = getCurrentAccessibleContext();
3206 if (ac != null) {
3207 return ac.getAccessibleName();
3208 } else {
3209 return null;
3210 }
3211 }
3212
3213 public void setAccessibleName(String s) {
3214 AccessibleContext ac = getCurrentAccessibleContext();
3215 if (ac != null) {
3216 ac.setAccessibleName(s);
3217 }
3218 }
3219
3220 public String getAccessibleDescription() {
3221 AccessibleContext ac = getCurrentAccessibleContext();
3222 if (ac != null) {
3223 return ac.getAccessibleDescription();
3224 } else {
3225 return null;
3226 }
3227 }
3228
3229 public void setAccessibleDescription(String s) {
3230 AccessibleContext ac = getCurrentAccessibleContext();
3231 if (ac != null) {
3232 ac.setAccessibleDescription(s);
3233 }
3234 }
3235
3236 public AccessibleRole getAccessibleRole() {
3237 AccessibleContext ac = getCurrentAccessibleContext();
3238 if (ac != null) {
3239 return ac.getAccessibleRole();
3240 } else {
3241 return null;
3242 }
3243 }
3244
3245 public AccessibleStateSet getAccessibleStateSet() {
3246 AccessibleContext ac = getCurrentAccessibleContext();
3247 AccessibleStateSet s;
3248 if (ac != null) {
3249 s = ac.getAccessibleStateSet();
3250 } else {
3251 s = new AccessibleStateSet();
3252 }
3253
3254 s.add(AccessibleState.SELECTABLE);
3255 if (parent.isFocusOwner()
3256 && (indexInParent == parent.getLeadSelectionIndex())) {
3257 s.add(AccessibleState.ACTIVE);
3258 }
3259 if (parent.isSelectedIndex(indexInParent)) {
3260 s.add(AccessibleState.SELECTED);
3261 }
3262 if (this.isShowing()) {
3263 s.add(AccessibleState.SHOWING);
3264 } else if (s.contains(AccessibleState.SHOWING)) {
3265 s.remove(AccessibleState.SHOWING);
3266 }
3267 if (this.isVisible()) {
3268 s.add(AccessibleState.VISIBLE);
3269 } else if (s.contains(AccessibleState.VISIBLE)) {
3270 s.remove(AccessibleState.VISIBLE);
3271 }
3272 s.add(AccessibleState.TRANSIENT); // cell-rendered
3273 return s;
3274 }
3275
3276 public int getAccessibleIndexInParent() {
3277 return indexInParent;
3278 }
3279
3280 public int getAccessibleChildrenCount() {
3281 AccessibleContext ac = getCurrentAccessibleContext();
3282 if (ac != null) {
3283 return ac.getAccessibleChildrenCount();
3284 } else {
3285 return 0;
3286 }
3287 }
3288
3289 public Accessible getAccessibleChild(int i) {
3290 AccessibleContext ac = getCurrentAccessibleContext();
3291 if (ac != null) {
3292 Accessible accessibleChild = ac.getAccessibleChild(i);
3293 ac.setAccessibleParent(this);
3294 return accessibleChild;
3295 } else {
3296 return null;
3297 }
3298 }
3299
3300 public Locale getLocale() {
3301 AccessibleContext ac = getCurrentAccessibleContext();
3302 if (ac != null) {
3303 return ac.getLocale();
3304 } else {
3305 return null;
3306 }
3307 }
3308
3309 public void addPropertyChangeListener(PropertyChangeListener l) {
3310 AccessibleContext ac = getCurrentAccessibleContext();
3311 if (ac != null) {
3312 ac.addPropertyChangeListener(l);
3313 }
3314 }
3315
3316 public void removePropertyChangeListener(PropertyChangeListener l) {
3317 AccessibleContext ac = getCurrentAccessibleContext();
3318 if (ac != null) {
3319 ac.removePropertyChangeListener(l);
3320 }
3321 }
3322
3323 public AccessibleAction getAccessibleAction() {
3324 return getCurrentAccessibleContext().getAccessibleAction();
3325 }
3326
3327 /**
3328 * Get the AccessibleComponent associated with this object. In the
3329 * implementation of the Java Accessibility API for this class,
3330 * return this object, which is responsible for implementing the
3331 * AccessibleComponent interface on behalf of itself.
3332 *
3333 * @return this object
3334 */
3335 public AccessibleComponent getAccessibleComponent() {
3336 return this; // to override getBounds()
3337 }
3338
3339 public AccessibleSelection getAccessibleSelection() {
3340 return getCurrentAccessibleContext().getAccessibleSelection();
3341 }
3342
3343 public AccessibleText getAccessibleText() {
3344 return getCurrentAccessibleContext().getAccessibleText();
3345 }
3346
3347 public AccessibleValue getAccessibleValue() {
3348 return getCurrentAccessibleContext().getAccessibleValue();
3349 }
3350
3351
3352 // AccessibleComponent methods
3353
3354 public Color getBackground() {
3355 AccessibleContext ac = getCurrentAccessibleContext();
3356 if (ac instanceof AccessibleComponent) {
3357 return ((AccessibleComponent) ac).getBackground();
3358 } else {
3359 Component c = getCurrentComponent();
3360 if (c != null) {
3361 return c.getBackground();
3362 } else {
3363 return null;
3364 }
3365 }
3366 }
3367
3368 public void setBackground(Color c) {
3369 AccessibleContext ac = getCurrentAccessibleContext();
3370 if (ac instanceof AccessibleComponent) {
3371 ((AccessibleComponent) ac).setBackground(c);
3372 } else {
3373 Component cp = getCurrentComponent();
3374 if (cp != null) {
3375 cp.setBackground(c);
3376 }
3377 }
3378 }
3379
3380 public Color getForeground() {
3381 AccessibleContext ac = getCurrentAccessibleContext();
3382 if (ac instanceof AccessibleComponent) {
3383 return ((AccessibleComponent) ac).getForeground();
3384 } else {
3385 Component c = getCurrentComponent();
3386 if (c != null) {
3387 return c.getForeground();
3388 } else {
3389 return null;
3390 }
3391 }
3392 }
3393
3394 public void setForeground(Color c) {
3395 AccessibleContext ac = getCurrentAccessibleContext();
3396 if (ac instanceof AccessibleComponent) {
3397 ((AccessibleComponent) ac).setForeground(c);
3398 } else {
3399 Component cp = getCurrentComponent();
3400 if (cp != null) {
3401 cp.setForeground(c);
3402 }
3403 }
3404 }
3405
3406 public Cursor getCursor() {
3407 AccessibleContext ac = getCurrentAccessibleContext();
3408 if (ac instanceof AccessibleComponent) {
3409 return ((AccessibleComponent) ac).getCursor();
3410 } else {
3411 Component c = getCurrentComponent();
3412 if (c != null) {
3413 return c.getCursor();
3414 } else {
3415 Accessible ap = getAccessibleParent();
3416 if (ap instanceof AccessibleComponent) {
3417 return ((AccessibleComponent) ap).getCursor();
3418 } else {
3419 return null;
3420 }
3421 }
3422 }
3423 }
3424
3425 public void setCursor(Cursor c) {
3426 AccessibleContext ac = getCurrentAccessibleContext();
3427 if (ac instanceof AccessibleComponent) {
3428 ((AccessibleComponent) ac).setCursor(c);
3429 } else {
3430 Component cp = getCurrentComponent();
3431 if (cp != null) {
3432 cp.setCursor(c);
3433 }
3434 }
3435 }
3436
3437 public Font getFont() {
3438 AccessibleContext ac = getCurrentAccessibleContext();
3439 if (ac instanceof AccessibleComponent) {
3440 return ((AccessibleComponent) ac).getFont();
3441 } else {
3442 Component c = getCurrentComponent();
3443 if (c != null) {
3444 return c.getFont();
3445 } else {
3446 return null;
3447 }
3448 }
3449 }
3450
3451 public void setFont(Font f) {
3452 AccessibleContext ac = getCurrentAccessibleContext();
3453 if (ac instanceof AccessibleComponent) {
3454 ((AccessibleComponent) ac).setFont(f);
3455 } else {
3456 Component c = getCurrentComponent();
3457 if (c != null) {
3458 c.setFont(f);
3459 }
3460 }
3461 }
3462
3463 public FontMetrics getFontMetrics(Font f) {
3464 AccessibleContext ac = getCurrentAccessibleContext();
3465 if (ac instanceof AccessibleComponent) {
3466 return ((AccessibleComponent) ac).getFontMetrics(f);
3467 } else {
3468 Component c = getCurrentComponent();
3469 if (c != null) {
3470 return c.getFontMetrics(f);
3471 } else {
3472 return null;
3473 }
3474 }
3475 }
3476
3477 public boolean isEnabled() {
3478 AccessibleContext ac = getCurrentAccessibleContext();
3479 if (ac instanceof AccessibleComponent) {
3480 return ((AccessibleComponent) ac).isEnabled();
3481 } else {
3482 Component c = getCurrentComponent();
3483 if (c != null) {
3484 return c.isEnabled();
3485 } else {
3486 return false;
3487 }
3488 }
3489 }
3490
3491 public void setEnabled(boolean b) {
3492 AccessibleContext ac = getCurrentAccessibleContext();
3493 if (ac instanceof AccessibleComponent) {
3494 ((AccessibleComponent) ac).setEnabled(b);
3495 } else {
3496 Component c = getCurrentComponent();
3497 if (c != null) {
3498 c.setEnabled(b);
3499 }
3500 }
3501 }
3502
3503 public boolean isVisible() {
3504 int fi = parent.getFirstVisibleIndex();
3505 int li = parent.getLastVisibleIndex();
3506 // The UI incorrectly returns a -1 for the last
3507 // visible index if the list is smaller than the
3508 // viewport size.
3509 if (li == -1) {
3510 li = parent.getModel().getSize() - 1;
3511 }
3512 return ((indexInParent >= fi)
3513 && (indexInParent <= li));
3514 }
3515
3516 public void setVisible(boolean b) {
3517 }
3518
3519 public boolean isShowing() {
3520 return (parent.isShowing() && isVisible());
3521 }
3522
3523 public boolean contains(Point p) {
3524 AccessibleContext ac = getCurrentAccessibleContext();
3525 if (ac instanceof AccessibleComponent) {
3526 Rectangle r = ((AccessibleComponent) ac).getBounds();
3527 return r.contains(p);
3528 } else {
3529 Component c = getCurrentComponent();
3530 if (c != null) {
3531 Rectangle r = c.getBounds();
3532 return r.contains(p);
3533 } else {
3534 return getBounds().contains(p);
3535 }
3536 }
3537 }
3538
3539 public Point getLocationOnScreen() {
3540 if (parent != null) {
3541 Point listLocation = parent.getLocationOnScreen();
3542 Point componentLocation = parent.indexToLocation(indexInParent);
3543 if (componentLocation != null) {
3544 componentLocation.translate(listLocation.x, listLocation.y);
3545 return componentLocation;
3546 } else {
3547 return null;
3548 }
3549 } else {
3550 return null;
3551 }
3552 }
3553
3554 public Point getLocation() {
3555 if (parent != null) {
3556 return parent.indexToLocation(indexInParent);
3557 } else {
3558 return null;
3559 }
3560 }
3561
3562 public void setLocation(Point p) {
3563 if ((parent != null) && (parent.contains(p))) {
3564 ensureIndexIsVisible(indexInParent);
3565 }
3566 }
3567
3568 public Rectangle getBounds() {
3569 if (parent != null) {
3570 return parent.getCellBounds(indexInParent,indexInParent);
3571 } else {
3572 return null;
3573 }
3574 }
3575
3576 public void setBounds(Rectangle r) {
3577 AccessibleContext ac = getCurrentAccessibleContext();
3578 if (ac instanceof AccessibleComponent) {
3579 ((AccessibleComponent) ac).setBounds(r);
3580 }
3581 }
3582
3583 public Dimension getSize() {
3584 Rectangle cellBounds = this.getBounds();
3585 if (cellBounds != null) {
3586 return cellBounds.getSize();
3587 } else {
3588 return null;
3589 }
3590 }
3591
3592 public void setSize (Dimension d) {
3593 AccessibleContext ac = getCurrentAccessibleContext();
3594 if (ac instanceof AccessibleComponent) {
3595 ((AccessibleComponent) ac).setSize(d);
3596 } else {
3597 Component c = getCurrentComponent();
3598 if (c != null) {
3599 c.setSize(d);
3600 }
3601 }
3602 }
3603
3604 public Accessible getAccessibleAt(Point p) {
3605 AccessibleContext ac = getCurrentAccessibleContext();
3606 if (ac instanceof AccessibleComponent) {
3607 return ((AccessibleComponent) ac).getAccessibleAt(p);
3608 } else {
3609 return null;
3610 }
3611 }
3612
3613 public boolean isFocusTraversable() {
3614 AccessibleContext ac = getCurrentAccessibleContext();
3615 if (ac instanceof AccessibleComponent) {
3616 return ((AccessibleComponent) ac).isFocusTraversable();
3617 } else {
3618 Component c = getCurrentComponent();
3619 if (c != null) {
3620 return c.isFocusTraversable();
3621 } else {
3622 return false;
3623 }
3624 }
3625 }
3626
3627 public void requestFocus() {
3628 AccessibleContext ac = getCurrentAccessibleContext();
3629 if (ac instanceof AccessibleComponent) {
3630 ((AccessibleComponent) ac).requestFocus();
3631 } else {
3632 Component c = getCurrentComponent();
3633 if (c != null) {
3634 c.requestFocus();
3635 }
3636 }
3637 }
3638
3639 public void addFocusListener(FocusListener l) {
3640 AccessibleContext ac = getCurrentAccessibleContext();
3641 if (ac instanceof AccessibleComponent) {
3642 ((AccessibleComponent) ac).addFocusListener(l);
3643 } else {
3644 Component c = getCurrentComponent();
3645 if (c != null) {
3646 c.addFocusListener(l);
3647 }
3648 }
3649 }
3650
3651 public void removeFocusListener(FocusListener l) {
3652 AccessibleContext ac = getCurrentAccessibleContext();
3653 if (ac instanceof AccessibleComponent) {
3654 ((AccessibleComponent) ac).removeFocusListener(l);
3655 } else {
3656 Component c = getCurrentComponent();
3657 if (c != null) {
3658 c.removeFocusListener(l);
3659 }
3660 }
3661 }
3662
3663 // TIGER - 4733624
3664 /**
3665 * Returns the icon for the element renderer, as the only item
3666 * of an array of <code>AccessibleIcon</code>s or a <code>null</code> array
3667 * if the renderer component contains no icons.
3668 *
3669 * @return an array containing the accessible icon
3670 * or a <code>null</code> array if none
3671 * @since 1.3
3672 */
3673 public AccessibleIcon [] getAccessibleIcon() {
3674 AccessibleContext ac = getCurrentAccessibleContext();
3675 if (ac != null) {
3676 return ac.getAccessibleIcon();
3677 } else {
3678 return null;
3679 }
3680 }
3681 } // inner class AccessibleJListChild
3682 } // inner class AccessibleJList
3683}